Nur OnTopic Arduino / ATMega Programmierung

Und dann auch gleich meine erste Frage:

Kleiner Test-Code um MIDI transponierung zu testen:
Code:
#include <MIDI.h>
MIDI_CREATE_DEFAULT_INSTANCE();
#define CHANNEL 1
int noteIn = 36;
int note = 0;

void setup() {
   MIDI.begin(CHANNEL);
   MIDI.turnThruOff();
   // evenutelle Notenhänger beenden
   for (note = 0; note < 127 ; note++) {
      MIDI.sendNoteOff(note,127,CHANNEL);
   }
}

void loop() {
   if (MIDI.read()) {                    
      byte type = MIDI.getType();
      if (type == midi::NoteOn) {
         noteIn = MIDI.getData1();
      }
   }
   note = 12 + noteIn;
   MIDI.sendNoteOn(note,127,CHANNEL);
   delay(200);
   MIDI.sendNoteOff(note,127,CHANNEL);
   delay(200);
}
Ich gebe also einfach eine Note aus die sich aus dem Notenwert 12 und einem Transposewert "noteIn" zusammensetzt. Transpose ist im default auf 36. Die Note wird auch gespielt.

Über Noten am MIDI-In sollte jetzt [/FONT]einfach die eine Note transponiert werden können. Das passiert aber nicht, bzw passiert irgendwann und ich kann keinen Zusammenhang mit einem Tastendruck feststellen. Was ist da faul?
 
Als Erstes würde ich mal den Outputkram in den zweiten if-Block ziehen, also nur etwas Ausgeben, wenn auch etwas reingekommen ist..
Im zweiten Step würde ich nicht nur einfache delays verwenden, sondern NoreOn senden wenn NoterOn empfangen wurde und NoteOff senden, wenn Noteoff empafngen wurde.
 
Auch fraglich: Die 128 Note offs werden auch einfach alle schlagartig übergeben. Ist der Buffer da überhaupt groß genug?
 
Zweite Idee: Beißt sich da ggf. das Interrupt-Handling von Delay() und der Abarbeitung des MIDI Buffers? Müsste man sich mal anschauen, wie das genau implementiert ist.
 
Als Erstes würde ich mal den Outputkram in den zweiten if-Block ziehen, also nur etwas Ausgeben, wenn auch etwas reingekommen ist..
Im zweiten Step würde ich nicht nur einfache delays verwenden, sondern NoreOn senden wenn NoterOn empfangen wurde und NoteOff senden, wenn Noteoff empafngen wurde.
Warum? Der NoteOn der transponierenden Daten hat ja nichts mit dem Rhythmus der Sequenz zu tun.
 
Auch fraglich: Die 128 Note offs werden auch einfach alle schlagartig übergeben. Ist der Buffer da überhaupt groß genug?
Das funktioniert. Ist ja auch nur im setup. Das ganze stammt aus einem Nachbau des JX3P sequencers, der eigentlich komplett funktioniert. Nur Transpose tut quasi nix.

Zweite Idee: Beißt sich da ggf. das Interrupt-Handling von Delay() und der Abarbeitung des MIDI Buffers? Müsste man sich mal anschauen, wie das genau implementiert ist.
Denkbar. Das kann ich mal mit variablen hochzählen ersetzen. (Wird aber vermutlich Montag werden).
 
Zuletzt bearbeitet:
Nächste Frage: Woher weiß das MIDI Objekt, dass dieses Event des Input Buffers (gibt es da einen?) jetzt gelesen wurde und es verworfen werden kann?
 
Warum baut man da ein delay von 200ms ein?
Edit: Ich nehm die Frage zurück, weil sie in dem Falle zweitrangig ist.

Über Noten am MIDI-In sollte jetzt [/FONT]einfach die eine Note transponiert werden können
Eigentlich nicht wirklich: Dein Code addiert auf die eingehende Note einen Offset von einer Oktave. Dass bedeutet, dass nicht die eingehende Note als Transpositionswert verwendet wird, sondern die transponierte Note darstellt.
Oder habe ich dein Formulierung da falsch aufgefasst?
 
