|
|
(11 versioni intermedie di uno stesso utente non sono mostrate) |
Riga 1: |
Riga 1: |
− | ====Preambolo====
| + | #REDIRECT [[ALSA_e_Gambas_-_Subsistema_Seq:_introduzione]] |
− | Nella precedente [[Cosa_è_A.L.S.A.|sezione]] abbiamo visto in linea teorica la struttura generale di ALSA ed il suo rapporto con i Client, ossia con i programmi-utente che interagiscono con esso.
| |
− | | |
− | <p>Nella presente sezione, invece, affronteremo praticamente l'argomento della programmazione di ALSA con Gambas.</p>
| |
− | | |
− | <p>Metodologicamente intendiamo mostrare il rapporto fra ALSA e Gambas attraverso la costruzione di un semplice programma capace di inviare semplici dati Midi, e spiegandone adeguatamente ogni passaggio fondamentale della sua codifica.</p>
| |
− | | |
− | Innanzitutto imposteremo il nostro progetto assegnandogli una classe principale, ove inseriremo i comandi essenziali, ed una classe speciale ove scriveremo tutte le funzioni specifiche di ALSA.
| |
− | | |
− | | |
− | Nella classe principale - che chiameremo: ''FMain.class'' - scriveremo innanzitutto le informazioni per creare la classe secondaria, che chiameremo: ''CAlsa.class'' .
| |
− | <BR>Scriveremo la funzione che chiama la routine analoga in ''CAlsa.class'', contenente il nome del nostro client che sarà attribuito effettivamente con una specifica funzione di ALSA che richiameremo in ''CAlsa.class'' .
| |
− | | |
− | | |
− | Cominciamo poi a strutturare la classe che richiama le funzioni di ALSA: ''CALsa.class'' <SUP>[[[#Note|Nota 1]]]</sup>.
| |
− | Per poter utilizzare le funzioni di ALSA è necessario usare la libreria delle API di ALSA (attualmente: ''/usr/lib/x86_64-linux-gnu/libasound.so.2.0.0'') specificandola nel codice del progetto Gambas in maniera adeguata con l'istruzione "''Library'' ":
| |
− | Library "''libasound:2''"
| |
− | Tale libreria è propriamente un'interfaccia ai drivers di ALSA <SUP>[[[#Note|Nota 2]]]</sup>.
| |
− | | |
− | | |
− | Pertanto, il nostro codice comincerà ad assumere queste linee iniziali (distingueremo il codice della classe principale, e quello della classe particolare delle funzioni di ALSA):
| |
− | | |
− | In '''FMain.class''':
| |
− | Public alsa as CAlsa
| |
− |
| |
− |
| |
− | '''Public''' SUB Form_Open()
| |
− |
| |
− | <FONT Color=gray>' ''Crea un Oggetto della Classe "CAlsa":''</font>
| |
− | alsa = New CAlsa as "alsa"
| |
− |
| |
− | <FONT Color=gray>' ''Invoca la prima procedura della Classe "CAlsa":''</font>
| |
− | alsa.alsa_open("Progetto sequencer in Gambas")
| |
− |
| |
− | '''End'''
| |
− | | |
− | | |
− | In '''CAlsa.class''':
| |
− | Library "libasound:2"
| |
− | | |
− | ===Richiamare le funzioni di ALSA===
| |
− | Tutte le funzioni di ALSA <SUP>[[[#Note|Nota 3]]]</sup>, che si rendono a noi necessarie, presenti nella libreria delle API di ALSA sono esterne al sistema Gambas, e devono pertanto essere debitamente "richiamate". Per mezzo di tale chiamata esse vengono rese disponibili ed effettivamente utilizzabili nella nostra programmazione. Praticamente la chiamata avviene con una dichiarazione di una normale subroutine di Gambas mediante il termine "'''Extern'''" <SUP>[[[#Note|Nota 4]]]</sup><SUP>[[[#Note|Nota 5]]]</sup>.
| |
− | <p>Le funzioni proprie di ALSA sono degli ''handle'' <SUP>[[[#Note|Nota 6]]]</sup> che ritornano sempre un codice di errore: " ''error = snd_seq_xxx(yyy, ....)'' ", laddove la variabile "error" è un integer. Un valore di ritorno pari a zero significa che l'uso della funzione esterna ha avuto successo.</p>
| |
− | | |
− | =Il Client e le sue porte=
| |
− | | |
− | ====Linee generali====
| |
− | Nella sezione precedente abbiamo detto che il dispositivo ALSA svolge sostanzialmente due compiti: quello di ''scheduling'', pianificare l'esecuzione di processi, ossia pianificare secondo una precisa sequenza, come in un elenco, l'invio degli eventi e quello di ''dispatching'', ossia di indirizzarli all'esatta destinazione nel momento giusto. La fase della gestione reale degli eventi è lasciata ai programmi applicativi-utente (come i sequencer), i quali, in qualità di ''Client'', comunicano tali dati con il dispositivo ALSA (più precisamente con uno dei suoi sub-sistemi; per il Midi: ''seq''). Abbiamo anche detto che il nostro applicativo deve possedere le funzioni di un Client. Più precisamente una parte di esso svolgerà le funzioni di Client. Si rende pertanto necessario creare appunto un ''Client'', capace di comunicare con gli altri dispositivi esterni e certamente con il dispositivo ALSA. Per creare il nostro Client dobbiamo utilizzare particolari funzioni di ALSA, che dovranno essere preventivamente "richiamate"; attraverso con una particolare dichiarazione contenente l'istruzione "'''Extern'''".
| |
− | <p>Il nostro progetto seguirà più o meno la struttura base di ogni applicazione in ALSA:</p>
| |
− | <p>1) aprire il subsistema specifico (in questo caso "seq" del dispositivo ALSA mediante l'apposita funzione;</p>
| |
− | <p>2) impostare i parametri di detta funzione;</p>
| |
− | <p>3) ricevere e/o inviare dati Midi al dispositivo;</p>
| |
− | <p>4) chiudere il dispostivo.</p>
| |
− | | |
− | =Scrittura ed invio degli eventi Midi=
| |
− | | |
− | ====Linee generali====
| |
− | Dobbiamo innanzitutto comprendere tre questioni:
| |
− | | |
− | 1. che cosa vuole ALSA;
| |
− | | |
− | 2. con quali strumenti preparare e modificare la zona di memoria richiesta;
| |
− | | |
− | 3. l'organizzazione generale del programma.
| |
− | | |
− | | |
− | Riguardo al punto 1, ALSA vuole una zona di memoria contenente, in posizioni specifiche, i valori adatti per la preparazione e per la realizzazione dell'evento Midi. Nello specifico, ALSA vuole una zona di memoria contigua, dove nel primo byte c'è il tipo di evento, nel secondo byte i flag, dal terzo al sesto byte il timestamp e via dicendo.
| |
− | | |
− | Riguardo al punto 2, va definita una zona di memoria. Per preparare e modificare questa zona di memoria è possibile usare [[Alsa_e_Gambas:_Uso_dei_Memory_Stream|'''''Stream''''']] o [[Alsa_e_Gambas:_Uso_delle_Strutture|'''''Strutture''''']] oppure una [[Alsa_e_Gambas:_Uso_di_una_Classe_specifica|'''''Classe specifica''''']].
| |
− | | |
− | Riguardo infine al punto 3, l'organizzazione generale significa: ogni volta che viene inviato un evento ad ALSA, è necessario passargli una zona di memoria che sarà sempre la stessa, ri-scrivendola ogni volta, evitando (anche se potrebbe essere un’alternativa) di crearla al momento. Per preparare un evento, inoltre, visto che ci sono campi comuni, si userà una sola routine comune a tutti gli eventi.
| |
− | | |
− | | |
− | ==La struttura virtuale degli eventi MIDI in ALSA==
| |
− | Gli eventi Midi gestiti dal subsistema ''seq'' di ALSA sono temporizzati mediante dei ''timestamp'', e sono organizzati secondo un'apposita struttura precisa di dati: <SUP>[[[#Note|Nota 7]]]</sup>
| |
− | | |
− | <TABLE>
| |
− | <TR>
| |
− | <TD>'''Type'''</td><TD> </td><TD> un singolo byte</td><TD> </td>
| |
− | </tr>
| |
− | <TR>
| |
− | <TD>'''Flags'''</td><TD> </td><TD> un singolo byte</td><TD> </td>
| |
− | </tr>
| |
− | <TR>
| |
− | <TD>'''Tag'''</td><TD> </td><TD> un singolo byte</td><TD> </td>
| |
− | </tr>
| |
− | <TR>
| |
− | <TD>'''Queue'''</td><TD> </td><TD> un singolo byte</td><TD> </td>
| |
− | </tr>
| |
− | <TR>
| |
− | <TD>'''Time''',</td><TD> composto da:</td><TD> </td>
| |
− | </tr>
| |
− | <TR>
| |
− | <TD> </td><TD>'''Tick''' o '''Tv_sec'''</td><TD> un integer</td>
| |
− | </tr>
| |
− | <TR>
| |
− | <TD> </td><TD>'''Tv_nsec'''</td><TD> un integer</td>
| |
− | </tr>
| |
− | <TR>
| |
− | <TD>'''Source''',</td><TD> composto da:</td><TD> </td>
| |
− | </tr>
| |
− | <TR>
| |
− | <TD> </td><TD>'''Id_client'''</td><TD> un singolo byte</td>
| |
− | </tr>
| |
− | <TR>
| |
− | <TD> </td><TD>'''Porta_client'''</td><TD> un singolo byte</td>
| |
− | </tr>
| |
− | <TR>
| |
− | <TD>'''Dest''',</td><TD> composto da:</td><TD> </td>
| |
− | </tr>
| |
− | <TR>
| |
− | <TD> </td><TD>'''Client_dest'''</td><TD> un singolo byte</td>
| |
− | </tr>
| |
− | <TR>
| |
− | <TD> </td><TD>'''Porta_dest'''</td><TD> un singolo byte</td>
| |
− | </tr>
| |
− | </table>
| |
− | | |
− | Da considerare che i campi ''Time'', ''Source'' e ''Dest'' sono dati <span style= "text-decoration:underline">''strutturati''</span>, cioé contengono al loro interno altri campi. Per questi dati il campo è uno solo, ma per ragioni tecniche viene spezzato in due istruzioni. Quindi un evento in ALSA contiene 4 dati semplici + 3 strutturati "doppi" (totale sette), ma i tre strutturati vengono scritti, per ragioni tecniche, con un passaggio doppio, e quindi il totale delle scritture diventa 10.
| |
− | <p>In particolare riguardo ai membri della Struttura ALSA degli Eventi Midi: <SUP>[[[#Note|Nota 7 bis]]]</sup>:
| |
− | * '''Type''' contiene il tipo di evento;
| |
− | * '''Flags''' può contenere il ''Timestamp mode'' (''Real-Time'' o ''Tick-Time'', ''Assoluto'' o ''Relativo''), il ''Data storage type'' ed il ''scheduling priority'';
| |
− | * '''Tag''' può contenere un marcatore arbitrario;
| |
− | * '''Queue''' contiene il valore per lo ''Scheduling queue'';
| |
− | * '''Time''' contiene al suo interno due campi entrambi di un integer: il primo ed il secondo per il ''timestamp'' specificato in ''Real time''; oppure solo il primo per il ''timestamp'' specificato in ''Midi Tick'', in tal caso il secondo campo non serve e può essere posto a zero;
| |
− | * '''Source''' contiene l'indirizzo del sorgente dell'evento. E' formato da due campi: il primo l'identificativo numerico, il secondo il numero della porta;
| |
− | * '''Dest''' contiene l'indirizzo del destinatario dell'evento. E' formato da due campi: il primo l'identificativo numerico, il secondo il numero della porta.</p>
| |
− | | |
− | Complessivamente i dati generali, comuni a tutti i messaggi Midi, occupano ben 16 byte della memoria allocata:
| |
− | | |
− | | |
− | <TABLE>
| |
− | <TR>
| |
− | <TD><Font color="blue">'''Byte'''</font>:</td><TD>0</td><TD></td><TD>1</td><TD></td><TD>2</td><TD></td><TD>3</td><TD></td><TD>4 5 6 7</td><TD></td><TD>8 9 10 11</td><TD></td><TD>12</td><TD></td><TD>13</td><TD></td><TD>14</td><TD></td><TD>15</td></tr>
| |
− | <TR>
| |
− | <TD></td><TD>┴—</td><TD></td><TD>┴—</td><TD></td><TD>┴—</td><TD></td><TD>┴—</td><TD></td><TD>┴———</td><TD></td><TD>┴————</td><TD></td><TD>┴—</td><TD></td><TD>┴—</td><TD></td><TD>┴—</td><TD></td><TD>┴—</td></tr>
| |
− | <TR>
| |
− | <TD><Font color="red">'''Dati'''</font>:</td><TD>'''T'''ype</td><TD>│</td><TD>'''F'''lags</td><TD>│</td><TD>'''T'''ag</td><TD>│</td><TD>'''Q'''ueue</td><TD>│</td><TD>'''T'''imestamp Real / Tick</td><TD>│</td><TD>'''T'''imestamp Tv_nsec / 0</td><TD>│</td><TD>'''I'''d_client</td><TD>│</td><TD>'''P'''orta_client</td><TD>│</td><TD>'''C'''lient_dest</td><TD>│</td><TD>'''P'''orta_dest</td></tr>
| |
− | | |
− | </table>
| |
− | | |
− | | |
− | Dopo i precedenti dati generali comuni dovranno seguire nella zona di memoria riservata i dati specifici del particolare messaggio Midi.
| |
− | | |
− | ====Il Timestamp====
| |
− | Il ''Timestamp'' di un evento può essere specificato in:
| |
− | * ''real time'', corrisponde all'orologio, e la risoluzione è in nanosecondi;
| |
− | * ''song ticks'', corrisponde ai ''tick'' del Midi.
| |
− | <p>Il ''Timestamp'' può inoltre essere:
| |
− | * ''assoluto'', quando il tempo è determinato dal momento in cui la coda di eventi ha inizio;
| |
− | * ''relativo'', quando il tempo è determinato dal momento in cui l'evento della coda è stato inviato.</p>
| |
− | | |
− | | |
− | | |
− | ===Gli eventi ed i messaggi MIDI in particolare===
| |
− | | |
− | Come è noto, i messaggi Midi si dividono in:
| |
− | * messaggi di '''Stato''', che identificano il numero del Canale ed il tipo di evento Midi trasmesso;
| |
− | * messaggi di '''Dati''', che sono i valori relativi ai messaggi di Stato, specificandone caratteristiche modificabili qualitative e quantitative.
| |
− | | |
− | Tenendo conto qui delle finalità meramente didattiche del nostro progetto applicativo, considereremo soltanto gli eventi Midi fondamentali direttamente legati alla esecuzione musicale: i ''Channel Voice Messages''.
| |
− | | |
− | In tale circostanza l'evento Midi è organizzato da ALSA utilizzando due ''Strutture'' ed in base al tipo di Messaggio Midi. La prima ''Struttura'', come abbiamo già visto, è comune a tutti i tipi di Messaggi Midi. La seconda ''Struttura'' è diversa a seconda del Messaggio Midi, ricevuto o da inviare.
| |
− | | |
− | In particolare per ospitare i dati dei Messaggi:
| |
− | * NoteOn;
| |
− | * NoteOff;
| |
− | * Polyphonic Aftertouch;
| |
− | verrà utilizzata una ''Struttura'' che è dichiarata nel file d'intestazione di Alsa ''seq_event.h'' come segue:
| |
− | typedef struct snd_seq_ev_note {
| |
− | unsigned char channel; <FONT color=gray>' ''/**< channel number */''</font>
| |
− | unsigned char note; <FONT color=gray>' ''/**< note */''</font>
| |
− | unsigned char velocity; <FONT color=gray>' ''/**< velocity */''</font>
| |
− | unsigned char off_velocity; <FONT color=gray>' ''/**< note-off velocity; only for #SND_SEQ_EVENT_NOTE */''</font>
| |
− | unsigned int duration; <FONT color=gray>' ''/**< duration until note-off; only for #SND_SEQ_EVENT_NOTE */''</font>
| |
− | } snd_seq_ev_note_t;
| |
− | Come specificato nel relativo commento, l'ultimo membro della suddetta ''Struttura'' è preso in considerazione solo nel caso di uso dell'evento Midi ALSA ''Event_NOTE''.
| |
− | | |
− | Invece, per ospitare i dati dei restanti Messaggi:
| |
− | * Control Change;
| |
− | * Program Change;
| |
− | * Channel Aftertouch (Key Pressure);
| |
− | * Pitch Bend (Pitch Wheel);
| |
− | verrà utilizzata una ''Struttura'' che è dichiarata nel file d'intestazione di Alsa ''seq_event.h'' come segue:
| |
− | typedef struct snd_seq_ev_ctrl {
| |
− | unsigned char channel; <FONT color=gray>' ''/**< channel number */''</font>
| |
− | unsigned char unused[3]; <FONT color=gray>' ''/**< reserved */''</font>
| |
− | unsigned int param; <FONT color=gray>' ''/**< control parameter */''</font>
| |
− | signed int value; <FONT color=gray>' ''/**< control value */''</font>
| |
− | } snd_seq_ev_ctrl_t;
| |
− | | |
− | Volendo fondere le due precedenti ''Strutture'', otteniamo un'unica organizzazione dei dati i cui campi, che andranno a contenere i dati degli specifici Messaggi Midi, sono i seguenti:
| |
− | <TABLE>
| |
− | <TR>
| |
− | <TD>'''Canale'''</td><TD> </td><TD> un singolo byte</td><TD> </td>
| |
− | </tr>
| |
− | <TR>
| |
− | <TD>'''Nota'''</td><TD> </td><TD> un singolo byte</td><TD> </td>
| |
− | </tr>
| |
− | <TR>
| |
− | <TD>'''Velocity'''</td><TD> </td><TD> un singolo byte <SUP>[[[#Note|Nota 8]]]</sup></td><TD> </td>
| |
− | </tr>
| |
− | <TR>
| |
− | <TD>('''Off_Velocity''') <SUP>[[[#Note|Nota 9]]]</sup></td><TD> </td><TD>un singolo byte</td><TD></td>
| |
− | </tr>
| |
− | <TR>
| |
− | <TD>'''Param''' <SUP>[[[#Note|Nota 10]]]</sup></td><TD> </td><TD>un Integer</td><TD></td>
| |
− | </tr>
| |
− | <TR>
| |
− | <TD>'''Value'''</td><TD> </td><TD>un Integer</td><TD> </td>
| |
− | </tr>
| |
− | </table>
| |
− | | |
− | <BR>Ciascun evento Midi utilizza soltanto 2 o 3 (a seconda del messaggio) di questi dati sopra descritti, i quali nella memoria riservata occupano i rispettivi byte come appresso riportato:
| |
− | <BR>
| |
− | <BR>
| |
− | <TABLE>
| |
− | <TR>
| |
− | <TD><Font color="blue">'''Byte'''</font>:</td><TD></td><TD>16</td><TD></td><TD>17</td><TD></td><TD>18</td><TD></td><TD>19</td><TD></td><TD>20 21 22 23</td><TD></td><TD>24 25 26 27</td></tr>
| |
− | <TR>
| |
− | <TD></td><TD>(da 0 a 15 i dati comuni)...</td><TD>┴—</td><TD></td><TD>┴—</td><TD></td><TD>┴—- - - -</td><TD>-</td><TD>┴—</td><TD></td><TD>┴—————</td><TD></td><TD>┴—————</td></tr>
| |
− | <TR>
| |
− | <TD><Font color="red">'''Dati'''</font>:</td><TD></td><TD>'''C'''anale</td><TD>│</td><TD>'''N'''ota</td><TD>│</td><TD>'''V'''elocity</td><TD>-</td><TD>('''O'''ff_Velocity)</td><TD>│</td><TD>'''P'''aram</td><TD>│</td><TD>'''V'''alue</td></tr>
| |
− | | |
− | </table>
| |
− | | |
− | <P>In ALSA il tipo di evento Midi viene distinto mediante una definizione identificativa, rappresentata da una costante numerica, che dovremo scrivere come costanti all'interno di CAlsa.class, ed assegnare al campo "''type''" della struttura sopra descritta dell'evento Midi:</p>
| |
− | | |
− | (Const SND_SEQ_EVENT_NOTE As Byte = 5)
| |
− | Const SND_SEQ_EVENT_NOTEON As Byte = 6
| |
− | Const SND_SEQ_EVENT_NOTEOFF As Byte = 7
| |
− | Const SND_SEQ_EVENT_KEYPRESS As Byte = 8
| |
− | Const SND_SEQ_EVENT_CONTROLLER As Byte = 10
| |
− | Const SND_SEQ_EVENT_PGMCHANGE As Byte = 11
| |
− | Const SND_SEQ_EVENT_CHANPRESS As Byte = 12
| |
− | Const SND_SEQ_EVENT_PITCHBEND As Byte = 13
| |
− | Per vedere tramite codice Gambas il numero identificativo costante di alcuni più importanti eventi Midi, come gestiti ALSA, potremo adottare un breve applicativo, composto oltre che dal progetto principale Gambas, anche da una libreria condivisa .so, da noi appositamente scritta all'interno del medesimo progetto Gambas.
| |
− | Library "/tmp/libalsa"
| |
− |
| |
− | <FONT Color=gray>' ''void Eventi_ALSA()''
| |
− | ' ''Mostra i valori delle Costanti identificatrici degli eventi ALSA.''</font>
| |
− | Private Extern Id_Eventi_ALSA()
| |
− |
| |
− |
| |
− | '''Public''' Sub Main()
| |
− |
| |
− | Creaso()
| |
− |
| |
− | Eventi_ALSA()
| |
− |
| |
− | '''End'''
| |
− |
| |
− |
| |
− | '''Private''' Procedure Creaso()
| |
− |
| |
− | File.Save("/tmp/libalsa.c", "#include <stdio.h>\n#include <alsa/seq_event.h>\n\n" &
| |
− | "void Eventi_ALSA() {\n\n" &
| |
− | " printf(\"SND_SEQ_EVENT_NOTEON = %d\\n\", SND_SEQ_EVENT_NOTEON);\n" &
| |
− | " printf(\"SND_SEQ_EVENT_NOTEOFF = %d\\n\", SND_SEQ_EVENT_NOTEOFF);\n" &
| |
− | " printf(\"SND_SEQ_EVENT_KEYPRESS = %d\\n\", SND_SEQ_EVENT_KEYPRESS);\n" &
| |
− | " printf(\"SND_SEQ_EVENT_CONTROLLER = %d\\n\", SND_SEQ_EVENT_CONTROLLER);\n" &
| |
− | " printf(\"SND_SEQ_EVENT_PGMCHANGE = %d\\n\", SND_SEQ_EVENT_PGMCHANGE);\n" &
| |
− | " printf(\"SND_SEQ_EVENT_CHANPRESS = %d\\n\", SND_SEQ_EVENT_CHANPRESS);\n" &
| |
− | " printf(\"SND_SEQ_EVENT_PITCHBEND = %d\\n\", SND_SEQ_EVENT_PITCHBEND);\n" &
| |
− | " printf(\"SND_SEQ_EVENT_START = %d\\n\", SND_SEQ_EVENT_START);\n" &
| |
− | " printf(\"SND_SEQ_EVENT_CONTINUE = %d\\n\", SND_SEQ_EVENT_CONTINUE);\n" &
| |
− | " printf(\"SND_SEQ_EVENT_STOP = %d\\n\", SND_SEQ_EVENT_STOP);\n" &
| |
− | " printf(\"SND_SEQ_EVENT_TEMPO = %d\\n\", SND_SEQ_EVENT_TEMPO);\n" &
| |
− | " printf(\"SND_SEQ_EVENT_SENSING = %d\\n\", SND_SEQ_EVENT_SENSING);\n" &
| |
− | " printf(\"SND_SEQ_EVENT_ECHO = %d\\n\", SND_SEQ_EVENT_ECHO);\n\n}")
| |
− |
| |
− | Shell "gcc -o /tmp/libalsa.so /tmp/libalsa.c -shared -lasound -fPIC" Wait
| |
− |
| |
− | '''End'''
| |
− | | |
− | | |
− | Nel nostro progetto iniziale inseriremo per ciascun evento Midi un ''button'' per attivare dalla classe principale del nostro progetto ogni evento mediante una specifica routine da scrivere invece all'interno della classe secondaria (CAlsa.class).
| |
− | | |
− | ====Dimensione finale della zona di memoria riservata====
| |
− | | |
− | La imprescindibile zona di memoria riservata sarà dunque impegnata dalle due parti:
| |
− | * la prima, quella dei dati comuni a tutti gli eventi Midi (<Font color="#00AA00">'''16'''</font> byte complessivamente: dal byte n° 0 al byte n° 15);
| |
− | * la seconda, quella dei dati specifici del particolare singolo evento Midi (<Font color="#00AFCC">'''12'''</font> byte complessivamente: dal byte n° 16 al byte n° 27).
| |
− | | |
− | Come sappiamo, i singoli specifici eventi Midi utilizzeranno solo alcuni settori, dunque solo alcuni byte, della seconda parte della memoria allocata.
| |
− | | |
− | ===Accodare gli eventi in un buffer "intermedio"===
| |
− | | |
− | Al termine della scrittura nella zona di memoria riservata di tutti i dati necessari per definire l'evento Midi desiderato, sarà inserita la funzione di ALSA, espressa in C: ''int snd_seq_event_output(snd_seq_t * seq, snd_seq_event_t * ev)'', che pone in uscita un evento. Più precisamente questa funzione accoda un evento in un buffer intermedio. Tale funzione ritorna il numero (integer) di eventi rimanenti da far uscire, oppure un codice di errore se negativo.
| |
− | Detta funzione di ALSA dovrà essere dichiarata con '''Extern''', in questo modo:
| |
− | <FONT Color= #B22222>Private Extern snd_seq_event_output(handle As Pointer, ev As Pointer) As Integer</font>
| |
− | e nella routine sarà invocata così:
| |
− | err = snd_seq_event_output(handle, ev) <FONT Color=gray>' ''output an event</font>
| |
− |
| |
− | printerr("Evento Midi = ", err)
| |
− | | |
− | | |
− | ===Passare ad ALSA i valori presenti nella memoria allocata===
| |
− | | |
− | Al termine di ciascuna routine per l'invio dell'evento Midi desiderato viene richiamata la subroutine "''flush( )''", presente nella classe secondaria CAlsa.class, la quale contiene la funzione di ALSA, espressa in C: ''int snd_seq_drain_output(snd_seq_t * seq)'', che sversa il contenuto della zona di memoria allocata al sequencer ALSA. Tale funzione ritorna un integer uguale a zero, se tutti gli eventi sono stati inviati al sequencer, e quindi se il buffer intermedio è stato svuotato. Un valore negativo ritorna un errore.
| |
− | <BR>Più in particolare, questa funzione viene invocata dopo ogni evento; e questo assicura che l'evento venga processato in tempo, svuotando il buffer intermedio precedentemente riempito di dati dalla funzione: ''snd_seq_event_output'' <SUP>[[[#Note|Nota 11]]]</sup>, precedentemente descritta.
| |
− | | |
− | Detta funzione di ALSA dovrà essere dichiarata con '''Extern''', in questo modo:
| |
− | <FONT Color= #B22222>Private Extern snd_seq_drain_output(handle As Pointer) As Integer</font>
| |
− | Verrà scritta così in un'apposita routine:
| |
− | '''Public''' Sub flush()
| |
− | Dim err As Integer
| |
− |
| |
− | err = snd_seq_drain_output(handle) <FONT Color=gray>' ''drain output buffer to sequencer</font>
| |
− | Printerr("Flush", err)
| |
− | '''End'''
| |
− | | |
− | | |
− | | |
− | =Note=
| |
− | [1] Si suggerisce di dare un'occhiata al capitolo [[Traduzione_della_comunità_di_Gambas-it#Interfacciare_Gambas_con_librerie_esterne|Interfacciare Gambas con librerie esterne]], redatta da D. Blengino, presente nell'area "Guide della Comunità", ove sarà possibile avere le informazioni basilari necessarie per la gestione delle API di ALSA. Lì, tra l'altro, sono anche presenti esempi esplicativi della funzione Extern per la gestione di ALSA con Gambas, e l'intera codifica di una classe CAlsa.Class relativa all'esempio di una Drum Machine. Si precisa che la conoscenza della funzione Extern, relativa alle dichiarazioni esterne in Gambas, nonché delle API di ALSA (richiamate appunto dalla funzione Extern), è comunque presupposto necessario ed imprescindibile per la gestione in Gambas dei dati Midi con ALSA.
| |
− | | |
− | [2] Una libreria, come lo stesso nome indica, contiene una raccolta di subroutine da utilizzare più volte da più programmi.
| |
− | | |
− | [3] Ci sono due metodi per gestire con ALSA i dati Midi: il metodo definito "''rawmidi''" ed il metodo "''sequencer''". Il metodo ''rawmidi'', che ha a che fare con i byte Midi essenziali, è ad un livello inferiore e semplice, mentre il metodo ''sequencer'' opera a livello più alto e complesso. In questa guida ci occuperemo della gestione dei dati Midi mediante il metodo ''sequencer''.
| |
− | | |
− | [4] Una dichiarazione Extern fa riferimento all'ultima istruzione LIBRARY incontrata. Se l'ultima Library non è quella che contiene la funzione successiva alla libreria medesima, detta funzione non potrà essere richiamata dal programma. Pertanto, se si devono usare più librerie, queste o si dichiarano di volta in volta prima della funzione di riferimento (ma sempre all'esterno della routine contenente detta funzione), oppure si specificano all'interno di ogni loro dichiarazione.
| |
− | | |
− | [5] Sull'uso di ''Extern'' vedere: [[Extern:_richiamare_funzioni_esterne_a_Gambas|Extern: richiamare funzioni esterne a Gambas]].
| |
− | | |
− | [6] Un Handle (''maniglia'') rappresenta "''una '''variabile''' associata a un oggetto complesso, che lo identifica''". Possiamo dire che in modo figurato questa ''maniglia'' è la ''protuberanza'' con la quale si interagisce con l'oggetto medesimo. In gambas, per esempio, qualsiasi oggetto (form, o altro) è un ''handle'', e per creare un oggetto/handle in Gambas, come è noto, bisogna scrivere "NEW oggetto_da_creare", in ALSA invece la funzione per creare l'handle è, come sappiamo: ''variab_int = snd_seq_etc_etc(xxx, ......) as integer''.
| |
− | | |
− | [7] Vedere: https://www.alsa-project.org/alsa-doc/alsa-lib/structsnd__seq__event__t.html
| |
− | | |
− | [7 bis] Vedi anche D. Blengino: [[Un_contributo_sulle_funzioni_esterne_in_Gambas_a_cura_di_Doriano_B.#Definizione_degli_eventi_Midi_in_ALSA|Definizione degli eventi Midi in ALSA]].
| |
− | | |
− | [8] Questo tipo di dato può essere definito anche '''''Short''''', qualora non si intenda utilizzare l'evento ALSA: ''SND_SEQ_EVENT_NOTE''. Si dovrà ovviamente eliminare il successivo dato ''Off_Velocity'', poiché il tipo ''Short'' occupa 2 byte. | |
− | | |
− | [9] Questo dato è utilizzato solo con l'evento ALSA: ''SND_SEQ_EVENT_NOTE''. | |
− | | |
− | [10] Questo dato è definito "'''Duration'''" nel caso di utilizzo dell'evento ALSA: ''SND_SEQ_EVENT_NOTE''.
| |
− | | |
− | [11] Distinzione fra la funzione ''err = snd_seq_event_output(handle, ev)'' ed ''err = snd_seq_drain_output(handle)'':
| |
− | * ''err = snd_seq_event_output(handle, ev)'' --> accoda gli eventi in un buffer "intermedio". Quando tale buffer è pieno, viene svuotato automaticamente nel buffer-ALSA. Lo svuotamento di tale buffer intermedio può essere indotto anche se esso non è pieno.
| |
− | * ''err = snd_seq_drain_output(handle)'' --> svuota "a richiesta" il buffer intermedio (anche quindi se il buffer intermedio non è pieno).
| |
− | | |
− | | |
− | | |
− | =Riferimenti=
| |
− | [1] [http://www.alsa-project.org/alsa-doc/alsa-lib/group___sequencer.html Midi Sequencer]
| |
− | | |
− | [2] [http://www.alsa-project.org/alsa-doc/alsa-lib/seq.html The C library reference: Sequencer interface]
| |
− | | |
− | [3] [http://git.alsa-project.org/?p=alsa-lib.git;a=blob_plain;f=src/seq/seq.c;hb=HEAD Sequencer Interface - main file]
| |
− | | |
− | [4] [http://www.alsa-project.org/alsa-doc/alsa-lib/seq__event_8h-source.html Il file d'intestazione ''seq_event.h'']
| |