Midi keyboard/Oktaven knöpfe

Hi Leute!

Ich brauch mal eure programier Hilfe!

Ich bin gerade dabei mir ein Midikeyboard aus den Tasten einer alten E-Orgel zu bauen.
(44 Tasten, 4x12 Matrix)
Im internet bin ich auf verschiedene Codes gestoßen, aus denen ich mir einen zusammengeschustert habe und so weit klappt auch alles ganz gut.
Bis jetzt kann mein Keyboard alle Töne von F2 bis C6 als Midi Signal ausgeben.

Mein Wunsch/Plan ist es jetzt zwei Knöpfe zu installieren, die die Oktaven verändern.
Die Knöpfe sollen also das komplette Keyboard entweder beliebig viele Oktaven höher( je nach dem wie oft man drückt) oder niedriger stimmen.

Da ich in Sachen Programmieren ein totaler Neuling bin, bitte ich euch nun um Lösungsvorschläge oder Tipps :slight_smile:

Ich habe viel rumprobiert mit schaltern, was auch einigermaßen funktioniert hat, allerdings fände ich die Lösung mit Knöpfen den zwei Knöpfen doch um einiges cooler.

hier der Code mit dem ich arbeite:

#define NUM_ROWS 4
#define NUM_COLS 12

#define NOTE_ON_CMD 0x90
#define NOTE_OFF_CMD 0x80
#define NOTE_VELOCITY 127

//MIDI baud rate
#define SERIAL_RATE 31250

// Pin Definitions

// Row input pins
const int row1Pin = 2;
const int row2Pin = 3;
const int row3Pin = 4;
const int row4Pin = 5;


// 74HC595 pins
const int dataPin = 6;
const int latchPin = 7;
const int clockPin = 8;

boolean keyPressed[NUM_ROWS][NUM_COLS];

uint8_t keyToMidiMap[4][12] =   
{ 
 36,37,38,39,
 40,41,42,43,
 44,45,46,47,
 48,49,50,51,
 52,53,54,55,
 56,57,58,59,
 60,61,62,63,
 64,65,66,67,
 68,69,70,71,
 72,73,74,75,
 76,77,78,79,
 80,81,82,83,  
 };
 
// bitmasks for scanning columns
int bits[] =
{ 
  B11111110,
  B11111101,
  B11111011,
  B11110111,
  B11101111,
  B11011111,
  B10111111,
  B01111111
};