Zuletzt bearbeitet:
Eigentlich nicht wirklich: Dein Code addiert auf die eingehende Note einen Offset von einer Oktave. Dass bedeutet, dass nicht die eingehende Note als Transpositionswert verwendet wird, sondern die transponierte Note darstellt.
Oder habe ich dein Formulierung da falsch aufgefasst?
Das ist eine Vereinfachung. Die konstante 12 stellt quasi die Sequenz dar (dauernd wiederholter Ton auf C1). Da mein Keyboard als tiefsten Notenwert C3 ausgibt (dec. 36) sollte bei einer Transponierung mindestens ein C4 (dec. 48) rauskommen. In Realitas wird dann da "eigentlicheSequenzNote + noteIn - 36" stehen, aber das wäre im Beispiel verwirrend.

Warum baut man da ein delay von 200ms ein?
Edit: Ich nehm die Frage zurück, weil sie in dem Falle zweitrangig ist.

Die ersten 200ms zwischen NoteOn und NoteOff sind die Notendauer, die 200ms nach dem NoteOff sind die Abstände zwischen den Noten. Das ist jetzt nur ein Workaround. Im späteren Code wird das in Abhängigkeit von den Clockticks gemacht.
 
Zuletzt bearbeitet:
Da es hier ja um Die Programmierung allgemein gehen soll, möchte ich den Hinweis von 2bit hier aufgreifen:
Ich kenne mich zwar mit Arduino nicht so aus, habe aber mit ATmegas und GCC die Erfahrung gemacht, dass es sinnvoll ist, die Datentypen immer explizit anzugeben. Also eben uint8_t, uint16_t usw.
Ich versuche auch soweit möglich globale Variablen zu vermeiden und verwende wann immer möglich statics in Funktionen. Ob der Arduino Compiler dieselben Scopes auf Variablen anwendet wie GCC weiss ich allerdings nicht.
 
Zweite Idee: Beißt sich da ggf. das Interrupt-Handling von Delay() und der Abarbeitung des MIDI Buffers? Müsste man sich mal anschauen, wie das genau implementiert ist.
Treffer! Die Verwendung von delay war es.
Ich habe meine Test-Notenauslösung jetzt einfach so gemacht:
trigger = millis() % 400;
if (trigger == 0) MIDI.sendNoteOn(noteIn,127,CHANNEL);
if (trigger == 200) MIDI.sendNoteOff(noteIn,127,CHANNEL);
 
Ich hab mal wieder eine Frage zur MIDI-Programmierung am Arduino

Ich will MIDI-Informationen auf einem Kanal empfangen und auf einem anderen Kanal ausgeben. Dabei soll nach Datentypen unterschieden werden.
Das funktioniert wunderbar zB mit NoteOn/Off oder ControlChange, aber ich finde nichts wie ich Pitchbend-Daten kriege, bzw was ich ausgeben muss. Hier der Code für ControlChange:
Code:
#include <MIDI.h>
MIDI_CREATE_DEFAULT_INSTANCE();
uint8_t outchannel=1;
uint8_t inchannel=2;

void setup() {
   MIDI.begin();
   MIDI.setThruFilterMode(1);
   MIDI.turnThruOff();
}

void loop() {
      MIDI.read();
      byte type=MIDI.getType();
      if (MIDI.getChannel() == inchannel) {                                           //ICH NEHMEN NUR DATEN MIT inchannel
         switch (type) {
            case midi::ControlChange:
                MIDI.sendControlChange(MIDI.getData1(),MIDI.getData2(),outchannel);   //ICH SENDE DIESE DATEN MIT outchannel
            break;
           /* ... und vieles mehr ... */
         }
      }
}

Für die BenderDaten muss es doch auch so was geben. Nur finde ich keine Info für das, was ich statt dem "WAS MUSS HIER HIN?" einsetzen müsste.

Code:
case midi::PitchBend:
   MIDI.sendPitchBend(MIDI.get<WAS MUSS HIER HIN?>(),outchannel);
