MIDI Clock mit dem Arduino empfangen und auswerten!

Hallo,

ich möchte gern MIDI Clock mit dem Arduino (Teensy 3.1) auswerten. Die Hardware funktioniert.
Das senden von Daten hat geklappt. Jetzt möchte ich Midi Clock empfangen was auch schon geht.
StatusByte (248 ) Timingclock wird empfangen.

Um die genaue BPM zu ermitteln möchte ich gern die Zeit zwischen zwei Timestamps messen. Ich möchte die Zeit zwischen zwei Statusbytes mit 248 messen.

Beispiel:
Bei 120 BPM am Eingang sollten zwischen den beiden Statusbytes eine Zeit von (20833,333 Micos) liegen.
Es wird ja bei einem Takt 96 mal ein Byte mit 248 gesendet!

Aber wie schreibe ich den Code so, dass ich zwei Zeiten speichern kann?
if (StatusByte1_IN == 248)
{
Timestamp1 = micros();
}
Ich möchte gern Timestamp2 beim nächsten eintreffen von Statusbyte mit 248 setzten und dann rechnen. Aber wie ??? Hat jemand eine Vorschlag ?
Zeit =(Timestamp2 – Timestamp1)

In MIDIOX würde das so aussehen. siehe Bild!

Genau das was MIDIOX macht möchte ich mit dem Teensy auch.

Ich würde mich über Hilfe freunen

MFG Michael
 

Anhänge

  • MIDIOX.jpg
    MIDIOX.jpg
    72,9 KB · Aufrufe: 239
Du brauchst zwei Variablen:

CurrentTimeStamp
LastTimeStamp

Schleife:
{
CurrentTimeStamp = *Zeit messen*

Time = CurrentTimeStamp - LastTimeStamp

LastTimeStamp = CurrentTimeStamp
}
-
 
Hallo Max,

habe es jetzt mal so gemacht !
------------------------------------------------------------------------------------------------
unsigned long BPM;
unsigned long Time;
unsigned long LastTimeStamp;
unsigned long CurrentTimeStamp;


if (StatusByte1_IN == 248)

{
CurrentTimeStamp = micros(); //aktuellen Zeitstempel speichern
delay(1);
Time = (CurrentTimeStamp - LastTimeStamp); // Zeit berechnen
delay(1);
LastTimeStamp = CurrentTimeStamp; // letzten Zeitstempel neu speichern
delay(1);
//Beispiel BPM = 60000000/(20833*24); // BPM berechnen
BPM = 60000000/(Time*24); // BPM berechnen


}








Serial.println("Start");
Serial.println(StatusByte1_IN,HEX);
Serial.println(LastTimeStamp);
Serial.println(CurrentTimeStamp);
Serial.println(Time);
Serial.println(BPM);

Ergebnis im Serialmonitor:

Start
F8
229944390
229944390
229944390
0
Start
F8
230047453
230047453
230047453
0


die Time sollte eigentlich 20833 sein . Wie kann man auf das nächste Statusbyte warten ?

Gruß MIchael
 
Der Code, der die Zeiten misst, muss in die Loop-Methode - dadurch wird er in einer Endlos-Schleife ausgeführt.

Das Tempo kannst du erst berechnen, wenn die zweite Clock-Nachricht eintrifft (ansonsten ist die Zeit beim ersten Mal gleich "0" wie in deinem Code). Wenn das ein Problem sein sollte, musst du diesen Fall getrennt behandeln und bei der Messung überprüfen, ob schon ein gültiger LastTimeStamp vorhanden ist.

Das delay(1) solltest du rausnehmen, das braucht es nicht.
 
In etwa so:

unsigned long BPM;
unsigned long Time;
unsigned long LastTimeStamp ;
unsigned long CurrentTimeStamp;

void setup()
{
// alles initialisieren...
}

