In diesem Abschnitt werden wir lernen einen MIDI-Controller anzuschließen, um Ereignisse an Sonic Pi zu senden, mit denen unsere Synths und Klänge steuern. Besorge dir einen MIDI-Controller, wie z. B. ein Keyboard oder eine Controller-Oberfläche und lass uns handgreiflich werden!
Um Informationen von einem externen MIDI-Gerät in Sonic Pi zu bekommen müssen wir es zunächst an unseren Computer anschließen. Normalerweise wird das mittels einer USB-Verbindung sein, ältere Geräte hingegen haben einen 5-Pol-DIN-Stecker für den du Hardwareunterstützung in deinem Computer brauchst (manche Audio-Interfaces bzw. Soundkarten haben MIDI-DIN-Stecker). Nachdem du dein Gerät angeschlossen hast, starte Sonic Pi und wirf einen Blick auf den I/O-Bereich in den Einstellungen. Du solltest dein Gerät dort aufgelistet finden. Falls nicht, klicke auf ‘MIDI zurücksetzen’ und sieh nach, ob es auftaucht. Wenn du immer noch keinen Eintrag in der Liste findest, ist der nächste Schritt zu versuchen in der MIDI-Konfiguration deines Betriebssystems nachzusehen, um zu sehen ob dein Gerät erkannt wird. Wenn das alles fehlschlägt, kannst du gerne in unserem freundlichen öffentlichen Forum Fragen dazu stellen: https://in-thread.sonic-pi.net
Sobald dein Gerät verbunden ist, wird Sonic Pi automatisch Ereignisse empfangen. Du kannst das selbst nachprüfen indem du dein MIDI-Gerät bedienst und auf das Cue-Protokoll unten rechts im Anwendungsfenster unterhalb des Protokolls schaust (falls es nicht sichtbar ist, gehe zu Einstellungen->Editor->Ein-/Ausblenden und aktiviere ‘Zeige Cue-Protokoll’). Du wirst einen Datenstrom von Ereignissen sehen wie:
/midi:nanokey2_keyboard:0:1/note_off [55, 64]
/midi:nanokey2_keyboard:0:1/note_on [53, 102]
/midi:nanokey2_keyboard:0:1/note_off [57, 64]
/midi:nanokey2_keyboard:0:1/note_off [53, 64]
/midi:nanokey2_keyboard:0:1/note_on [57, 87]
/midi:nanokey2_keyboard:0:1/note_on [55, 81]
/midi:nanokey2_keyboard:0:1/note_on [53, 96]
/midi:nanokey2_keyboard:0:1/note_off [55, 64]
Wenn du einen Strom von Signalen wie diesen siehst, hast du dein MIDI-Gerät erfolgreich verbunden. Glückwunsch, lass uns schauen, was wir damit machen können!
Diese Ereignisse sind in zwei Abschnitte unterteilt. Als Erstes steht da der Name des Ereignisses, wie z. B. /midi:nanokey2_keyboard:0:1/note_on
und zum Zweiten gibt es die Werte des Ereignisses wie z. B. [18, 62]
. Interessanterweise sind das die zwei Dinge, die wir brauchen, um Ereignisse in Time-State zu speichern. Sonic Pi fügt eingehende MIDI-Ereignisse automatisch in Time-State ein. Das bedeutet du kannst auf den letzten MIDI -Wert mit get
zugreifen, und auch mit sync
auf den nächsten MIDI-Wert warten - dabei kannst du alles anwenden, was wir in Abschnitt 10 dieses Tutorials gelernt haben.
Nachdem wir jetzt ein MIDI-Gerät verbunden haben, dessen Ereignisse im Cue-Protokoll sehen konnten und wissen, dass unsere Kenntnisse über Time-State alles sind, was wir zum Arbeiten mit Ereignissen brauchen - können wir jetzt anfangen Spaß zu haben. Lass uns ein einfaches MIDI-Piano bauen:
live_loop :midi_piano do
note, velocity = sync "/midi:nanokey2_keyboard:0:1/note_on"
synth :piano, note: note
end
Es passieren einige Dinge im obigen Code, einschließlich einiger Probleme. Zunächst haben wir da einen einfachen live_loop
, der den Code zwischen dem do
/end
Block unendlich wiederholt. Das wurde im Abschnitt 9.2 behandelt. Danach rufen wir sync
auf, um auf das nächste Time-State-Ereignis zu warten. Wir benutzen eine Zeichenkette, die das MIDI-Signal repräsentiert, nach dem wir suchen (es ist dasselbe, das uns im Cue-Protokoll angezeigt wurde). Achte darauf, wie dir Sonic Pi die vollständige Zeichenkette mit seinem Autovervollständigungssystem erstellt, sodass du sie nicht komplett von Hand eintippen musst. Im Protokoll haben wir gesehen, dass es zwei Werte für jedes MIDI-Note-on-Ereignis gab, also weisen wir das Ergebnis zwei separaten Variablen note
und velocity
zu. Schließlich triggern wir den :piano
-Synth und übergeben unsere Note.
Jetzt probiere du es. Gib den obigen Programm-Code ein, ersetzte in sync den Schlüssel mit einer Zeichenkette, die zu deinem spezifischen MIDI-Gerät passt und klicke auf Ausführen. Hey, und schon hast du ein funktionierendes Klavier! Wahrscheinlich fallen dir jedoch ein paar Probleme auf: Unabhängig davon wie stark du die Tasten anschlägst, klingen die Noten immer gleich laut. Das lässt sich leicht dadurch beheben, dass wir den MIDI-Wert für Velocity (Anschlagstärke) nutzen und zu Lautstärke wandeln. Da MIDI einen Wertebereich von 0-127 hat, müssen wir diese Zahl in einen Wert zwischen 0->1 zu konvertieren, indem wir sie einfach durch 127 teilen:
live_loop :midi_piano do
note, velocity = sync "/midi:nanokey2_keyboard:0:1/note_on"
synth :piano, note: note, amp: velocity / 127.0
end
Aktualisiere deinen Code und klicke nochmals auf Ausführen. Jetzt wird der Anschlagstärke des Keyboards berücksichtigt. Als nächstes lass uns diese nervige Pause loswerden.
Bevor wir die Pause entfernen können, müssen wir herausfinden, warum sie da ist. Um alle Synths und Effekte auf einer Vielzahl von unterschiedlich starken CPUs gut getaktet zu halten, setzt Sonic Pi Audio-Ereignisse standardmäßig 0,5 Sekunden im Voraus an. (Beachte, dass diese zusätzliche Latenz über die Funktionen set_sched_ahead_time!
und use_sched_ahead_time
konfiguriert werden kann). Diese 0,5 Sekunden Latenz werden zu unseren :piano
Synth-Triggern hinzugefügt, so wie bei allen Synths, die von Sonic Pi getriggert werden. Typischerweise wollen wir diese hinzugefügte Latenz, weil sie bedeutet, dass alle Synths gut getaktet laufen werden. Das ergibt jedoch nur Sinn bei Synths, die von Programm-Code durch play
und sleep
getriggert werden. In diesem Fall triggern wir den :piano
-Synth jedoch tatsächlich mit unserem externen MIDI-Gerät und möchten daher nicht, dass Sonic Pi das Timing für uns steuert. Wir können diese Latenz mit dem Befehl use_real_time
ausschalten, der für den aktuellen Thread die Latenz ausschaltet. Das bedeutet, dass du den Echtzeitmodus use_real_time
für Live-Loops verwenden kannst, die ihr Timing mittels sync
mit externen Geräten synchronisieren, und für alle anderen Live-Loops die normale Latenz behalten kannst. Lass mal sehen:
live_loop :midi_piano do
use_real_time
note, velocity = sync "/midi:nanokey2_keyboard:0:1/note_on"
synth :piano, note: note, amp: velocity / 127.0
end
Passe deinen Code entsprechend dem obigen Code an und klicke nochmals auf Ausführen. Jetzt haben wir ein Piano mit niedriger Latenz und variabler Anschlagstärke mit nur 5 Zeilen programmiert. Wenn das nicht einfach war!
Schließlich, da unsere MIDI-Ereignisse direkt in den Time-State gehen, können wir die get
-Funktion benutzen, um den letzten gesehenen Wert zu erhalten. Dies blockiert nicht den aktuellen Thread und gibt nil
zurück, wenn es keinen Wert gibt (was du überschreiben kannst, indem du einen Standardwert übergibst - sieh dir dafür die Dokumentation für get
an). Erinnere dich, dass du get
in jedem Thread und zu jeder Zeit aufrufen kannst, um den letzten passenden Time-State-Wert zu erhalten. Du kannst sogar time_warp
benutzen, um in der Zeit zurück zu springen und mit get
vergangene Events angezeigt zu bekommen …
Das eigentlich Aufregende ist, dass du dieselben Code-Strukturen verwenden kannst, um mittels sync
und get
MIDI-Informationen mit jedem beliebigen MIDI-Gerät zu synchronisieren und du mit diesen Werten tun kannst, was du willst. Jetzt kannst du entscheiden, was dein MIDI-Gerät tun wird!