break;
Ich habe was mit MIDI.setHandle... gefunden, aber 1.) verstehe ich nicht, was ein Handle genau macht, 2.) funktionieren die gefundenen Beispiele nur, wenn Eingangskanal und Ausgangskanal identisch sind.

EDIT: aus Versehen im zweiten Beispiel ProgramChange statt PitchBend geschrieben
 
Zuletzt bearbeitet:
Mit Kollege @inorx zusammen habe ich die Lösung gefunden:

MIDI.sendPitchBend( (((MIDI.getData2() * 128) + MIDI.getData1() ) + 8192),outchannel);
 
Zuletzt bearbeitet:
Ich habe was mit MIDI.setHandle... gefunden, aber 1.) verstehe ich nicht, was ein Handle genau macht
Im Allgemeinen ist ein Handle die Referenz auf ein Device. Daher ist das evtl. dafür gedacht, wenn ein Controller mehr als einen UART hat, sprich, man auch mehrere MIDI Ports verwenden könnte. Ist aber nur geraten in dem Falle, da ich mit der Arduino API noch nicht vertraut bin.
 
Da hier ja richtige Profis sitzen nur eine kleine Zwischenfrage, wenn es erlaubt ist. Dann lese ich auch wieder still mit .. faszinierend!

Ich habe mir den Clocky gebastelt, aber der will nicht so recht funzen.
Clocky – SoundForce (sound-force.nl)

Ohne Chip waren alle LEDs an. Nach aufspielen der Firmware geht nichts mehr, an einigen Buchsen und Schaltern liegt aber ein Clocksignal an. Über die Lötung mache ich mir mal am wenigsten Gedanken. Mir gehts um den Chip. Auf dem oberen Bild ist der vom Soundforge Orignal unten mein verbauter ... sind die baugleich?
 

Anhänge

  • clocky.jpg
    clocky.jpg
    977,6 KB · Aufrufe: 16
Ich hab mal wieder ein Programmier-Frage:

Im Beispiel-Code unten habe ich zwei Zustände
setMidiChannel == 0
oder
setMidiChannel == 1
Die beiden Zustände werden durch Led1 angezeigt

Wenn ich mit der Variablen "keyPressOrderLast" arbeite, dann kann ich zwischen den beiden Zuständen hin und her schalten.
Wenn ich eine MIDI-Cc #76 Message sende, sehe ich Anhand von Led2, dass die MIDI-Message korrekt erkannt wurde, aber das setMidiChannel=0 wird nicht ausgeführt. Das Programm bleibt konstant im Status setMidiChannel == 1

Warum?


#include <MIDI.h> MIDI_CREATE_DEFAULT_INSTANCE(); uint8_t setMidiChannel; […] void setup() { MIDI.begin(); […] digitalWrite(led1Pin, HIGH); setMidiChannel = 0; } void loop() { if ( setMidiChannel == 0 ) { digitalWrite(led1Pin, HIGH); } if ( setMidiChannel == 1 ) { digitalWrite(led1Pin, LOW); } […keyPressOrderLast wird hier gesetzt…] if (keyPressOrderLast == "Pp" ) { keyPressOrderLast = ""; setMidiChannel = 1; } if ( setMidiChannel == 1 ) if (keyPressOrderLast == "Ss" ) { setMidiChannel = 0; keyPressOrderLast = ""; // DAS FUNKTIONIERT } if (MIDI.read()) { byte type = MIDI.getType(); if (type == midi::ControlChange) { if (MIDI.getData1() == 76) { value[6] = MIDI.getData2(); channel = value[6]; digitalWrite(led2Pin, HIGH); delay(100) digitalWrite(led2Pin, LOW); setMidiChannel = 0; // DAS FUNKTIONIERT NICHT } } } } }
 
Ich hänge mich mal kurz rein und habe frevelhafterweise Chat gpt den Code gegeben. Dieser korrigierte ein Semikolon hinter dem delay(100).
 
It seems like you have a programming issue in your code. The structure of your if statements might be causing unexpected behavior. Let's try to simplify and organize the code:

```cpp
#include <MIDI.h>
MIDI_CREATE_DEFAULT_INSTANCE();
uint8_t setMidiChannel;

void setup() {
MIDI.begin();
digitalWrite(led1Pin, HIGH);
setMidiChannel = 0;
}

void loop() {
if (setMidiChannel == 0) {
digitalWrite(led1Pin, HIGH);
} else {
digitalWrite(led1Pin, LOW);
}

// ... keyPressOrderLast is set here ...

if (keyPressOrderLast == "Pp") {
keyPressOrderLast = "";
setMidiChannel = 1;
}

if (setMidiChannel == 1 && keyPressOrderLast == "Ss") {
setMidiChannel = 0;
keyPressOrderLast = "";
// This works as expected
}

if (MIDI.read()) {
byte type = MIDI.getType();
if (type == midi::ControlChange) {
if (MIDI.getData1() == 76) {
value[6] = MIDI.getData2();
channel = value[6];
digitalWrite(led2Pin, HIGH);
delay(100);
digitalWrite(led2Pin, LOW);
setMidiChannel = 0;
// Now this should work as intended
}
}
}
}
```

By organizing the code and fixing potential syntax errors (e.g., missing semicolon after `delay(100)`), the logic should be clearer. Make sure to adjust any pin assignments or variable types as needed for your specific setup. If the issue persists, you may want to check if other parts of your code are affecting the behavior.
 
Danke.

Das Semikolon war nur Tippfehler beim Schreiben des Beispiel-Codes hier fürs Forum. Der tatsächliche (viel umfangreichere) Code enthält das Semikolon; sonst würde der Code nicht kompilieren und an den Arduino übertragen werden.
 
Gerade eben wollte ich schreiben, dass da ja schon der Compiler meckern würde, aber da war @fanwander schneller.
 
@fanwander also auf den ersten Blick sehe ich jetzt nicht, warum
C++:
setMidiChannel = 0;
nicht greifen sollte.
Ich frage mich allerdings, was hier genau passiert
C++:
// ... keyPressOrderLast is set here ...
Das liegt ja innerhalb des Loop und wenn da keyPressOrderLast wieder auf "Pp" gesetzt wird, wird anschließend ja auch wieder
C++:
setMidiChannel = 1;
ausgeführt.
 
Sehe es auch so wie MacroDX, falls keyPressOrderLast auf "Pp" gesetzt wird, ist setMidiChannel nur einen einzigen Durchgang des Loops "0" und so ein Aufblitzen der LED1 sieht man nicht (falls Du ein Oszilloskop hast, könntest du das aber sehen).
Eine andere Möglichkeit, warum setMidiChannel sich nicht auf "0" setzen lassen sollte, sehe ich nicht.
 
Zuletzt bearbeitet:
Nee, das alles funktioniert einwandfrei. Ich mach ja keyPressOrderLast immer wieder leer.

Drüben auf der diymeeting-Mailliste kam der Vorschlag, das MIDI.read() außerhalb der Bedingung zu machen. Und damit klappt es.

Verständlich ist es aber trotzdem nicht, weil das
digitalWrite(led2Pin, HIGH);
delay(100);
digitalWrite(led2Pin, LOW);

ja eindeutig funktioniert.
 
Wow, okay. Das ist sehr unerwartet als Lösung.
Kannst Du mal ausprobieren, ob deine ursprüngliche Lösung funktioniert, wenn Du setMidiChannel auf "volatile" setzt?

Code:
volatile uint8_t setMidiChannel;

Das zeigt dem Compiler üblicherweise an, dass das Ding jederzeit geändert werden kann, auch ohne "ersichtlichen" Code-Zugriff (z.B. durch eine Interrupt Service Routine). (und beeinflusst damit, was der Compiler in Sachen Optimierung machen darf und nicht machen darf)

Eigentlich verwendest du hier ja gar keinen Interrupt, aber interessant wäre es schon, ob das die Geschichte ändert. Offenbar baut der Compiler ja was ganz anderes zusammen, wenn Du MIDI.read() rausziehst.
 


News

Zurück
Oben