Brevi note su come inviare nella maniera più semplice possibile "
Messaggi MIDI" a un softsynth via A.L.S.A..
Come è noto, un flusso di dati MIDI non contiene - come è per quello di tipo audio - le informazioni che descrivono le caratteristiche dell'onda sonora, bensì informazioni che dovranno essere utilizzate da altri programmi per eseguire un suono individuato fra una collezione. Questa collezione standardizzata di suoni appartenenti a vari strumenti musicali, per lo più di formato WAV, è contenuta in un apposito file, chiamato soundfont bank.
Il programma che deve usare i suoni, contenuti nei file del banco di fonti sonori (soundfont bank), affinché una nota MIDI possa essere udita, sono chiamati softsynth.
Il protocollo MIDI mediante un "Messaggio MIDI" informa il softsynth di utilizzare un certo font sonoro WAV (corrispondente in pratica a uno strumento musicale) modificandolo a una certa frequenza sonora e per un determinato periodo di tempo.
Pertanto, un programma, che produce "Messaggi MIDI", deve far recapitare i dati necessari al softsynth. Solo così il "Messaggio MIDI" potrà procurare la produzione del suono richiesto.
Attualmente la comunicazione fra un programma, che invia dati MIDI e il softsynth in dotazione avviene tramite il sistema sonoro A.L.S.A. che si incarica di gestire il protocollo di trasmissione, ricevimento ed eventuale temporizzazione dei dati MIDI, nonché il rapporto con il sistema operativo e l'hardware audio in dotazione.
ALSA si comporta da "Server" centrale che fornisce funzionalità e servizi ai programmi tenuti a rapportarsi con esso per la riproduzione audio.
Tale programmi, pertanto, diventano "Client" di ALSA, e si rapportano con tale sistema centrale sonoro per poter comunicare con altri "Client" e con il sistema operativo.
I "Client" del "Server" ALSA sono visibili consultando il file "/proc/asound/seq/clients".
Se dunque vogliamo in Gambas realizzare un qualsiasi programma che invii "Messaggi MIDI" al softsynth in dotzione per la loro gestione sonora, esso dovrà relazionarsi con ALSA.
Il sistema ALSA è composto da alcuni sub-sistemi. Il sub-sistema deputato a gestire i dati MIDI è chiamato "sequencer" di ALSA e identificato con l'abbreviativo "seq". Pertanto bisognerà aprire tale sub-sistema per poter da un lato trasformare il nostro programma in un "client" di ALSA e dall'altro fruire delle risorse che tale sub-sistema fornisce.
Per operare direttamente con ALSA e le sue risorse, il nostro programma Gambas dovrà utilizzare le funzione esterne di ALSA mediante l'istruzione "Extern",
dopo aver comunque dichiarato la libreria condivisa di ALSA, contenente le funzioni esterne che serviranno per l'invio dei "Messaggi Midi".
La libreria condivisa esterna di ALSA sarà così dichiarata:
Library "libasound:2.0.0"
La funzione esterna di ALSA che permette di rendere il nostro programma Gambas un "Client" di ALSA è:
snd_seq_open()Dei quattro parametri formali qui rilevano in particolare il primo, che è un Puntatore di Puntatore, e che in Gambas va così riprodotto: VarPtr(Pointer); nonché il terzo: l'argomento passato sarà una Costante per l'impostazione del "sequencer" di ALSA per i dati in "Uscita".
Tale funzione esterna sarà nel nostro programma così dichiarata:
Private Extern snd_seq_open(handle As Pointer, name As String, streams As Integer, mode As Integer) As Integer
Al termine il sub-sistema "seq" va chiuso, al fine di liberare la memoria usata per la gestione delle risorse fornite da ALSA.
La chiusura va effettuata con la funzione esterna di ALSA così dichiarata in Gambas:
Private Extern snd_seq_close(handle As Pointer) As Integer
La gestione degli eventuali errori con le funzioni esterne di ALSA avverrà con la specifica funzione esterna così dichiarata in Gambas:
Private Extern snd_strerror(err As Integer) As String
Disponendo l'apertura del sub-sistema "seq" di ALSA mediante un ''ToggleButton'', avremo dunque sino ad adesso il seguente codice:
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
Dunque cliccando sul ''ToggleButton'' si renderà il nostro programma un "Client" di ALSA. Ciò può essere constatato nel predetto file di sistema "/proc/asound/seq/clients". Se è stato già lanciato il sofsynth, allora al nostro programma sarà assegnato il numero identificativo 129 di "Client" di ALSA.
Il nostro programma MIDI è ora pronto per inviare dati MIDI al softsynth via ALSA.
ALSA è molto rigoroso e richiede che il "Messaggio MIDI" sia composto da vari, specifici dati da disporre su un'area di memoria riservata, formata da 28 byte.
Ogni "Messaggio MIDI" è rappresentato nel protocollo di ALSA con un "Evento MIDI" costituito dalla predetta area di memoria allocata di 28 byte, per la quale il protocollo di ALSA prevede l'uso di una Struttura.
Gambas ci mette a disposizione più di una opzione per creare tale area di memoria riservata. Poiché il nostro esempio si limiterà ai soli "Messaggi MIDI" di accensione e spegnimento di una nota Midi, potremo usare con tranquillità un vettore di tipo Byte[].
Cliccando su un ''Button'', dei 28 byte valorizzeremo solo alcuni:
All'elemento di indice zero assegneremo un intero che rappresenta di volta in volta il "Messaggio MIDI" di Note-ON (accensione della nota MID) oppure di Note-OFF (spegnimento della nota MIDI che è in esecuzione).
All'elemento di indice 3 assegneremo il valore 253 per significare che l'invio dell'"Evento MIDI" di ALSA è diretto.
All'elemento di indice 14 assegnaremo il numero identificativo del softsynth, al quale inviare l'"Evento MIDI" di ALSA, e che solitamente risulta essere 128, come è constatabile nel predetto file "/proc/asound/seq/clients".
All'elemento di indice 17 assegnaremo un numero della nota MIDI, da eseguire o da silenziare.
All'elemento di indice 18 assegnaremo il valore 100 di "velocità di tocco".
L'elemento d'indice 16 rappresenta il canale MIDI, che resterà - nel nostro caso - prestabilito a zero (canale MIDI 1). Se lo poniamo a 9 (canale MIDI 10), udremo uno strumento a percussione.
Costituito l'"Evento MIDI" di ALSA nel vettore di tipo Byte[], è necessario inviarlo al softsynth tramite ALSA.
Ciò avviene nel nostro caso essenziale tramite un'apposita funzione esterna, così dichiarata nel nostro programma:
Private Extern snd_seq_event_output_direct(handle As Pointer, ev As Byte[])
Tutto è pronto.
Di seguito il codice completo del nostro semplicissimo ed essenziale programma Gambas:
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