Alsa e Gambas: Gestione dei dati Midi in Ricezione - Introduzione

Da Gambas-it.org - Wikipedia.

Nei capitoli precedenti abbiamo considerato l'argomento dell'Invio dei dati Midi con Gambas ad ALSA. Ora prendiamo in considerazione la gestione dei dati in Ricezione da un device esterno al nostro applicativo.

Introduzione

La situazione è la seguente:
Il dispositivo esterno invia un dato alla porta del nostro applicativo, la quale dovrà essere posta dunque in modalità Write (tenuto conto che la si deve pensare "dal punto di vista dell'altro device"). Il dato, giunto alla porta dell'applicativo, non sarà però accolto automaticamente dal programma, ma questo - per poterlo acquisire - dovrà andare a leggere la porta.
Il problema più grosso è che però l'applicativo - ovviamente - non conosce il momento esatto in cui il dato sarà scritto, cioè inviato alla sua porta; e pertanto non sa quando deve leggere la propria porta per acquisire il dato inviato.

Possiamo dire che questa è stato il problema più grosso da superare nello studio da noi compiuto per gestire la "ricezione" dei dati Midi. La soluzione più corretta sarebbe operare mediante un Callback (che in Gambas dovrebbe essere stato finalmente supportato), quando arriva un evento. La funzione Callback dovrebbe semplicemente alzare un evento (RAISE xxx), che poi esegue la lettura da ALSA e gestisce quanto ricevuto. Tale soluzione ottimale non è perseguibile, poiché ALSA purtroppo non è in grado invocare un Callback.

Senza perderci d'animo, siamo giunti al termine di un lungo studio e numerose prove ad individuare e testare positivamente, comunque, ben quattro soluzioni: [nota 1]

1) uso dei File Descriptors:
la strategia è di interrogare il file descriptor passato direttamente dalle funzioni di ALSA, ma semplicemente per avere un evento "_Read()", e leggere quindi i dati giusti ed utili degli Eventi Midi dall'apposita funzione di ALSA "snd_seq_event_input()".

2) uso della Classe Timer:
questa funzione di gambas deve provvedere a scatenare una routine, nella quale avviene la lettura della porta, per intercettare i dati degli Eventi Midi dalla funzione esterna di ALSA "snd_seq_event_input()".
L'approccio del timer non è del tutto opportuno: pur volendo impostare la Proprietà ".Delay" del Timer con valore 1 millisecondo, l'idea non è ottimale, sebbene possa comunque essere funzionante.

3) uso di un ciclo:
le istruzioni per intercettare i dati degli Eventi Midi dalla funzione esterna di ALSA "snd_seq_event_input()" sono poste all'interno di un ciclo infinito.

4) uso di un programma C come supporto:
la strategia è quella di far compiere ad un altro programma scritto in C, che funzionerebbe come demone, la funzione di ricevere i dati degli Eventi Midi dalla funzione esterna di ALSA "snd_seq_event_input()".

5) uso del file-device creato dal sistema e che rappresenta la porta virtuale di un dispositivo Midi esterno collegato al computer.
Citiamo ugualmente questa soluzione, nonostante possa essere definita extra ALSA, in quanto non prevede per la ricezione dei messaggi Midi nessuna funzione di ALSA. [nota 2]


Organizzazione dei dati di un evento Midi inviati da ALSA all'applicativo

E' bene precisare sin da ora che ALSA invia gli eventi Midi al Client ricevente con la medesima modalità con la quale li accetta. ALSA, dunque, trasmette tutti i dati comuni agli eventi Midi con l'aggiunta dei dati specifici dell'evento trasmesso secondo un ordine ed una struttura ben precisa.
Pertanto, se andassimo a vedere tutti i 28 byte (sui quali si trovano i valori che compongono i vari eventi Midi) di un NoteON (per esempio: canale 0, num. midi Nota 69, velocità 100) trasmesso da una tastiera esterna, riscontreremmo in console i seguenti valori ricevuti:

Valore ricevuto - Dato corrispondente:
6=byte n. 0: Type
0= byte n. 1: Flags
0= byte n. 2: Tag
253= byte n. 3: Queue
0= byte n. 4: Tick o Tv_sec (Integer)
0= byte n. 5: (Tick o Tv_sec)
0= byte n. 6: (Tick o Tv_sec)
0= byte n. 7: (Tick o Tv_sec)
0= byte n. 8: Tv_nsec (Integer)
0= byte n. 9: (Tv_nsec)
0= byte n. 10: (Tv_nsec)
0= byte n. 11: (Tv_nsec)
14= byte n. 12: Id_client source
0= byte n. 13: Porta_client source
128= byte n. 14: Client_dest
1= byte n. 15: Porta_dest
0= byte n. 16: Canale
60= byte n. 17: num. midi Nota
100= byte n. 18: Velocità
0= byte n. 19: Off-Velocity
0= byte n. 20: primoValore (Integer)
0= byte n. 21: (primoValore)
0= byte n. 22: (primoValore)
0= byte n. 23: (primoValore)
0= byte n. 24: secondoValore (Integer)
0= byte n. 25: (secondoValore)
0= byte n. 26: (secondoValore)
0= byte n. 27: (secondoValore)


Note

[1] Si potrebbe esperire anche un ciclo infinito, ad esempio con "DO...LOOP" oppure con "WHILE True...Wend", in mezzo al quale si porrebbe un Wait per dare modo a Gambas di fare anche altro. Però, tale seconda soluzione, pur probabilmente avendo meno latenza, impegnerebbe eccessivamente la CPU, impedendo così di fatto al programma di compiere fluidamente altre operazioni.

[2] Al riguardo vedere la seguente pagina della wiki: La gestione dei dati Midi senza l'uso delle funzioni di Alsa