Senden von MIDI-Daten an einen Softsynth über ALSA

Eigene oder Gemeinschaftsprojekte, Klassen, Module, Codeschnipsel, HowTos, ...
Antworten
vuott
Foriker
Beiträge: 269
Registriert: Fr 16. Jan 2015, 01:27
Wohnort: Europäische Union - Italia
Kontaktdaten:

Senden von MIDI-Daten an einen Softsynth über ALSA

Beitrag von vuott » Mi 6. Dez 2023, 12:50

Kurze Hinweise, wie man auf möglichst einfache Weise "MIDI-Nachrichten" über A.L.S.A. an einen Softsynth senden kann.

Bekanntlich enthält ein MIDI-Datenstrom nicht - wie bei Audiodaten - Informationen, die die Eigenschaften der Schallwelle beschreiben, sondern Informationen, die von anderen Programmen verwendet werden, um einen aus einer Sammlung ausgewählten Klang abzuspielen. Diese standardisierte Sammlung von Klängen, die zu verschiedenen Musikinstrumenten gehören, meist im WAV-Format, ist in einer speziellen Datei enthalten, die Soundfont-Bank genannt wird.

Die Programme, die die in den Soundfont-Bank-Dateien (Soundfont-Bank) enthaltenen Klänge verwenden müssen, damit eine MIDI-Note zu hören ist, werden Softsynths genannt.

Das MIDI-Protokoll teilt dem Softsynth mittels einer "MIDI-Nachricht" mit, dass er einen bestimmten WAV-Soundfont (der in der Praxis einem Musikinstrument entspricht) verwenden soll, indem er ihn mit einer bestimmten Tonfrequenz und für eine bestimmte Zeitspanne modifiziert.

Daher muss ein Programm, das "MIDI-Befehle" erzeugt, die erforderlichen Daten an den Softsynth liefern. Erst dann kann die "MIDI-Nachricht" die Erzeugung des gewünschten Klangs bewirken.

Derzeit erfolgt die Kommunikation zwischen einem Programm, das MIDI-Daten sendet, und dem mitgelieferten Softsynth über das A.L.S.A.-Soundsystem, das für die Verwaltung des Sendeprotokolls, des Empfangs und des eventuellen Timings der MIDI-Daten sowie für die Beziehung zum Betriebssystem und zur mitgelieferten Audio-Hardware verantwortlich ist.

ALSA fungiert als zentraler "Server", der Funktionen und Dienste für die Programme bereitstellt, die für die Audiowiedergabe mit ihm in Verbindung stehen müssen.

Diese Programme werden daher zu "Clients" von ALSA und stehen mit diesem zentralen Audiosystem in Verbindung, um mit anderen "Clients" und dem Betriebssystem zu kommunizieren.

Die "Clients" des ALSA-"Servers" sind in der Datei "/proc/asound/seq/clients" sichtbar.

Wenn wir also ein Programm in Gambas erstellen wollen, das "MIDI-Befehle" an den Softsynth in Dotation für die Klangverwaltung sendet, muss es mit ALSA in Verbindung stehen.

Das ALSA-System besteht aus mehreren Subsystemen. Das Subsystem, das für die Verwaltung der MIDI-Daten zuständig ist, wird als ALSA-"Sequenzer" bezeichnet und ist mit der Abkürzung "seq" gekennzeichnet. Es ist daher notwendig, dieses Subsystem zu öffnen, um einerseits unser Programm in einen ALSA-"Client" zu verwandeln und andererseits die Ressourcen, die dieses Subsystem zur Verfügung stellt, nutzen zu können.

Um direkt mit ALSA und seinen Ressourcen arbeiten zu können, muss unser Gambas-Programm die externen Funktionen von ALSA mit Hilfe der Anweisung "Extern" verwenden, nachdem es die gemeinsam genutzte Bibliothek von ALSA deklariert hat, die die externen Funktionen enthält, die zum Senden von "Midi-Nachrichten" verwendet werden.

