/* Midi Off Verzögerer von Wolfgang Spahn 09_2008 Auslesen von Midi Daten am RX-Eingang und Senden ein des Midi Signal mit verzögertem MidiOff Signal ############################################################################################################# ACHTUNG: Midi-Kabel beim Starten ausgesteckt lassen, da der Bootloader sonst glaubt, ein neues Programm wird geladen. Absturzgefahr, neu programierung ist manchmal nötig. ############################################################################################################# Grundlegendes über das MIDI Protokoll MIDI Signale fangen mit einem Status byte an, es können ein oder zwei Datenbyte folgen Beispiel: 144-36-100 das Statusbyte "144": "144" steht für Note an am Kanal 1. in diesem Fall ist das zweite Byte der Notenwert (36 = mittleres C) das dritte Byte ist die Anschlagsdynamik der Note (wie stark die Taste gedrückt wurde = 100) Beispiel: 128-36 das ist eine "Note aus" Signale (Statusbyte = 128). gefolgt vom Notenwert (DatenByte = 36) da das "Note aus" Signal keine Dynamik braucht, gibt es nur zwei Byte Anmerkung: einige Keyboards senden keine "Note aus" Signale, statt dessen nur ein "Note an" mit null Dynamik */ //Definition der Variablen int gMidiSignal[3] = {0, 0, 0}; // Tabelle für das Midi Signal, (Statusbyte, Datenbyte1, Datenbyte2) // oder (Status mit Kanal, Note, Dynamik) int gStatus = 0; // Statuswert: // 0 = kein Statusbyte im Speicher, // 1 = StatusByte und kein Datenbyte im Speicher, // 2 = StatusByte und ein Datenbyte im Speicher, // 3 = StatusByte und zwei Datenbyte im Speicher, int gWert = 0; // Zwischenspeicher für den Wert am Seriellen Einganges (0 bis 255) int gSchleife = 0; // Schleife an 1,2,3,4, Schleife aus = 0 int gDauer = 0; // Länge des Pattern int gKontrollLed = 13; // Auswahl des Pins für die Kontroll LED int gPWM1 = 9; // Auswahl der PWM Kanals (Pin 9) int gDynamik = 0; // Wert des PWM Ausgang in Abhaengigkeit der Dynamik int gMidiPower = 7; // Auswahl des Pins für die Stromversorgung des Midi In Moduls int gPad1Indikator = 0; // Indikator um anzuzeigen ob ein Ausgang ON ist int gPad1Dauer = 32000; // Zeitdauer; wielange soll der Ausgang ON sein // 400 000 entspricht 12,5 sek (geht nicht als integer, besser long variables) // 32 000 entspricht 1 sek int gPad1Zaehler = 0; // Zaeler zum festzustellen wie lang der Ausgang schon ON ist int gPad1Kanal = 144; // Zwischenspeicher für den Midikanal int gPad2Indikator = 0; // ... int gPad2Dauer = 32000; // ... int gPad2Zaehler = 0; // ... int gPad2Kanal = 144; // ... int gPad3Indikator = 0; int gPad3Zaehler = 0; int gPad3Dauer = 32000; int gPad3Kanal = 144; int gPad4Indikator = 0; int gPad4Dauer = 32000; int gPad4Zaehler = 0; int gPad4Kanal = 144; int gPadSmall1Indikator = 0; int gPadSmall1Dauer = 32000; int gPadSmall1Zaehler = 0; int gPadSmall1Kanal = 144; int gPadSmall2Indikator = 0; int gPadSmall2Dauer = 32000; int gPadSmall2Zaehler = 0; int gPadSmall2Kanal = 144; int gPadSmall3Indikator = 0; int gPadSmall3Dauer = 32000; int gPadSmall3Zaehler = 0; int gPadSmall3Kanal = 144; //****************************************************************************************************************************** //Ein- und Ausgänge definieren und den Serialport konfigurieren void setup() { pinMode(gKontrollLed,OUTPUT); // Status LED Pin als Ausgang festlegen pinMode(gMidiPower,OUTPUT); // Stromversorgungs Pin als Ausgang festlegen Serial.begin(31250); //Baud Rate für die serielle Komunikation auf 31250 Baud (Bits in der Sekunde) festlegen digitalWrite(gKontrollLed, HIGH); // Kontoll LED anschalten digitalWrite(gMidiPower, HIGH); // Stromversorgung Midi In Modul anschalten } //**************************************************************************************************************************** /* HauptSchleife: */ void loop () { Zaehlen (); Auslesen (); Senden (); } //**************************************************************************************************************************** /* Auslesen der MIDI Daten und Fallprüfung: Prüfen ob Serielle Daten im Zwischenspeicher sind und Speicher derselben Unterscheidung der Fälle: - Eingang eins Signal ein Statusbyte der Zwischenspeicher ist leer - Speichern es liegt ein Statusbyte mit einem ( oder zwei) Datenbyte im Zwischenspeicher - DatenErgaenzen, Schalten, Speichern ein Datenbyte der Zwischenspeicher ist leer - StatusErgaenzen (gStatus = 1), Speichern es liegt ein Statusbyte mit keinem oder einem Datenbyte im Zwischenspeicher - Speichern - kein Eingang eines Signals - "Zuruech" (Die Tabelle mit den Midi Werten wir immer überschrieben) Bei "Note aus" (Statuswert 128) wird nicht auf ein zweites Datenbyte gewartet */ void Auslesen () { if (Serial.available() > 0) { // Prüfen ob Daten am Seriellen Einngang vorhanden sind gWert = Serial.read(); // Lesen des Serielle Einganges und Speichern des Wertes if ((gWert >= 128) && (gStatus == 0)){ // Prüfen ob das Byte ein Statusbyte ist (Werte Zwischen 128 und 255) // und der Speicher leer ist Speichern (); } else if ((gWert >= 128) && (gStatus > 0)){ // Prüfen ob das Byte ein Statusbyte ist (Werte Zwischen 128 und 255) // und ein Status Signale bereits im Speicher liegt DatenErgaenzen (); Senden (); Speichern (); } else if ((gWert < 128) && (gStatus == 0)){ // Prüfen ob das Byte ein Datenbyte ist (Werte zwischen 0 und 127) // obwohl kein Status Signale im Speicher liegt gStatus = 1; Speichern (); } else if ((gWert < 128) && (gStatus > 0)){ // Prüfen ob das Byte ein Datenbyte ist (Werte zwischen 0 und 127) // und ein Status Signale im Speicher liegt Speichern (); } } if ((gMidiSignal[0] >= 128) && (gMidiSignal[0] <= 143) && (gStatus == 2)) { // Prüfen ob ein "Note aus" Signal vorliegt DatenErgaenzen (); } } //******************************************************************************************************************************* /* Ablegen und Speicher der Midi Werte in einer Tabelle */ void Speichern () { switch (gStatus) { // gStatus Prüfen case 0: // Fall 0: StatusByte nicht vorhanden if (gWert >= 128) { // Prüfen ob das Byte ein Statusbyte ist (erstes Bit gleich 1 bzw. // Dez.Werte Zwischen 128 und 255) gMidiSignal[0] = gWert; // Speichern des Wertes am ersten Tabellenplatz gStatus = 1; // Status Wert auf 1 setzen } break; // Fallunterscheidung beenden case 1: // Fall 1: StatusByte vorhanden und kein Datenbyte if (gWert <= 127) { gMidiSignal[1] = gWert; gStatus = 2; // Status Wert auf 2 setzen } break; // Fallunterscheidung beenden case 2: // Fall 1: StatusByte vorhanden und ein Datenbyte if (gWert <= 127) { gMidiSignal[2] = gWert; gStatus = 3; // Status Wert auf 3 setzen break; // Fallunterscheidung beenden } } } //***************************************************************************************************************************** // Ergänzen eines unvollständigen Midisignals (mit 0 auffüllen) void DatenErgaenzen () { switch (gStatus) { // gStatus Prüfen case 1: // Fall 1: StatusByte vorhanden und kein Datenbyte gMidiSignal[1] = 0; gMidiSignal[2] = 0; gStatus = 3; // Status Wert auf 3 setzen break; // Fallunterscheidung beenden case 2: // Fall 1: StatusByte vorhanden und ein Datenbyte gMidiSignal[2] = 0; gStatus = 3; // Status Wert auf 3 setzen break; // Fallunterscheidung beenden } } //****************************************************************************************************************************** /* Anschalten der Ausgänge in Abhängigkeit der Mididaten und Abschalten der Ausgänge in Abhängigkeit der erreichten Anschaltdauer */ void Senden(){ // Abschalten der einzelnen Ausgänge wenn die Dauer erreicht ist if (gPad1Zaehler == gPad1Dauer) { // Prüfen ob die Dauer erreicht ist MidiOff1Senden (); // Midi Off Senden } if (gPad2Zaehler == gPad2Dauer) { // Prüfen ob die Dauer erreicht ist MidiOff2Senden (); // Midi Off Senden } if (gPad3Zaehler == gPad3Dauer) { // Prüfen ob die Dauer erreicht ist MidiOff3Senden (); // Midi Off Senden } if (gPad4Zaehler == gPad4Dauer) { // Prüfen ob die Dauer erreicht ist MidiOff4Senden (); // Midi Off Senden } if (gPadSmall1Zaehler == gPadSmall1Dauer) { // Prüfen ob die Dauer erreicht ist MidiOffSmall1Senden (); // Midi Off Senden } if (gPadSmall2Zaehler == gPadSmall2Dauer) { // Prüfen ob die Dauer erreicht ist MidiOffSmall2Senden (); // Midi Off Senden } if (gPadSmall3Zaehler == gPadSmall3Dauer) { // Prüfen ob die Dauer erreicht ist MidiOffSmall3Senden (); // Midi Off Senden } // Anschalten der Ausgänge wenn ein Kompletes Note On Signal eingegangen ist if (gStatus == 3) { // Prüfen ob ein vollständiges Midi Signal vorliegt if (gMidiSignal[0] >= 144 && gMidiSignal[0] < 160 && gMidiSignal[2] > 0) { // Nur wenn ein Note On Signal anliegt dann Schalten switch (gMidiSignal[1]) { case 40: // bei Note E1 (Pad 1) if (gPad1Indikator == 1) { // kontrolieren ob noch eine Note On ist MidiOff1Senden(); // Midi Off senden MidiSenden(); // Midi On senden gPad1Kanal = gMidiSignal[0] - 16; // als Off Kanal Speichern, 16 weniger (zB: 156 ist kanal 10 on; 140 ist Kanal 10 off) gPad1Indikator = 1; // Indikator fuer Pad 1 anschalten } else { MidiSenden(); // Senden der Midi Daten gPad1Kanal = gMidiSignal[0] - 16; // als Off Kanal Speichern, 16 weniger gPad1Indikator = 1; // Indikator fuer Pad 1 anschalten } break; case 50: // bei Note D2 (Pad 2) if (gPad2Indikator == 1) { // kontrolieren ob noch eine Note On ist MidiOff2Senden(); // Midi Off senden MidiSenden(); // Midi On senden gPad2Kanal = gMidiSignal[0] - 16; // als Off Kanal Speichern, 16 weniger gPad2Indikator = 1; // Indikator anschalten } else { MidiSenden(); // Senden der Midi Daten gPad2Kanal = gMidiSignal[0] - 16; // als Off Kanal Speichern, 16 weniger gPad2Indikator = 1; // Indikator anschalten } break; case 47: // bei Note B1 (Pad 3) if (gPad3Indikator == 1) { // kontrolieren ob noch eine Note On ist MidiOff3Senden(); // Midi Off senden MidiSenden(); // Midi On senden gPad3Kanal = gMidiSignal[0] - 16; // als Off Kanal Speichern, 16 weniger gPad3Indikator = 1; // Indikator anschalten } else { MidiSenden(); // Senden der Midi Daten gPad3Kanal = gMidiSignal[0] - 16; // als Off Kanal Speichern, 16 weniger gPad3Indikator = 1; // Indikator anschalten } break; case 43: // bei Note G1 (Pad 4) if (gPad4Indikator == 1) { // kontrolieren ob noch eine Note On ist MidiOff4Senden(); // Midi Off senden MidiSenden(); // Midi On senden gPad4Kanal = gMidiSignal[0] - 16; // als Off Kanal Speichern, 16 weniger gPad4Indikator = 1; // Indikator anschalten } else { MidiSenden(); // Senden der Midi Daten gPad4Kanal = gMidiSignal[0] - 16; // als Off Kanal Speichern, 16 weniger gPad4Indikator = 1; // Indikator anschalten } break; case 57: // bei Note A2 (Pad Small 1) if (gPadSmall1Indikator == 1) { // kontrolieren ob noch eine Note On ist MidiOffSmall1Senden(); // Midi Off senden MidiSenden(); // Midi On senden gPadSmall1Kanal = gMidiSignal[0] - 16; // als Off Kanal Speichern, 16 weniger gPadSmall1Indikator = 1; // Indikator anschalten } else { MidiSenden(); // Senden der Midi Daten gPadSmall1Kanal = gMidiSignal[0] - 16; // als Off Kanal Speichern, 16 weniger gPadSmall1Indikator = 1; // Indikator anschalten } break; case 51: // bei Note D#2 (Pad Small 2) if (gPadSmall2Indikator == 1) { // kontrolieren ob noch eine Note On ist MidiOffSmall2Senden(); // Midi Off senden MidiSenden(); // Midi On senden gPadSmall2Kanal = gMidiSignal[0] - 16; // als Off Kanal Speichern, 16 weniger gPadSmall2Indikator = 1; // Indikator anschalten } else { MidiSenden(); // Senden der Midi Daten gPadSmall2Kanal = gMidiSignal[0] - 16; // als Off Kanal Speichern, 16 weniger gPadSmall2Indikator = 1; // Indikator anschalten } break; case 42: // bei Note A#1 (Pad Small 3) if (gPadSmall3Indikator == 1) { // kontrolieren ob noch eine Note On ist MidiOffSmall3Senden(); // Midi Off senden MidiSenden(); // Midi On senden gPadSmall3Kanal = gMidiSignal[0] - 16; // als Off Kanal Speichern, 16 weniger gPadSmall3Indikator = 1; // Indikator fuer Pad 1 anschalten } else { MidiSenden(); // Senden der Midi Daten gPadSmall3Kanal = gMidiSignal[0] - 16; // als Off Kanal Speichern, 16 weniger gPadSmall3Indikator = 1; // Indikator anschalten } break; } } gStatus = 0; // Statusanzeige auf Null setzen } } //************************************************************************************************************************* // Senden der Midi Daten void MidiSenden() { Serial.print(gMidiSignal[0], BYTE); Serial.print(gMidiSignal[1], BYTE); Serial.print(gMidiSignal[2], BYTE); } //************************************************************************************************************************* // Senden das Midi Off Befehles und Rücksetzen der Indikatoren und Zähler void MidiOff1Senden() { Serial.print(gPad1Kanal, BYTE); // Senden des Kanals und Off auf demselben Serial.print(40, BYTE); // Senden der Note gPad1Zaehler = 0; // Rücksetzen des Zaehlers gPad1Indikator = 0; // Indikator fuer Pad 1 ausschalten } void MidiOff2Senden() { Serial.print(gPad2Kanal, BYTE); // Senden des Kanals und Off auf demselben Serial.print(50, BYTE); // Senden der Note gPad2Zaehler = 0; // Rücksetzen des Zaehlers gPad2Indikator = 0; // Indikator fuer Pad 1 ausschalten } void MidiOff3Senden() { Serial.print(gPad3Kanal, BYTE); // Senden des Kanals und Off auf demselben Serial.print(47, BYTE); // Senden der Note gPad3Zaehler = 0; // Rücksetzen des Zaehlers gPad3Indikator = 0; // Indikator fuer Pad 1 ausschalten } void MidiOff4Senden() { Serial.print(gPad4Kanal, BYTE); // Senden des Kanals und Off auf demselben Serial.print(43, BYTE); // Senden der Note gPad4Zaehler = 0; // Rücksetzen des Zaehlers gPad4Indikator = 0; // Indikator fuer Pad 1 ausschalten } void MidiOffSmall1Senden() { Serial.print(gPadSmall1Kanal, BYTE); // Senden des Kanals und Off auf demselben Serial.print(57, BYTE); // Senden der Note gPadSmall1Zaehler = 0; // Rücksetzen des Zaehlers gPadSmall1Indikator = 0; // Indikator fuer Pad 1 ausschalten } void MidiOffSmall2Senden() { Serial.print(gPadSmall2Kanal, BYTE); // Senden des Kanals und Off auf demselben Serial.print(51, BYTE); // Senden der Note gPadSmall2Zaehler = 0; // Rücksetzen des Zaehlers gPadSmall2Indikator = 0; // Indikator fuer Pad 1 ausschalten } void MidiOffSmall3Senden() { Serial.print(gPadSmall3Kanal, BYTE); // Senden des Kanals und Off auf demselben Serial.print(42, BYTE); // Senden der Note gPadSmall3Zaehler = 0; // Rücksetzen des Zaehlers gPadSmall3Indikator = 0; // Indikator fuer Pad 1 ausschalten } //************************************************************************************************************************* /* Wert von Zahler der verschiedenen Pads jeden Loop um eins erhöhen wenn der dazugehörige indikator an ist. */ void Zaehlen () { if (gPad1Indikator == 1){ gPad1Zaehler += 1; } if (gPad2Indikator == 1){ gPad2Zaehler += 1; } if (gPad3Indikator == 1){ gPad3Zaehler += 1; } if (gPad4Indikator == 1){ gPad4Zaehler += 1; } if (gPadSmall1Indikator == 1){ gPadSmall1Zaehler += 1; } if (gPadSmall2Indikator == 1){ gPadSmall2Zaehler += 1; } if (gPadSmall3Indikator == 1){ gPadSmall3Zaehler += 1; } } // Blinken zum Testen void Blinken(){ digitalWrite(gKontrollLed, LOW); delay(200); digitalWrite(gKontrollLed, HIGH); delay(200); } //******************************************************************************************************************************************************