void loop()
{
if (neue MIDI-Nachricht angekommen)
{
if (MIDI-Nachricht ist Clock-Event && LastTimeStamp > 0)
{
CurrentTimeStamp = micros();
Time = (CurrentTimeStamp - LastTimeStamp); // Zeit berechnen
LastTimeStamp = CurrentTimeStamp; // letzten Zeitstempel neu speichern
BPM = 60000000/(Time*24); // BPM berechnen
// BPM ausgeben
}
}
}
 
Max schrieb:
In etwa so:

unsigned long BPM;
unsigned long Time;
unsigned long LastTimeStamp ;
unsigned long CurrentTimeStamp;

void setup()
{
// alles initialisieren...
}

void loop()
{
if (neue MIDI-Nachricht angekommen)
{
if (MIDI-Nachricht ist Clock-Event && LastTimeStamp > 0)
{
CurrentTimeStamp = micros();
Time = (CurrentTimeStamp - LastTimeStamp); // Zeit berechnen
LastTimeStamp = CurrentTimeStamp; // letzten Zeitstempel neu speichern
BPM = 60000000/(Time*24); // BPM berechnen
// BPM ausgeben
}
}
}

geht leider auch nicht :sad: Das Problem ist das Zweite Byte zu speichern !
 
Natürlich geht das, du hast es halt nicht richtig umgesetzt ;-)

-> poste am Besten mal deinen kompletten Code

Bist du dir sicher, dass dein Aufbau grundsätzlich funktioniert? Wie empfängst du denn die MIDI-Noten? Über die serielle Schnittstelle? (dann kann es einen Konflikt mit der Text-Ausgabe an den PC geben). Benutzt du eine MIDI-Library?
 
Hallo Max,

so siehts aus


//**************************************** Programm by SEQ *************************************************
// MIDI_CLOCK_INPUT
//15.10.2015
//18:10

// Eingang 120 BPM von MIDICLOCK nur Timming Clock


//Deklaration von globalen Variablen XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

byte IncomingByte = 0;
byte StatusByte1_IN = 248;
byte StatusByte2_IN = 248;
byte Data1Byte_IN = 0;
byte Data2Byte_IN = 0;

unsigned long BPM;
unsigned int Time;
unsigned long LastTimeStamp;
unsigned long CurrentTimeStamp;
/*
// Beispiel bei 120 BPM
unsigned int PulsTime_micros_IN = 20833;
unsigned long currentMicros = 0;
unsigned long previousMicros = 0;
unsigned long Timestamp1 = 0;
unsigned long Timestamp2 = 0;
unsigned long Timestamp3 = 0;
unsigned long Timestamp4 = 0;
byte BPM_IN = 120;
*/
// RX1LED Kontrolle für den Empfang von MIDI Daten auf RX1**************************************************

const int RX1LED = 13; // RX1 Empfang LED an Pin 13
int ledState13 = LOW; // ledState used to set the LED
//unsigned long previousMicros13 = 0; // will store last time LED was updated
//unsigned int RX1LEDinterval_micros= 500000; // Interval der RX1LED ist die PulsTime 24ppqn
int RX1 = 0; // RX1 PIN 0 Eingang
int DIG2 = 2; // DIG2 PIN 2 Eingang
byte RX1State = 0; // Signal an RX1 LOW oder HiGH


// BPM_LED Blinkt im 1/4 Takt ******************************************************************************
/*
const int BPM_LED = 14; // Beat Kontroll LED an Pin 14
int ledState14 = LOW; // ledState used to set the LED
unsigned long previousMicros14 = 0; // will store last time LED was updated
unsigned int BeatLEDinterval_micros = 250; // Interval der BeatLED ist die 1/4 Note


*/


void setup() // Setup Teil XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

{

// Pinbelgung festlegen

pinMode (RX1LED, OUTPUT);
// pinMode (BPM_LED,OUTPUT);
pinMode (RX1, INPUT);
pinMode (DIG2, INPUT);

// Serial Datenübertragung aufmachen !
Serial1.begin(31250) ; // DIN MIDI
}

