Midi Keyboard Pitch Bend Wheel

Hallo liebes Forum.

Ich habe mir vor längerer Zeit ein Midi keyboard aus einer alten E-Orgel Tastatur und einem Arduino gebaut. Da ich bei weitem nicht über genug Programmiererfahrung verfüge, habe ich mir den Code nach und nach aus dem Internet zusammengesucht und frankensteinartig zusammengeflickt bis es schlussendlich funktioniert hat.

Das Keyboard verfügt unter anderem über einen Joystick. Mit diesem würde ich gerne in der Software Maschine 2 das pitch bend steuern. Das problem ist, dass es hierfür keine Midi learn taste gibt. Es muss also glaube ich um in dem Programm pitch ben kontrollieren zu können, der offizielle Midi Befehl hierfür verwendet werden. Leider scheitere ich daran, mein Code so zu modizifieren, dass mein Joystick dies tut. Vielleicht könmnt ihr mir ja helfen!

Hier mein Code:

#include <midi_Defs.h>
#include <midi_Message.h>
#include <midi_Namespace.h>
#include <midi_Settings.h>
#include <Bounce2.h>

Bounce debouncerMinus = Bounce();
Bounce debouncerPlus = Bounce();

#define  MIDI_OUT_CHANNEL    0x0
#define NUM_ROWS 4
#define NUM_COLS 12

const int row1Pin = 2;
const int row2Pin = 3;
const int row3Pin = 4;
const int row4Pin = 5;
const int dataPin = 6;
const int latchPin = 7;
const int clockPin = 8;
const byte tasterPlusPin = 9;
const byte tasterMinusPin = 10;
int volValue = 0;
int volPin = A2;
int val = 0;
int oktave = 0;
int speicher = 0;
int controlChange = 176;
int noteVelocity = 0;
int tastenBeobachter = 0;
int potiWertPitch = 0;
int controllerWertPitch = 0;
int controllerWertPitchAlt = 0;
int controllerNummerPitch = 21;
int potiWert[8];
int controllerWert[8];
int controllerWertAlt[8];
int controllerNummer[] = {23,24,25,26,27,28,29,30};
int i = 0;
int bit1 = 0;
int bit2 = 0;
int bit3 = 0;

boolean keyPressed[NUM_ROWS][NUM_COLS];


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


int bits[] =
{
  B11111110,
  B11111101,
  B11111011,
  B11110111,
  B11101111,
  B11011111,
  B10111111,
  B01111111
};