Die externe Shared Library von ALSA wird wie folgt deklariert:
Library "libasound:2.0.0"
Die externe ALSA-Funktion, die es unserem Gambas-Programm ermöglicht, ein ALSA-'Client' zu sein, ist:
snd_seq_open()
Von den vier formalen Parametern ist der erste, der ein Pointer ist und in Gambas wie folgt wiedergegeben werden muss: VarPtr(Pointer); sowie der dritte: das übergebene Argument ist eine Konstante zur Einstellung des ALSA-"Sequenzers" für die Daten im "Output".

Diese externe Funktion wird in unserem Programm wie folgt deklariert:
Private Extern snd_seq_open(handle As Pointer, name As String, streams As Integer, mode As Integer) As Integer
Nach Beendigung muss das Subsystem 'seq' geschlossen werden, um den Speicher freizugeben, der für die Handhabung der von ALSA bereitgestellten Ressourcen verwendet wird.
Das Schließen erfolgt mit der externen ALSA-Funktion, die in Gambas deklariert wurde:
Private Extern snd_seq_close(handle As Pointer) As Integer
Die Behandlung von Fehlern mit den externen Funktionen von ALSA erfolgt über die spezifische externe Funktion, die in Gambas deklariert wurde:
Private Extern snd_strerror(err As Integer) As String
Wenn wir das ALSA-Subsystem "seq" mit einem "ToggleButton" öffnen, haben wir also bisher den folgenden Code: gambas code
Private midi As Pointer
 
 
 Library "libasound:2.0.0"
 
 Private Const SND_SEQ_OPEN_OUTPUT As Integer = 1
 
 Private Extern snd_seq_open(handle As Pointer, name As String, streams As Integer, mode As Integer) As Integer
 Private Extern snd_strerror(err As Integer) As String
 Private Extern snd_seq_close(handle As Pointer) As Integer
 
 Public Sub ToggleButton1_Click()
 
  If ToggleButton1.Value Then
    Dim rit As Integer
    rit = snd_seq_open(VarPtr(midi), "default", SND_SEQ_OPEN_OUTPUT, 0)
    If rit < 0 Then Error.Raise("Errore: " & snd_strerror(rit))
  Else
    snd_seq_close(midi)
  Endif
 
 End
Wenn Sie also auf den "ToggleButton" klicken, wird unser Programm zu einem ALSA-"Client". Dies ist in der bereits erwähnten Systemdatei "/proc/asound/seq/clients" zu sehen. Wenn der sofsynth bereits gestartet wurde, erhält unser Programm die ALSA-"Client"-ID-Nummer 129.

Unser MIDI-Programm ist nun bereit, MIDI-Daten über ALSA an den Softsynth zu senden.
ALSA ist sehr streng und verlangt, dass die "MIDI-Meldung" aus verschiedenen, spezifischen Daten besteht, die in einem reservierten Speicherbereich von 28 Bytes abgelegt werden.
Jede "MIDI-Meldung" wird im ALSA-Protokoll durch ein "MIDI-Ereignis" repräsentiert, das aus dem oben erwähnten 28-Byte-Speicherbereich besteht.
Gambas bietet uns mehr als eine Möglichkeit, einen solchen zugewiesenen Speicherbereich zu erstellen. Da sich unser Beispiel auf ''MIDI-Nachrichten'' zum Ein- und Ausschalten einer Midi-Note beschränkt, können wir getrost einen Vektor vom Typ Byte[] verwenden.
Wenn wir auf einen ''Button'' klicken, werden wir nur ein paar der 28 Bytes nutzen:
Dem Null-Indexelement weisen wir eine ganze Zahl zu, die die "MIDI-Meldung" Note-ON (Einschalten der MID-Note) oder Note-OFF (Ausschalten der gespielten MIDI-Note) darstellt.
Dem Index-Element 3 weisen wir den Wert 253 zu, um zu signalisieren, dass ALSAs "MIDI-Event" direkt gesendet wird.
Dem Indexelement 14 weisen wir die Identifikationsnummer des Softsynths zu, an den ALSAs "MIDI-Event" gesendet werden soll, normalerweise 128, wie in der oben erwähnten Datei "/proc/asound/seq/clients" zu sehen ist.
Dem Indexelement 17 weisen wir eine MIDI-Notennummer zu, die gespielt oder stummgeschaltet werden soll.
Dem Indexelement 18 weisen wir einen Wert von 100 für die "Anschlagsgeschwindigkeit" zu.
Das Indexelement 16 steht für den MIDI-Kanal, der in unserem Fall auf Null (MIDI-Kanal 1) eingestellt bleibt. Wenn wir ihn auf 9 (MIDI-Kanal 10) setzen, hören wir ein Schlaginstrument.