// Unterprogramme und Funktionen****************************************************************************

// keine

void loop()// Loop Teil XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

{

// 1 Dateneingang mit der RX1LED anzeigen ******************************************************************

// Bedingung: Eingangssignal auf Low und HIGH prufen pro Byte
if (digitalRead(DIG2) == LOW) // == Ist gleich wie ( Wenn der Eingang LOW gemacht wird , mach was !
{
digitalWrite(RX1LED, HIGH);
}
else
{
digitalWrite(RX1LED, LOW);
}


// 2 MIDIDATEN lesen ***************************************************************************************

if (Serial1.available())

{
StatusByte1_IN = 0; // StatusByte auf Null setzen
StatusByte1_IN = Serial1.read(); // MIDIDaten lesen
}

// 3 Erstes StatusByte auf Timing Clock prüfen ( F8,248)****************************************************

//if (StatusByte1_IN == 248 && LastTimeStamp > 0) Fehler geht nicht
if (StatusByte1_IN == 248)

{
CurrentTimeStamp = micros();
Time = (CurrentTimeStamp - LastTimeStamp); // Zeit berechnen
LastTimeStamp = CurrentTimeStamp; // letzten Zeitstempel neu speichern
BPM = 60000000 / (Time * 24); // BPM berechnen
}

// 4 Drucken ***********************************************************************************************

Serial.println("Start");
Serial.println(StatusByte1_IN);
Serial.println(CurrentTimeStamp);
Serial.println(LastTimeStamp);
Serial.println("Time");
Serial.println(Time);
Serial.println("BPM");
Serial.println(BPM);

}


Als Ergebiss kommt das raus

Start
248
188690691
188690691
Time
991
BPM
2522
Start
248
188691683
188691683
Time
992
BPM
2520
Start
248
188692674
188692674
Time
991
BPM
2522
Start
248
188693670
188693670
Time
996
BPM
2510
Start
248
188694661
188694661
Time
991
BPM
2522

Die Time mit 990 ist leder zu klein!
20833 micros wäre OK bei 120 BPM
 
Der Fehler ist hier:

if (Serial1.available())
{
...
-> hier gehört auch die Zeit-Berechnung rein! (ansonsten wird die Zeit zwischen zwei Loop-Aufrufen gemessen!)
}
 
Hier das Ergbniss !

Start
248
8807776
8807776
Time
21033
BPM
118
Start
248
8807776
8807776
Time
21033
BPM
118

Jetzt muß ich nur noch rausfinden warum 118 BPM angezeigt werden ,

120 BPM gehen rein

Erstmal vielen Dank für deine Hilfe!
Wieder ein Schritt weiter und was gelernt!

Gruß Michael
 
Hallo,
so das mit den LEDS klappt schon mal ganz gut und die BPM Anzeige mit einer Kommastelle habe ich auch hin bekommen :)

Jetzt ist das nächste Ziel einen sauberen 32Takt zum Pro One zu senden, was auch schon zum Teil funktioniert. Leider schwankt die berechnete Zeit zwischen den gemessenen MIDI CLOCK Signalen minimal. Das heißt, dass bei der Ausgabe, das 32 Signal leicht eiert. Ist ja auch klar weil die Berechneten Takt96 Werte nicht immer gleich sind.

Hier mal ein Ausdruck:
Takt96:20830
BPM:120.0
Takt96:20829
BPM:120.0
Takt96:20835
BPM:119.9
Takt96:20830
BPM:120.0
Takt96:20829
BPM:120.0
Takt96:20833
BPM:120.0

Ideal wäre ja bei 120 BMP ein Takt96 Wert von 20833. Wie kann man die Messwerte so manipulieren das der Wert von Takt96 nicht schwankt? Wenn ich den Wert für den Takt96 fest vorgebe läuft der Sequencer sauber, ist ja auch klar, schwankt ja auch nix. Ich habe schon alles Mögliche probiert aber ich komm nicht weiter.
Kann mir da jemand helfen?