void setup()
{
  pinMode(dataPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(latchPin, OUTPUT);

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

  pinMode(11, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(13, OUTPUT);

  Serial.begin(31250);
  pinMode(tasterMinusPin, INPUT_PULLUP);
  pinMode(tasterPlusPin, INPUT_PULLUP);
  debouncerMinus.attach(tasterMinusPin);
  debouncerMinus.interval(10);
  debouncerPlus.attach(tasterPlusPin);
  debouncerPlus.interval(10);
}

void loop()
{
 potiPitchAbfragen(A3); 
 volValue = analogRead(volPin);  
 noteVelocity = map(volValue, 0, 1023, 0, 127);   
  
  for (i = 0; i <= 7; i++) {
 bit1 = bitRead(i,0);
 bit2 = bitRead(i,1);
 bit3 = bitRead(i,2);
 
 digitalWrite(11, bit1);
 digitalWrite(12, bit2);
 digitalWrite(13, bit3);
 
 potisAbfragen(i,A0);

 }
  for (int colCtr = 0; colCtr < NUM_COLS; ++colCtr)
  {
    scanColumn(colCtr);

    int rowValue[NUM_ROWS];
    rowValue[0] = !digitalRead(row1Pin);
    rowValue[1] = !digitalRead(row2Pin);
    rowValue[2] = !digitalRead(row3Pin);
    rowValue[3] = !digitalRead(row4Pin);

    for (int rowCtr = 0; rowCtr < NUM_ROWS; ++rowCtr)
    {
      if (rowValue[rowCtr] != 0 && !keyPressed[rowCtr][colCtr])
      {
        keyPressed[rowCtr][colCtr] = true;
        tastenBeobachter ++;
        noteOn(0x91, keyToMidiMap[rowCtr][colCtr], noteVelocity);
      }

      debouncerMinus.update();
      debouncerPlus.update();

      if ( debouncerMinus.fell()&& tastenBeobachter == 0) {
        oktave = oktave - 12;
      }
      if ( debouncerPlus.fell()&& tastenBeobachter == 0) {
        oktave = oktave + 12;
      }   
      if (debouncerMinus.fell()&& tastenBeobachter != 0) {
        speicher = speicher - 12;
      }
      if (debouncerPlus.fell()&& tastenBeobachter != 0) {
        speicher = speicher + 12;
      }
      if (tastenBeobachter == 0) {
        oktave = oktave + speicher;
        speicher = 0;
      }
    }

    for (int rowCtr = 0; rowCtr < NUM_ROWS; ++rowCtr)
    {
      if (rowValue[rowCtr] == 0 && keyPressed[rowCtr][colCtr])
      {
        keyPressed[rowCtr][colCtr] = false;
        tastenBeobachter --;
        noteOn(0x91, keyToMidiMap[rowCtr][colCtr], 0);
      }
    }
  }
}
void potiPitchAbfragen(int analogPin){
  potiWertPitch = 0.05 * potiWertPitch + 0.95 * analogRead(analogPin);
  controllerWertPitch = map(potiWertPitch,0,1023,0,127);
 
 if (controllerWertPitch != controllerWertPitchAlt) {
  sendeMIDI(controlChange, controllerNummerPitch, controllerWertPitch);
  controllerWertPitchAlt = controllerWertPitch;
 }
}
void potisAbfragen(int zaehler, int analogPin) {
 potiWert[zaehler] = 0.05 * potiWert[zaehler] + 0.95 * analogRead(analogPin);
 controllerWert[zaehler] = map(potiWert[zaehler],0,1023,0,127);
 if (controllerWert[zaehler] != controllerWertAlt[zaehler]) {
 sendeMIDI(controlChange, controllerNummer[zaehler], controllerWert[zaehler]);
 controllerWertAlt[zaehler] = controllerWert[zaehler];
 }
}

void sendeMIDI(int statusByte, int dataByte1, int dataByte2) {
 Serial.write(statusByte);
 Serial.write(dataByte1);
 Serial.write(dataByte2);
}

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 cmd, int pitch, int velocity) {
  Serial.write(cmd);
  Serial.write(pitch + oktave);
  Serial.write(velocity);
}

Tolles Flickwerk-blickst du da irgendwas ? :confused:

Pitchbend verfügt, im Gegensatz zu den einfacheren Midi-Befehlen, über einen grösseren Wertebereich (zwei Byte).
Die musst du dann auch senden.

https://www.midikits.net/midi_analyser/pitch_bend.htm

Hallo Rabenauge,

danke für deine Antwort.

Ich glaube ich hätte meinen Code einfach weglassen sollen und fragen sollen, ob mir jemand einen Beispiel Code eines Midi Pitchbendbefehls zeigen kann, welcher durch einen Poti gesteuert wird.

Die Seite hilft mir leider nicht weiter, da ich bei weitem nicht gut genug bin um den Inhalt in Arduino Code umzusetzen. :roll_eyes:

Hi

Wenn hier Jeder meisterhaft Coden würde, wäre das Forum gleich eine ganze Ecke leerer.
Und: Nicht nur Du hast ganz Unten angefangen - man kann halt im Laufe der Zeit besser werden (man kann's auch lassen, dann bist Du hier aber gaaanz falsch).

Da ich mit Midi absolut Nichts am Hut hab von mir nur so viel: Kopf hoch, wird schon werden.

MfG

Code-Snippet:

void sendeMIDI(int statusByte, int dataByte1, int dataByte2) {
 Serial.write(statusByte);
 Serial.write(dataByte1);
 Serial.write(dataByte2);
}

Du kannst das dataByte1 ja mal stumpf auf 0 setzen und mit dataByte2 spielen.
Den Analogwert vom Pitchbend-Wheel einlesen kannst Du? Hab Deinen Code nicht gelesen :smiling_imp:

Hallo,
hier ist meine Funktion die ich bisher benutze. Diese beinhaltet ja an sich genau das was Klaus gepostet hat. Bisher nutze ich für Byte 1 die Zahl 176, also Control Change. Wie Rabenauge schon sagte, ist der Pitch Bend befehl ja aber kein Control Change sondern ein eigener Befehl. Leider weiß ich zum verrecken nicht wie ich das in meine Funktion reinbekommen soll.
Vielleicht kann mir ja jemand konkret sagen, was ich für Byte 1 etc. verwenden muss. Der Link den Rabenauge gepostet hat liefert wahrscheinlich genau diese Antwort, aber leider blicke ich bei dem ganzen Mischmasch von Hexadecimal, Binär und Decimal nicht durch. :sob: :sweat_smile:

void potiPitchAbfragen(int analogPin){
potiWertPitch = 0.05 * potiWertPitch + 0.95 * analogRead(analogPin);
controllerWertPitch = map(potiWertPitch,0,1023,0,127);

if (controllerWertPitch != controllerWertPitchAlt) {
sendeMIDI(controlChange, controllerNummerPitch, controllerWertPitch);
controllerWertPitchAlt = controllerWertPitch;
}
}

Ich habe es jetzt doch herausgefunden!

folgende Seite hat mir geholfen:

http://www.zem-college.de/midi/mc_cvm7.htm

Für Byte 1 habe ich 224 eingegeben, für byte 2: 0 und für Byte 3: 1-127 variabel an meinen Joystick gekoppelt

Hm-das ist dann aber ne Krücke.
Damit hast du ja nur einen Bruchteil des tatsächlichen Wertebereichs....ok-wenn du damit leben kannst, ich könnt es nicht (ich bediene diverse Synthies mit meinem Eigenbau), da will man auch rausholen, was drinsteckt.

Der Trick besteht darin, den Wert (dezimal 0 - 16383) auf zwei Byte aufzuteilen.

Den Dezimalwert bekommst du, indem du deinen Joystick halt auf diesen Wertebereich mappst (ich gehe von nem normalen Setup aus, wo der Stick analog ausgelesen wird):

dezimalWert=map(analogRead(joystick),0,1023,0,16383).

Nun musst du diesen Wert einfach auf die zwei Byte aufteilen, und die nacheinander senden.
Wie man das machen kann, wird z.B. hier: Konvertieren Integer in zwei Byte und umgekehrt... - Deutsch - Arduino Forum beschrieben.

Hi

Trotzdem wird sich Nichts an dem 'Raster' des Joystick ändern - ob ich 0...1023 in 1er Schritten habe, oder 0...16383 in 16er Schritten - ich sehe Da keinen Vorteil.

MfG

Naja-zwischen 1024 Schritten und 255 Schritten gibt es schon nen gewissen Unterschied.... :o

Gerade beim Pitchbend-Controller ist der bei bestimmten Instrumenten das Detail, was zwischen "super" und "unbrauchbar" unterscheidet...wahrscheinlich ist genau das eben der Grund, wieso er zwei Byte bekommen hat.
Kannst ja mal versuchen, eine synthetische Gitarre mit nem Byte Auflösung zu biegen...ich möcht das lieber nicht hören müssen. :confused:

Hi

Ok, Ein Byte gegen zwei Bytes lasse ich ja gelten - aber die Auflösung bleibt bei 10bit-ADC eben bei 10bit, egal, wie sehr ich die Zahl auch aufpumpe.
Ob ich für's menschliche Auge zwei Nullen dran hänge, oder für den Genossen µC den Kram zwei Bit nach links schiebe - an den enthaltenen Informationen 0...1023(+1) oder 0...16383(+16) ändert sich Nichts.

MfG

Noch zu erwähnen ist

  • der Synthie bestimmt die Grenzen des Pitchbending, der Gesamtbereich wird mit den 2 Bytes überstrichen
  • der Nullwert ist die Mitte, denn man kann ja nach oben und unten verbiegen