Nachdem das ALSA-'MIDI-Event' im Vektor vom Typ Byte[] konstituiert wurde, muss es über ALSA an den Softsynth gesendet werden.
Dies geschieht in unserem Fall durch eine externe Funktion, die in unserem Programm deklariert wird:
Private Extern snd_seq_event_output_direct(handle As Pointer, ev As Byte[])
Alles ist bereit.
Nachstehend finden Sie den vollständigen Code für unser sehr einfaches und unverzichtbares Gambas-Programm: gambas code
Private midi As Pointer
 Private evento As New Byte[28]
 
 
 Library "libasound:2.0.0"
 
 Private Const SND_SEQ_OPEN_OUTPUT As Integer = 1
 Private Const SND_SEQ_QUEUE_DIRECT As Byte = 253
 Private Enum SND_SEQ_EVENT_NOTEON = 6, SND_SEQ_EVENT_NOTEOFF
 
 Private Extern snd_seq_open(handle As Pointer, name As String, streams As Integer, mode As Integer) As Integer
 Private Extern snd_strerror(err As Integer) As String
 Private Extern snd_seq_event_output_direct(handle As Pointer, ev As Byte[])
 Private Extern snd_seq_close(handle As Pointer) As Integer
 
 
 Public Sub Form_Open()
  
  Button1.Enabled = False
  
End

 
 Public Sub ToggleButton1_Click()
 
  If ToggleButton1.Value Then
    Dim rit As Integer
    rit = snd_seq_open(VarPtr(midi), "default", SND_SEQ_OPEN_OUTPUT, 0)
    If rit < 0 Then Error.Raise("Errore: " & snd_strerror(rit))
    Button1.Enabled = True
  Else
    snd_seq_close(midi)
    Button1.Enabled = False
  Endif
 
 End

Public Sub Button1_MouseDown()

  evento[0] = SND_SEQ_EVENT_NOTEON
  evento[3] = SND_SEQ_QUEUE_DIRECT
  evento[14] = 128
  evento[17] = 64
  evento[18] = 100
  
  snd_seq_event_output_direct(midi, evento)

End


Public Sub Button1_MouseUp()

  evento[0] = SND_SEQ_EVENT_NOTEOFF
  evento[3] = SND_SEQ_QUEUE_DIRECT
  evento[14] = 128
  evento[17] = 64
  evento[18] = 0
  
  snd_seq_event_output_direct(midi, evento)

End
Wenn Sie den Softsynth Fluidsynth auf Ihrem System installiert haben, dann sollte das oben beschriebene Programm automatisch eine Verbindung zu diesem Softsynth herstellen.

Wenn dies nicht der Fall ist, gehen Sie wie folgt vor:
1) Führen Sie im Terminal die folgende Zeile aus: ~$ fluidsynth reload 0 ;
2) Ohne das Terminal zu schließen, überprüfen Sie im Dienstprogramm "System Monitor", ob Fluidsynth unter den aktiven Prozessen vorhanden ist;
3) Wenn Fluidsynth unter den Prozessen vorhanden ist, prüfen Sie auch, ob es unter Nr. 128 in der Datei /proc/asound/seq/clients vorhanden ist;
4) Wenn ja - ohne das Terminal zu schließen - führen Sie das oben beschriebene Programm aus.

Wenn Sie den Fluidsynth Softsynth verwenden, müssen Sie das Fluid (R3) General MIDI SoundFont (GM) Paket auf Ihrem System installiert haben: fluid-soundfont-gm
Europaeus sum

Antworten

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 0 Gäste