ALSA e Gambas: Esempio di semplice Client Midi di Alsa in ricezione ed invio dati
Mostreremo di seguito il codice di un semplice Client Midi di Alsa capace di ricevere dati Midi (a titolo esemplificativo sono stati individuati i quattro tipi più importanti e frequenti) da un altro Client di Alsa (ad esempio da una tastiera Midi esterna) e di inviarli immediatamente ad un altro Client (ad esempio ad un softsynth per l'ascolto sonoro).
Al terzo parametro della funzione snd_seq_open(), necessaria per l'apertura del subsistema Seq di Alsa, va inserita la costante SND_SEQ_OPEN_DUPLEX per consentire il flusso di dati sia in Entrata che in Uscita.
I dati Midi ricevuti sono assegnati ad una variabile di tipo Struttura organizzata secondo lo schema tipico di un "Evento" Midi Alsa.
Dopo l'avvio del softsynth e del seguente applicativo, sarà necessario connettere via Alsa la tastiera Midi ed softsynth al presente applicativo Midi rispettivamente in entrata ed in uscita.
Library "libasound:2.0.0" Public Struct snd_seq_event_t type As Byte ' byte n° 0 flag As Byte tag As Byte queue As Byte tick_o_tv_sec As Integer tv_nsec As Integer source_client As Byte source_port As Byte dest_client As Byte dest_port As Byte channel As Byte ' byte n° 16 note As Byte velocity As Byte off_velocity As Byte parametro As Integer ' byte n° 20 valore As Integer ' byte n° 24 End Struct Private Const SND_SEQ_OPEN_DUPLEX As Integer = 3 Private Const SND_SEQ_PORT_CAP_WRITE As Integer = 2 Private Const SND_SEQ_PORT_CAP_SUBS_WRITE As Integer = 64 Private Const SND_SEQ_PORT_CAP_READ As Integer = 1 Private Const SND_SEQ_PORT_CAP_SUBS_READ As Integer = 32 Private Const SND_SEQ_PORT_TYPE_APPLICATION As Integer = 1048576 Private Const SND_SEQ_ADDRESS_UNKNOWN As Byte = 253 Private Const SND_SEQ_ADDRESS_SUBSCRIBERS As Byte = 254 Private Enum SND_SEQ_EVENT_NOTEON = 6, SND_SEQ_EVENT_NOTEOFF, SND_SEQ_EVENT_CONTROLLER = 10, SND_SEQ_EVENT_PGMCHANGE ' const char * snd_strerror(int errnum) ' Returns the message for an error code. Private Extern snd_strerror(errnum As Integer) As String ' int snd_seq_open (snd_seq_t **handle, const char *name, int streams, int mode) ' Open the ALSA sequencer. Private Extern snd_seq_open(handle As Pointer, name As String, streams As Integer, mode As Integer) As Integer ' int snd_seq_set_client_name(snd_seq_t* seq, const char* name) ' Set client name. Private Extern snd_seq_set_client_name(handle As Pointer, name As String) As Integer ' int snd_seq_client_id (snd_seq_t *seq) ' Get the client id. Private Extern snd_seq_client_id(handle As Pointer) As Integer ' int snd_seq_create_simple_port(snd_seq_t *seq, const char *name, unsigned int caps, unsigned int type) ' Create a port - simple version. Private Extern snd_seq_create_simple_port(handle As Pointer, name As String, caps As Integer, type As Integer) As Integer ' int snd_seq_event_input(snd_seq_t *handle, snd_seq_event_t **ev) ' Retrieve an event from sequencer. Private Extern snd_seq_event_input(handle As Pointer, ev As Snd_seq_event_t) As Integer ' int snd_seq_event_output_direct (snd_seq_t *handle, snd_seq_event_t *ev) ' Output an event directly to the sequencer NOT through output buffer. Private Extern snd_seq_event_output_direct(handle As Pointer, ev As Snd_seq_event_t) As Integer Public Sub Main() Dim seq As Pointer Dim idportaE, idportaU, err, id As Integer err = snd_seq_open(VarPtr(seq), "default", SND_SEQ_OPEN_DUPLEX, 0) If err < 0 Then Error.Raise("Impossibile inizializzare il subsistema 'seq' di ALSA: " & snd_strerror(err)) Print "Apertura del dispositivo 'seq' di Alsa: regolare\nNome del dispositivo 'seq': "; "'default'" err = snd_seq_set_client_name(seq, "Sequencer dimostrativo di ALSA") If err < 0 Then Error.Raise("Impossibile assegnare un nome al Client applicativo di ALSA: " & snd_strerror(err)) id = snd_seq_client_id(seq) Print "Numero identificativo del sequencer dimostrativo: "; id idportaE = snd_seq_create_simple_port(seq, "Porta di Entrata del sequencer dimostrativo di ALSA", SND_SEQ_PORT_CAP_WRITE Or SND_SEQ_PORT_CAP_SUBS_WRITE, SND_SEQ_PORT_TYPE_APPLICATION) If idportaE < 0 Then Error.Raise("Impossibile creare una porta del Client applicativo di ALSA: " & snd_strerror(err)) Print "Numero identificativo della porta di Entrata: "; idportaE idportaU = snd_seq_create_simple_port(seq, "Porta di Uscita del sequencer dimostrativo di ALSA", SND_SEQ_PORT_CAP_READ Or SND_SEQ_PORT_CAP_SUBS_READ, SND_SEQ_PORT_TYPE_APPLICATION) If idportaU < 0 Then Error.Raise("Impossibile creare una porta del Client applicativo di ALSA: " & snd_strerror(err)) Print "Numero identificativo della porta di Uscita: "; idportaU Print intercettaMessaggiMidi(seq, id, idportaU) End Public Procedure intercettaMessaggiMidi(seqiM As Pointer, idC As Integer, portEx As Integer) Dim ev As Pointer Dim midi@ As Snd_seq_event_t Do snd_seq_event_input(seqiM, VarPtr(ev)) midi@ = ev With midi@ Select Case midi@.type Case SND_SEQ_EVENT_NOTEON Print "Evento 'NoteOn' sul canale: "; .channel, .note, .velocity Case SND_SEQ_EVENT_NOTEOFF Print "Evento 'NoteOff' sul canale: "; .channel, .note, .velocity Case SND_SEQ_EVENT_CONTROLLER Print "Evento 'Control' sul canale: "; .channel, .parametro, .valore Case SND_SEQ_EVENT_PGMCHANGE Print "Evento 'Program Change' sul canale: "; .channel, .valore End Select .source_client = idC .source_port = portEx .dest_client = SND_SEQ_ADDRESS_SUBSCRIBERS .dest_port = SND_SEQ_ADDRESS_UNKNOWN End With snd_seq_event_output_direct(seqiM, midi@) Loop End