Gruß Michael
 
Diese "Schwankung" ist 1) vermutlich schon in der eingehenden Clock und 2) so minimal, dass sie vernachlässigbar ist.

Wenn ich das richtig sehe, sind das ja nur 6 Mikrosekunden maximale Abweichung, also 0.006 Millisekunden - ich würde das als "extrem tight" bezeichnen. Zum Vergleich: bei 44.1 kHz dauert ein einziges Sample mehr als 22 Mikrosekunden.
 
Hallo

hab mal ne einfache Frage.

Wie kann ich beim programieren eine Zeile kürzen ? ( Zum drucken ist zu lang). /N ???

Beispiel Ist:

Takt96_ReverenceMAX = float(Minute)/float(float(BPM_Reverence)-float(1.0))/float(24)*float(100)+float(0.5);

Soll:

Takt96_ReverenceMAX = float(Minute)/float(float(BPM_Reverence)
-float(1.0))/float(24)*float(100)+float(0.5);

Wie schreibe ich das richtig ? So das die Formel noch funktioniert ?

Gruß Michael
 
Max schrieb:
Diese "Schwankung" ist 1) vermutlich schon in der eingehenden Clock und 2) so minimal, dass sie vernachlässigbar ist.

Wenn ich das richtig sehe, sind das ja nur 6 Mikrosekunden maximale Abweichung, also 0.006 Millisekunden - ich würde das als "extrem tight" bezeichnen. Zum Vergleich: bei 44.1 kHz dauert ein einziges Sample mehr als 22 Mikrosekunden.

Hi habe grade mal nach gerechnet sind das bei 44,1 kHz nicht

44,1 kHz
0,002267574 ms Millisekunden
2,267573696 µs Mikosekunden

?
Gruss Michael
 
Zeilenumbrüche sind kein Problem im Code - im Gegenteil, bei dieser Formel bietet es sich an, das - schön logisch sortiert - über mehrere Zeilen zu verteilen. Vielleicht noch die eine oder andere explizite Klammer dazu, und schon ist es lesbarer Code...

(und ja, du bist um eine Stelle verrutscht)
 
Max schrieb:
Zeilenumbrüche sind kein Problem im Code - im Gegenteil, bei dieser Formel bietet es sich an, das - schön logisch sortiert - über mehrere Zeilen zu verteilen. Vielleicht noch die eine oder andere explizite Klammer dazu, und schon ist es lesbarer Code...

(und ja, du bist um eine Stelle verrutscht)

Hi wie mache ich den Zeilenumbruch den ? Kannst du bitte mal ein Beispiel machen! Ich weiß nicht wie !
 
Ich bin mir nicht sicher, ob wir vom Selben reden, aber ich meinte das in etwa so (auf die Schnelle - keine Garantie auf korrekte Klammerung!):

Code:
Takt96_ReverenceMAX =
  (float(Minute)/float(float(BPM_Reverence))
  -
  ((float(1.0))/float(24))*float(100))
  +
  float(0.5);

Ein ganz normaler Zeilenumbruch hat keine Bedeutung - erst das ";" beendet das Statement...


@Sample-Rate:

(1/44100)*1000 = 0.0226...
 
Max schrieb:
Ich bin mir nicht sicher, ob wir vom Selben reden, aber ich meinte das in etwa so (auf die Schnelle - keine Garantie auf korrekte Klammerung!):

Code:
Takt96_ReverenceMAX =
  (float(Minute)/float(float(BPM_Reverence))
  -
  ((float(1.0))/float(24))*float(100))
  +
  float(0.5);

Ein ganz normaler Zeilenumbruch hat keine Bedeutung - erst das ";" beendet das Statement...


@Sample-Rate:

(1/44100)*1000 = 0.0226...

OK da war was falsch danke also 22,67 µs