void setup()
{
  // setup pins output/input mode
  pinMode(dataPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(latchPin, OUTPUT);

  pinMode(row1Pin, INPUT);
  pinMode(row2Pin, INPUT);
  pinMode(row3Pin, INPUT);
  pinMode(row4Pin, INPUT);

  Serial.begin(SERIAL_RATE);
}

void loop()
{
  for (int colCtr = 0; colCtr < NUM_COLS; ++colCtr)
  {
    //scan next column
    scanColumn(colCtr);

    //get row values at this column
    int rowValue[NUM_ROWS];
    rowValue[0] = !digitalRead(row1Pin);
    rowValue[1] = !digitalRead(row2Pin);
    rowValue[2] = !digitalRead(row3Pin);
    rowValue[3] = !digitalRead(row4Pin);

    // process keys pressed
    for(int rowCtr=0; rowCtr<NUM_ROWS; ++rowCtr)
    {
      if(rowValue[rowCtr] != 0 && !keyPressed[rowCtr][colCtr])
      {
        keyPressed[rowCtr][colCtr] = true;
        noteOn(rowCtr,colCtr);
      }
    }

    // process keys released
    for(int rowCtr=0; rowCtr<NUM_ROWS; ++rowCtr)
    {
      if(rowValue[rowCtr] == 0 && keyPressed[rowCtr][colCtr])
      {
        keyPressed[rowCtr][colCtr] = false;
        noteOff(rowCtr,colCtr);
      }
    }
  }
}

void scanColumn(int colNum)
{
  digitalWrite(latchPin, LOW);

  if(0 <= colNum && colNum <= 7)
  {
    shiftOut(dataPin, clockPin, MSBFIRST, B11111111); //right sr
    shiftOut(dataPin, clockPin, MSBFIRST, bits[colNum]); //left sr
  }
  else
  {
    shiftOut(dataPin, clockPin, MSBFIRST, bits[colNum-8]); //right sr
    shiftOut(dataPin, clockPin, MSBFIRST, B11111111); //left sr
  }
  digitalWrite(latchPin, HIGH);
}
void noteOn(int row, int col)
{
  Serial.write(NOTE_ON_CMD);
  Serial.write(keyToMidiMap[row][col]);
  Serial.write(NOTE_VELOCITY);
}

void noteOff(int row, int col)
{
  Serial.write(NOTE_OFF_CMD);
  Serial.write(keyToMidiMap[row][col]);
  Serial.write(NOTE_VELOCITY);
}

Hoffe ihr könnt mir helfen.

David :slight_smile:

Uhh, ist mir zu lange zum Lesen :slight_smile:

Letztlich willst Du doch aber einfach nur 2 Taster abfragen und abhängig vom Drücken den Midi-Daten einen Offset mitgeben. Einfache Addition ist das.

Vielleicht solltest Du auch überlegen, nicht nur Oktavsprünge einzubauen sondern auch Halbtonschritte, das ist prima zum Transponieren.

DavidHaus:
Hoffe ihr könnt mir helfen.

Hallo David,
wenn ich es richtig verstehe, willst Du mit einem Plus- und einem Minus-Knopf einen Wert rauf oder runter zählen. Dazu würde ich das Beispiel change.ino der Bibliothek Bounce2 zur Grundlage nehmen und auf zwei Knöpfe erweitern. Den zu ändernden Wert habe ich oktave genannt:

#include <Bounce2.h>
Bounce debouncerMinus = Bounce();
Bounce debouncerPlus = Bounce();
const byte tasterMinusPin = 2, tasterPlusPin = 3;
byte oktave = 127;

void setup() {
  Serial.begin(9600);
  Serial.println("Anfang");
  pinMode(tasterMinusPin, INPUT_PULLUP);
  pinMode(tasterPlusPin, INPUT_PULLUP);
  debouncerMinus.attach(tasterMinusPin);
  debouncerMinus.interval(100);
  debouncerPlus.attach(tasterPlusPin);
  debouncerPlus.interval(100);
  Serial.print("Oktave: ");
  Serial.println(oktave);
}

void loop() {
  debouncerMinus.update();
  debouncerPlus.update();
  if ( debouncerMinus.fell() ) {
    oktave = oktave - 1;
    Serial.print("Oktave: ");
    Serial.println(oktave);
  }
  if ( debouncerPlus.fell() ) {
    oktave = oktave + 1;
    Serial.print("Oktave: ");
    Serial.println(oktave);
  }
}

Wenn Du diese Zeilen anpaßt und in Deinen Sketch integrierst, kann die verschobene Musik erklingen :slight_smile:

Oh, noch jemand....
Bin schon bissel weiter, allerdings hab ich ne "Live-Oktavverschiebung" noch nicht implementiert.

Generell ist es aber einfach (in einem der Betriebsmodi nutze ich die bereits, allerdings (noch) fest eingestellt:

Du addierst einfach zu den betreffenden Tasten 12 pro Oktave- oder ziehst sie eben ab.
WICHTIG: du musst die Tastendrücke abfangen.
Es darf nur dann umgeschalten werden, wenn keine Taste gedrückt war (alle müssen ihr NoteOff abgesendet haben), sonst hast du Notenhänger.

So mache ich das im Orgel-Preset.
Hier wird die richtige Tastennummer erzeugt:

*if((midiKanal==kanalLinks)&&(midiKanalModus==0)) *

  • {*
  • casioTaste=casioTaste+24; //linke Seite um 2 Oktaven nach oben schieben*
  • }*

(natürlich sowohl bei NoteOn als auch bei NoteOff) ;
und hier wird beobachtet, ob noch eine Taste gehalten wird:

*{ *

  • if(keypad.key[n].kstate==PRESSED) // wurde was gedrückt?*
  • {*
  • noteOn(midiKanal,casioTaste,anSchlag);*
  • tastenBeobachter ++; // wieviele Tasten sind gedrückt?*
  • }*
  • if(keypad.key[n].kstate==RELEASED) // wurde was los gelassen?*
  • {*
  • noteOff(midiKanal,casioTaste);*
  • tastenBeobachter --; // losgelassene wieder abrechnen *
  • }*

Umgeschalten wird dann nur, wenn tastenBeobachter ==0.

Ich nutze das derzeit (hab noch nicht alle Zusatzknöpfe eingebunden), da ich den Split (bei ner 44er lohnt der nicht, aber ich hab ne 61er) während dem Spiel ausschalten kann, ich kann jederzeit den rechts gespielten Kanal auf die komplette Tastatur legen, oder den linken, oder auch den Split wieder zuschalten.
Dann müssen natürlich die Oktaven wieder angepasst werden.

Hey Leute,
Ich habe versucht eure Tipps anzuwenden, habe allerdings bisher eher wenig erfolg :confused:

@klaus_ww:

Die Idee mit dem offset verstehe ich, allerdings weiß ich nicht wie ich es in meinem code einbinden soll.
Theoretisch muss ich ja alle Werte die ich hier definiere, bei knopfdruck um 12 addieren bzw um 12 subtrahieren.
Aber wie mache ich das? :confused:

uint8_t keyToMidiMap[4][12] =   
{ 
 36,37,38,39,
 40,41,42,43,
 44,45,46,47,
 48,49,50,51,
 52,53,54,55,
 56,57,58,59,
 60,61,62,63,
 64,65,66,67,
 68,69,70,71,
 72,73,74,75,
 76,77,78,79,
 80,81,82,83,  
 };

@agmue:

Dein Tipp hat mich schon mal ne ganze Ecke weiter gebracht, nur bleibt auch hier die Frage offen, wie ich das ganze auf meine Midi Werte anwende.
Wo muss ich den Wert oktave unterbringen, damit er den gewünschten Effekt bringt?
Meine Idee wäre gewesen in der noteOn bzw noteOff funktion den Wert oktave einfach zu addieren.

void noteOn(int row, int col)
{
  Serial.write(NOTE_ON_CMD);
  Serial.write(keyToMidiMap[row+oktave][col]);
  Serial.write(NOTE_VELOCITY);
}

Leider klappt das aber nicht :confused:

@rabenauge:

dein Lösungsvorschlag hat mir leider nicht viel geholfen, da unsere Codes zu unterschiedlich sind und ich wie gesagt ein totaler Neuling bin :smiley: trotzdem vielen Dank :slight_smile:

Dein Problem ist, dass du gar nicht verstehst, was da gemacht werden muss bzw. soll.
Eigentlich isses ganz simpel: aus deiner Map da kommt am Ende ne einfache Tastennummer heraus- dort drin wird nich rum gepfuscht.
Zu dieser Nummer addierst du einfach, bevor du die Daten sendest, den gewünschten Offset.

Das, was du da versuchst, verhaut dir die ganze Matrix, das kann so gar nicht funktionieren.
Probier es mal so:

tastenNummer=keyTomMidiMap+offset;
Serial.write(tastenNummer]);

Damit hast du leider sehr Recht.

Ich habs probiert, allerdings bekomme ich diese Fehlermeldung:

invalid conversion from 'uint8_t (*)[12] {aka unsigned char (*)[12]}' to 'uint8_t {aka unsigned char}' [-fpermissive]

Yees! hat jetzt doch geklappt! :))
vielen vielen Dank!
Das Einzige was jetzt noch stört sind die Notenhänger, die du ja auch schon angesprochen hast.
Gibt es keine Möglichkeit, dass das programm in diesem Falle automatisch einen noteOff befehl und gleichzeitig wieder einen noteOn befehl mit der veränderten oktave ausführt?

