MIDI Clock mit dem Arduino empfangen und auswerten!

Dieses Thema im Forum "Lötkunst" wurde erstellt von SEQ303, 15. Oktober 2015.

  1. SEQ303

    SEQ303 -

    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 == 24:cool:
    {
    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:

  2. Max

    Max aktiviert

    Du brauchst zwei Variablen:

    CurrentTimeStamp
    LastTimeStamp

    Schleife:
    {
    CurrentTimeStamp = *Zeit messen*

    Time = CurrentTimeStamp - LastTimeStamp

    LastTimeStamp = CurrentTimeStamp
    }
    -
     
  3. SEQ303

    SEQ303 -

    Hallo Max,

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


    if (StatusByte1_IN == 24:cool:

    {
    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
     
  4. Max

    Max aktiviert

    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.
     
  5. SEQ303

    SEQ303 -

    Hi

    wie wüdest du das schreiben ? Ich bekomme immer gleiche Werte :sad:

    Gruß Michael
     
  6. Max

    Max aktiviert

    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
    }
    }
    }
     
  7. SEQ303

    SEQ303 -

    geht leider auch nicht :sad: Das Problem ist das Zweite Byte zu speichern !
     
  8. Max

    Max aktiviert

    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?
     
  9. SEQ303

    SEQ303 -

    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,24:cool:****************************************************

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

    {
    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
     
  10. Max

    Max aktiviert

    Der Fehler ist hier:

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

    SEQ303 -

    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
     
  12. Max

    Max aktiviert

  13. SEQ303

    SEQ303 -

    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
     
  14. Max

    Max aktiviert

    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.
     
  15. SEQ303

    SEQ303 -

    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
     
  16. SEQ303

    SEQ303 -

    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
     
  17. Du bist eine Stelle verrutscht, oder?
     
  18. Max

    Max aktiviert

    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)
     
  19. SEQ303

    SEQ303 -

    Hi wie mache ich den Zeilenumbruch den ? Kannst du bitte mal ein Beispiel machen! Ich weiß nicht wie !
     
  20. SEQ303

    SEQ303 -

    wo genau ? müsste eigentlich Passen mit den 44,1 khz oder ?
     
  21. Max

    Max aktiviert

    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...
     
  22. SEQ303

    SEQ303 -

    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.
     
  23. SEQ303

    SEQ303 -

    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:

  24. SEQ303

    SEQ303 -

    Die Bytes werden nicht sauber gelesen !!
     
  25. Max

    Max aktiviert

    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.
     
  26. SEQ303

    SEQ303 -

    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.
     
  27. SEQ303

    SEQ303 -

    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 ?
     
  28. Max

    Max aktiviert

    Es sind nicht immer genau 3 Bytes, es können auch 1 oder 2 sein (oder auch mehr bei SysEx)

    Siehe hier: http://www.midi.org/techspecs/midimessages.php

    Oder du freundest dich halt doch mit der Library an. Versuch doch mal die Examples bei dir zum Laufen zu bringen...
     
  29. SEQ303

    SEQ303 -

    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
     
  30. Max

    Max aktiviert

    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.
     

Diese Seite empfehlen