Das da oben teste ich mal , bin grade dabei den Sketch zu überarbeiten . Danke erstmal.
 
Hallo ich brauch nochmal eure Hilfe!

Ich möchte jetzt das Programm (den Loopteil) mit den MIDI Befehlen Start, Stop und Continue anhalten bzw. neu starten. Is aber nicht so einfach wie ich dachte.

Versuche grade nur eine LED bei Stop ein bzw. aus zuschalten. Aber irgendwas klapp das nicht. Die LED geht nur an aber nicht aus.

Was mache ich falsch ?
Hat da jemand eine Idea wie man das lösen könnte ?

Am RX1 wird das MIDI Signal empfangen.
Am RX1 kommen nur folgende MIDI Befehle an!

242 Song Position Ptr
248 Timing Clock
250 Start
251 Continue
252 Stop

Hier mal ein Beispiel:

Gruß Michael
 

Anhänge

  • Stop Beispiel.jpg
    Stop Beispiel.jpg
    94,4 KB · Aufrufe: 92
SEQ303 schrieb:
Hallo ich brauch nochmal eure Hilfe!

Ich möchte jetzt das Programm (den Loopteil) mit den MIDI Befehlen Start, Stop und Continue anhalten bzw. neu starten. Is aber nicht so einfach wie ich dachte.

Versuche grade nur eine LED bei Stop ein bzw. aus zuschalten. Aber irgendwas klapp das nicht. Die LED geht nur an aber nicht aus.

Was mache ich falsch ?
Hat da jemand eine Idea wie man das lösen könnte ?

Am RX1 wird das MIDI Signal empfangen.
Am RX1 kommen nur folgende MIDI Befehle an!

242 Song Position Ptr
248 Timing Clock
250 Start
251 Continue
252 Stop

Hier mal ein Beispiel:

Gruß Michael

Die Bytes werden nicht sauber gelesen !!
 
Ich vermute du hast irgendeinen Fehler im MIDI-Protokoll beim Auslesen der seriellen Schnittstelle.

Nochmal meine Empfehlung: benutz eine Arduino MIDI-Library, damit hast du keine Probleme mehr wann du welche Bytes in welcher Reihenfolge bearbeiten musst.
 
Hallo Max das mit der MIDI,h bin ich grade am lesen . Möchte es lieber ohne die MIDI.h Geschichte machen. Hast du evetuell mal lust zum Tel ? Ich würde mich freun. Wenn ja dann sende mir mal deine Festnetz Nummer über eine Private Nachricht ich ruf dich dann mal an.
 
wie wird MIDI gesendet ?

So ?

StatusByte dann Databyte1 dann Databyte2 und dann das ganze von vorn ? Ich stelle mir das wie eine Kette vor. Ist das so richtig ?

Beispiel

StatusByte Databyte1 Databyte2
248 0 0
11111000 dann 0 dann 0 und dann wider von vorn oder ?

Immer drei Bytes hinter einander oder ?
 
Hi Max ,

Ich schau mir das morgen mal an . Ich werde mal versuchen die Bytes in ein Array zu packen dann sehe ich genau was rein kommt und kann es dann auswerten . Wie macht das die MIDI.h ? habe es noch nicht genau gefunden . So schwer kann das doch nicht sein die paar Bytes sauber ab zu fangen oder .

Gruß Michael
 
Hier gibt's ein paar Beispiele wie die MIDI Library benutzt wírd: https://github.com/FortySevenEffects/arduino_midi_library/tree/master/res/Examples

Es ist nicht übermäßig schwer, das MIDI Protokoll zu implementieren, aber bis du ALLE Nachrichten korrekt ausliest, wird es definitiv seine Zeit dauern. Und du musst auch die Messages korrekt auslesen, die du selbst gar nicht brauchst - ansonsten kommt alles durcheinander, sobald eine solche Nachricht eintrifft.
 


Zurück
Oben