Könnte man natürlich tun aber durchdenk dir das mal:
während ich eine Taste spiele (gedrückt halte) wechsle ich die Oktaven (warum sollte man das tun??).
Nun aber wird der eben gespiele Ton gestoppt und stat dessen ein neuer, ne Oktave höher oder tiefer gespielt?

Welchen Sinn sollte das machen? :astonished:

Besser ist es, wie ich es oben beschrieben hatte: immer bei noteOn wird ein Zähler eins hochgezählt (anfangs steht der natürlich auf 0), und bei jedem NoteOff (ich hoffe übrigens, du sendest nicht wirklich den NoteOff, sondern NoteOn mit Anschlag 0) wird der selbe Zähler um eins verringert.
Dann ist klar: immer wenn er auf 0 steht, kann man problemlos die Oktave anpassen.
So kann man, während dem Spiel, mit der freien Hand umschalten, und der nächste Ton, den ich anschlage (vorausgesetzt, ich hab alle anderen los gelassen) landet im neuen Bereich.
Das hab ich jetzt schon nen paar Wochen so und-es spielt sich hervorragend.

Wenn du es einfacher haben willst (ist aber schon bissel Murks, weils dann beim umschalten auf dein Timing ankommt, was in meinem Fall Wurst ist) kannst du ja beim umschalten einfach den all-notes-off-Befehl senden.
Das wäre ne quick&dirty-Lösung, aber eigentlich Stümperei.