Differenze tra le versioni di "Alsa e Gambas: Invio dei dati senza connessione della porta del programma con il Softsynth"
Riga 1: | Riga 1: | ||
− | Per inviare utilmente dati Midi ad altro ''Client'' di ALSA, non è necessario connettere i due ''Client'' (l'applicativo Gambas ed il ''Softsynth'') mediante la funzione | + | Per inviare utilmente dati Midi ad altro ''Client'' di ALSA, non è necessario connettere i due ''Client'' (l'applicativo Gambas ed il ''Softsynth'') mediante la funzione esterna di ALSA "snd_seq_connect_to()", purché si presti attenzione ad alcuni elementi che integrano due casi. |
===1° caso=== | ===1° caso=== | ||
Nel primo caso il codice dovrà prevedere: | Nel primo caso il codice dovrà prevedere: | ||
− | * la presenza della funzione di ALSA '' | + | * la presenza della funzione di ALSA "snd_seq_alloc_queue()", la quale restituisce l'identificativo della coda (''queue'' )degli ''Eventi Midi ALSA'' da assegnarsi al membro "queue" della Struttura ''[https://www.alsa-project.org/alsa-doc/alsa-lib/structsnd__seq__event__t.html snd_seq_event_t]''; |
− | + | * al membro ''dest_client'' e a quello ''dest_port'' della Struttura relativa agli eventi Midi dovranno essere rispettivamente assegnati i valori dell'Id e della porta del ''Client'' (''Softsynth''), al quale l'applicativo Gambas dovrà inviare i dati Midi. | |
− | * al membro '' | + | Il dispositivo destinatario degli ''Eventi Midi ALSA'' sarà individuato, dunque, esclusivamente attraverso il suo numero identificativo e il numero della sua porta quale Client di ALSA. |
Mostriamo un esempio pratico: | Mostriamo un esempio pratico: | ||
− | + | Private Const <FONT Color=#B22222><B>CLIENT_DESTINATARIO</b></font> As Byte = 128 <FONT Color=gray>' ''Solitamente l'identificativo è 128''</font> | |
− | Private Const | + | Private Const <FONT Color=#B22222><B>PORTA_DESTINATARIO</b></font> As Byte = 0 |
− | Private Const | ||
− | Library "libasound:2" | + | Library "libasound:2.0.0" |
Public Struct snd_seq_event_t | Public Struct snd_seq_event_t | ||
Riga 21: | Riga 20: | ||
tag As Byte | tag As Byte | ||
queue As Byte | queue As Byte | ||
− | + | tick_o_tv_sec As Integer | |
− | + | tv_nsec As Integer | |
source_client As Byte | source_client As Byte | ||
source_port As Byte | source_port As Byte | ||
Riga 48: | Riga 47: | ||
' ''Returns the message for an error code.''</font> | ' ''Returns the message for an error code.''</font> | ||
Private Extern snd_strerror(err As Integer) As Pointer | Private Extern snd_strerror(err As Integer) As Pointer | ||
+ | |||
+ | <FONT Color=gray>' ''int snd_seq_client_id (snd_seq_t * seq)'' | ||
+ | ' ''Get the client id.''</font> | ||
+ | Private Extern snd_seq_client_id(seq As Pointer) As Integer | ||
<FONT Color=gray>' ''int snd_seq_create_simple_port (snd_seq_t *seq, const char *name, unsigned int caps, unsigned int type)'' | <FONT Color=gray>' ''int snd_seq_create_simple_port (snd_seq_t *seq, const char *name, unsigned int caps, unsigned int type)'' | ||
Riga 55: | Riga 58: | ||
<FONT Color=gray>' ''int snd_seq_alloc_queue (snd_seq_t *handle)'' | <FONT Color=gray>' ''int snd_seq_alloc_queue (snd_seq_t *handle)'' | ||
' ''Allocate a queue.''</font> | ' ''Allocate a queue.''</font> | ||
− | Private Extern snd_seq_alloc_queue(handle As Pointer) As Integer | + | Private Extern <FONT Color=darkorange><B>snd_seq_alloc_queue</b></font>(handle As Pointer) As Integer |
<FONT Color=gray>' ''int snd_seq_event_output (snd_seq_t *handle, snd_seq_event_t *ev) | <FONT Color=gray>' ''int snd_seq_event_output (snd_seq_t *handle, snd_seq_event_t *ev) | ||
Riga 71: | Riga 74: | ||
'''Public''' Sub Main() | '''Public''' Sub Main() | ||
− | + | ||
+ | Dim handle As Pointer | ||
+ | Dim rit As Integer | ||
Dim note As Byte[] = [60, 62, 64, 65, 67, 69, 71, 72] | Dim note As Byte[] = [60, 62, 64, 65, 67, 69, 71, 72] | ||
− | Dim | + | Dim id, porta, que, b As Byte |
Dim ev As New Snd_seq_event_t | Dim ev As New Snd_seq_event_t | ||
− | + | ||
− | + | rit = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_OUTPUT, 0) | |
− | + | If rit < 0 Then Error.Raise("Impossibile aprire il subsistema 'seq' di ALSA: " & String@(snd_strerror(rit))) | |
+ | |||
+ | id = snd_seq_client_id(handle) | ||
+ | If id < 0 Then Error.Raise("Errore: " & String@(snd_strerror(rit))) | ||
+ | |||
+ | porta = snd_seq_create_simple_port(handle, "porta_", SND_SEQ_PORT_CAP_READ, SND_SEQ_PORT_TYPE_MIDI_GENERIC Or SND_SEQ_PORT_TYPE_APPLICATION) | ||
+ | If porta < 0 Then Error.Raise("Impossibile creare la porta di uscita dati ALSA !") | ||
+ | |||
+ | <FONT Color=darkorange><B>que</b></font> = <FONT Color=darkorange><B>snd_seq_alloc_queue</b></font>(handle) | ||
+ | |||
With ev | With ev | ||
− | <FONT Color= | + | .queue = <FONT Color=darkorange><B>que</b></font> |
− | .source_client = | + | .source_client = id |
− | .source_port = | + | .source_port = porta |
− | <FONT Color=#B22222> | + | .dest_client = <FONT Color=#B22222><B>CLIENT_DESTINATARIO</b></font> |
− | <FONT Color=#B22222> | + | .dest_port = <FONT Color=#B22222><B>PORTA_DESTINATARIO</b></font> |
.channel = 0 | .channel = 0 | ||
End With | End With | ||
− | + | ||
− | program_change(ev, 48) | + | program_change(handle, ev, 48) |
− | + | ||
For b = 0 To note.Max | For b = 0 To note.Max | ||
− | Note_On(ev, note[b]) | + | Note_On(handle, ev, note[b]) |
+ | <FONT Color=gray>' ''Imposta la durata dell'esecuzione della nota.'' | ||
+ | ' ''Va precisato che l'uso della funzione "Wait" NON è un modo coerente con il protocollo di ALSA per la temporizzazione degli Eventi Midi: dato che ALSA usa a tale fine la cosiddetta "marcatura degli Eventi Midi" assegnando degli opportuni valori nel membro "flags" e in quelli del "Timestamp" della Struttura "snd_seq_event_t".'' | ||
+ | ' ''Qui si fa uso della funzione "Wait" soltanto per meri motivi didattici ed esemplificativi del funzionamento degli altri aspetti trattati in questa pagina.</font> | ||
Wait 1 | Wait 1 | ||
− | Note_Off(ev, note[b]) | + | Note_Off(handle, ev, note[b]) |
Next | Next | ||
− | + | ||
− | snd_seq_close( | + | snd_seq_close(handle) |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
'''End''' | '''End''' | ||
+ | '''Private''' Procedure program_change(p As Pointer, ev_prch As Snd_seq_event_t, strum As Integer) | ||
− | |||
− | |||
With ev_prch | With ev_prch | ||
.type = SND_SEQ_EVENT_PGMCHANGE | .type = SND_SEQ_EVENT_PGMCHANGE | ||
.value = strum | .value = strum | ||
End With | End With | ||
− | + | ||
− | InviaEvento(ev_prch) | + | InviaEvento(p, ev_prch) |
− | + | ||
'''End''' | '''End''' | ||
+ | '''Private''' Procedure Note_On(p As Pointer, ev_nton As Snd_seq_event_t, nota As Byte) | ||
− | |||
− | |||
With ev_nton | With ev_nton | ||
.type = SND_SEQ_EVENT_NOTEON | .type = SND_SEQ_EVENT_NOTEON | ||
Riga 136: | Riga 134: | ||
.velocity = 64 | .velocity = 64 | ||
End With | End With | ||
− | + | ||
− | InviaEvento(ev_nton) | + | InviaEvento(p, ev_nton) |
− | + | ||
'''End''' | '''End''' | ||
+ | '''Private''' Procedure Note_Off(p As Pointer, ev_ntoff As Snd_seq_event_t, nota As Byte) | ||
− | |||
− | |||
With ev_ntoff | With ev_ntoff | ||
.type = SND_SEQ_EVENT_NOTEOFF | .type = SND_SEQ_EVENT_NOTEOFF | ||
Riga 149: | Riga 146: | ||
.velocity = 0 | .velocity = 0 | ||
End With | End With | ||
− | + | ||
− | InviaEvento(ev_ntoff) | + | InviaEvento(p, ev_ntoff) |
− | + | ||
'''End''' | '''End''' | ||
+ | '''Private''' Procedure InviaEvento(p As Pointer, evento As Snd_seq_event_t) | ||
− | + | snd_seq_event_output(p, evento) | |
− | |||
− | snd_seq_event_output( | ||
− | |||
− | |||
+ | snd_seq_drain_output(p) | ||
+ | |||
'''End''' | '''End''' | ||
− | |||
===2° caso=== | ===2° caso=== | ||
Nel secondo caso si dovrà tenere conto di quanto segue: | Nel secondo caso si dovrà tenere conto di quanto segue: | ||
− | * viene | + | * non viene utilizzata la funzione esterna (usata invece nel caso precedente) specifica di ALSA per la creazione della porta del Client applicativo; |
− | * al membro '' | + | * al membro ''queue'' della Struttura relativa agli eventi Midi dovrà essere assegnato il valore della Costante Alsa "SND_SEQ_QUEUE_DIRECT" (253); |
− | * al membro '' | + | * al membro ''dest_id'' e a quello ''dest_port'' della Struttura relativa agli eventi Midi dovranno essere rispettivamente assegnati i valori dell'Id e della porta del ''Client'' (''Softsynth''), al quale l'applicativo Gambas dovrà inviare i dati Midi. |
Mostriamo un esempio pratico: | Mostriamo un esempio pratico: | ||
− | Library "libasound:2" | + | Private Const <FONT Color=#B22222><B>CLIENT_DESTINATARIO</b></font> As Byte = 128 <FONT Color=gray>' ''Solitamente l'identificativo è 128''</font> |
+ | Private Const <FONT Color=#B22222><B>PORTA_DESTINATARIO</b></font> As Byte = 0 | ||
+ | |||
+ | |||
+ | Library "libasound:2.0.0" | ||
Public Struct snd_seq_event_t | Public Struct snd_seq_event_t | ||
Riga 179: | Riga 178: | ||
tag As Byte | tag As Byte | ||
queue As Byte | queue As Byte | ||
− | + | tick_o_tv_sec As Integer | |
− | + | tv_nsec As Integer | |
source_client As Byte | source_client As Byte | ||
source_port As Byte | source_port As Byte | ||
Riga 192: | Riga 191: | ||
value As Integer | value As Integer | ||
End Struct | End Struct | ||
− | + | ||
Private Const SND_SEQ_OPEN_OUTPUT As Integer = 1 | Private Const SND_SEQ_OPEN_OUTPUT As Integer = 1 | ||
− | Private Const SND_SEQ_QUEUE_DIRECT As Byte = 253 | + | Private Const SND_SEQ_PORT_CAP_READ As Integer = 1 |
+ | Private Const SND_SEQ_PORT_TYPE_MIDI_GENERIC As Integer = 2 | ||
+ | Private Const <FONT Color=#006400><B>SND_SEQ_QUEUE_DIRECT</b></font> As Byte = 253 | ||
+ | Private Const SND_SEQ_PORT_TYPE_APPLICATION As Integer = 1048576 | ||
Private Enum SND_SEQ_EVENT_NOTEON = 6, SND_SEQ_EVENT_NOTEOFF, SND_SEQ_EVENT_KEYPRESS, SND_SEQ_EVENT_CONTROLLER = 10, SND_SEQ_EVENT_PGMCHANGE | Private Enum SND_SEQ_EVENT_NOTEON = 6, SND_SEQ_EVENT_NOTEOFF, SND_SEQ_EVENT_KEYPRESS, SND_SEQ_EVENT_CONTROLLER = 10, SND_SEQ_EVENT_PGMCHANGE | ||
Riga 204: | Riga 206: | ||
' ''Returns the message for an error code.''</font> | ' ''Returns the message for an error code.''</font> | ||
Private Extern snd_strerror(err As Integer) As Pointer | Private Extern snd_strerror(err As Integer) As Pointer | ||
− | + | ||
+ | <FONT Color=gray>' ''int snd_seq_client_id (snd_seq_t * seq)'' | ||
+ | ' ''Get the client id.''</font> | ||
+ | Private Extern snd_seq_client_id(seq As Pointer) As Integer | ||
+ | |||
+ | <FONT Color=gray>' ''int snd_seq_create_simple_port (snd_seq_t *seq, const char *name, unsigned int caps, unsigned int type)'' | ||
+ | ' ''Create a port - simple version.''</font> | ||
+ | Private Extern snd_seq_create_simple_port(seq As Pointer, name As String, caps As Integer, type As Integer) As Integer | ||
+ | |||
+ | <FONT Color=gray>' ''int snd_seq_alloc_queue (snd_seq_t *handle)'' | ||
+ | ' ''Allocate a queue.''</font> | ||
+ | Private Extern snd_seq_alloc_queue(handle As Pointer) As Integer | ||
+ | |||
<FONT Color=gray>' ''int snd_seq_event_output (snd_seq_t *handle, snd_seq_event_t *ev) | <FONT Color=gray>' ''int snd_seq_event_output (snd_seq_t *handle, snd_seq_event_t *ev) | ||
' ''Output an event.''</font> | ' ''Output an event.''</font> | ||
Riga 219: | Riga 233: | ||
'''Public''' Sub Main() | '''Public''' Sub Main() | ||
− | + | ||
− | Dim | + | Dim handle As Pointer |
+ | Dim rit As Integer | ||
Dim note As Byte[] = [60, 62, 64, 65, 67, 69, 71, 72] | Dim note As Byte[] = [60, 62, 64, 65, 67, 69, 71, 72] | ||
− | Dim b As Byte | + | Dim id, porta, b As Byte |
− | Dim | + | Dim ev As New Snd_seq_event_t |
− | + | ||
− | + | rit = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_OUTPUT, 0) | |
− | + | If rit < 0 Then Error.Raise("Impossibile aprire il subsistema 'seq' di ALSA: " & String@(snd_strerror(rit))) | |
− | With | + | |
− | <FONT Color=# | + | id = snd_seq_client_id(handle) |
− | <FONT Color=#B22222> | + | If id < 0 Then Error.Raise("Errore: " & String@(snd_strerror(rit))) |
− | <FONT Color=#B22222> | + | |
+ | porta = snd_seq_create_simple_port(handle, "porta_", SND_SEQ_PORT_CAP_READ, SND_SEQ_PORT_TYPE_MIDI_GENERIC Or SND_SEQ_PORT_TYPE_APPLICATION) | ||
+ | If porta < 0 Then Error.Raise("Impossibile creare la porta di uscita dati ALSA !") | ||
+ | |||
+ | With ev | ||
+ | .queue = <FONT Color=#006400><B>SND_SEQ_QUEUE_DIRECT</b></font> | ||
+ | .source_client = id | ||
+ | .source_port = porta | ||
+ | .dest_client = <FONT Color=#B22222><B>CLIENT_DESTINATARIO</b></font> | ||
+ | .dest_port = <FONT Color=#B22222><B>PORTA_DESTINATARIO</b></font> | ||
.channel = 0 | .channel = 0 | ||
End With | End With | ||
− | + | ||
− | program_change( | + | program_change(handle, ev, 48) |
− | + | ||
For b = 0 To note.Max | For b = 0 To note.Max | ||
− | Note_On( | + | Note_On(handle, ev, note[b]) |
− | + | <FONT Color=gray>' ''Imposta la durata dell'esecuzione della nota.'' | |
+ | ' ''Va precisato che l'uso della funzione "Wait" NON è un modo coerente con il protocollo di ALSA per la temporizzazione degli Eventi Midi: dato che ALSA usa a tale fine la cosiddetta "marcatura degli Eventi Midi" assegnando degli opportuni valori nel membro "flags" e in quelli del "Timestamp" della Struttura "snd_seq_event_t".'' | ||
+ | ' ''Qui si fa uso della funzione "Wait" soltanto per meri motivi didattici ed esemplificativi del funzionamento degli altri aspetti trattati in questa pagina.</font> | ||
Wait 1 | Wait 1 | ||
− | Note_Off( | + | Note_Off(handle, ev, note[b]) |
− | |||
Next | Next | ||
− | + | ||
− | snd_seq_close( | + | snd_seq_close(handle) |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
'''End''' | '''End''' | ||
+ | '''Private''' Procedure program_change(p As Pointer, ev_prch As Snd_seq_event_t, strum As Integer) | ||
− | |||
− | |||
With ev_prch | With ev_prch | ||
.type = SND_SEQ_EVENT_PGMCHANGE | .type = SND_SEQ_EVENT_PGMCHANGE | ||
.value = strum | .value = strum | ||
End With | End With | ||
− | + | ||
− | + | InviaEvento(p, ev_prch) | |
− | + | ||
− | |||
'''End''' | '''End''' | ||
+ | '''Private''' Procedure Note_On(p As Pointer, ev_nton As Snd_seq_event_t, nota As Byte) | ||
− | |||
− | |||
With ev_nton | With ev_nton | ||
.type = SND_SEQ_EVENT_NOTEON | .type = SND_SEQ_EVENT_NOTEON | ||
Riga 282: | Riga 291: | ||
.velocity = 64 | .velocity = 64 | ||
End With | End With | ||
+ | |||
+ | InviaEvento(p, ev_nton) | ||
− | |||
− | |||
− | |||
'''End''' | '''End''' | ||
+ | '''Private''' Procedure Note_Off(p As Pointer, ev_ntoff As Snd_seq_event_t, nota As Byte) | ||
− | |||
− | |||
With ev_ntoff | With ev_ntoff | ||
.type = SND_SEQ_EVENT_NOTEOFF | .type = SND_SEQ_EVENT_NOTEOFF | ||
Riga 296: | Riga 303: | ||
.velocity = 0 | .velocity = 0 | ||
End With | End With | ||
− | + | ||
− | + | InviaEvento(p, ev_ntoff) | |
− | + | ||
− | |||
'''End''' | '''End''' | ||
+ | '''Private''' Procedure InviaEvento(p As Pointer, evento As Snd_seq_event_t) | ||
− | + | snd_seq_event_output(p, evento) | |
+ | |||
+ | snd_seq_drain_output(p) | ||
− | |||
− | |||
− | |||
'''End''' | '''End''' | ||
− | ===Esempio più essenziale per invio | + | ===Esempio più essenziale per invio diretto degli Eventi Midi ALSA=== |
− | Mostriamo ora la modalità più breve ed essenziale per inviare ad altro Client di Alsa alcuni eventi Midi, e nella quale non è prevista la creazione di porte del nostro Client applicativo: | + | Mostriamo ora la modalità più breve ed essenziale per inviare ad altro Client di Alsa alcuni eventi Midi, e nella quale non è prevista la creazione di porte del nostro Client applicativo. |
− | Library "libasound:2" | + | <BR>Si utilizzerà in questo caso la funzione esterna di ALSA: "snd_seq_event_output_direct()" per invio diretto di ciaascun Evento Midi ALSA. |
+ | Private Const <FONT Color=#B22222><B>CLIENT_DESTINATARIO</b></font> As Byte = 128 <FONT Color=gray>' ''Solitamente l'identificativo è 128''</font> | ||
+ | Private Const <FONT Color=#B22222><B>PORTA_DESTINATARIO</b></font> As Byte = 0 | ||
+ | |||
+ | Library "libasound:2.0.0" | ||
Public Struct snd_seq_event_t | Public Struct snd_seq_event_t | ||
Riga 321: | Riga 331: | ||
tag As Byte | tag As Byte | ||
queue As Byte | queue As Byte | ||
− | + | tick_o_tv_sec As Integer | |
− | + | tv_nsec As Integer | |
source_client As Byte | source_client As Byte | ||
source_port As Byte | source_port As Byte | ||
Riga 336: | Riga 346: | ||
Private Const SND_SEQ_OPEN_OUTPUT As Integer = 1 | Private Const SND_SEQ_OPEN_OUTPUT As Integer = 1 | ||
+ | Private Const <FONT Color=#006400><B>SND_SEQ_QUEUE_DIRECT</b></font> As Byte = 253 | ||
Private Enum SND_SEQ_EVENT_NOTEON = 6, SND_SEQ_EVENT_NOTEOFF | Private Enum SND_SEQ_EVENT_NOTEON = 6, SND_SEQ_EVENT_NOTEOFF | ||
− | |||
<FONT Color=gray>' ''int snd_seq_open (snd_seq_t **handle, const char * name, int streams, int mode)'' | <FONT Color=gray>' ''int snd_seq_open (snd_seq_t **handle, const char * name, int streams, int mode)'' | ||
Riga 349: | Riga 359: | ||
<FONT Color=gray>' ''int snd_seq_event_output_direct (snd_seq_t *handle, snd_seq_event_t *ev)'' | <FONT Color=gray>' ''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.''</font> | ' ''Output an event directly to the sequencer NOT through output buffer.''</font> | ||
− | Private Extern snd_seq_event_output_direct(handle As Pointer, ev As Snd_seq_event_t) As Integer | + | Private Extern <FONT Color=blue><B>snd_seq_event_output_direct</b></font>(handle As Pointer, ev As Snd_seq_event_t) As Integer |
<FONT Color=gray>' ''int snd_seq_close (snd_seq_t *handle) | <FONT Color=gray>' ''int snd_seq_close (snd_seq_t *handle) | ||
Riga 361: | Riga 371: | ||
Dim evento As New Snd_seq_event_t | Dim evento As New Snd_seq_event_t | ||
Dim rit As Integer | Dim rit As Integer | ||
− | + | ||
snd_seq_open(VarPtr(seq), "default", SND_SEQ_OPEN_OUTPUT, 0) | snd_seq_open(VarPtr(seq), "default", SND_SEQ_OPEN_OUTPUT, 0) | ||
If rit < 0 Then Error.Raise("Impossibile aprire il subsistema 'seq' di ALSA: " & String@(snd_strerror(rit))) | If rit < 0 Then Error.Raise("Impossibile aprire il subsistema 'seq' di ALSA: " & String@(snd_strerror(rit))) | ||
− | + | ||
With evento | With evento | ||
.type = SND_SEQ_EVENT_NOTEON | .type = SND_SEQ_EVENT_NOTEON | ||
− | .queue = SND_SEQ_QUEUE_DIRECT | + | .queue = <FONT Color=#006400><B>SND_SEQ_QUEUE_DIRECT</b></font> |
− | .dest_client = | + | .dest_client = <FONT Color=#B22222><B>CLIENT_DESTINATARIO</b></font> |
− | .dest_port = | + | .dest_port = <FONT Color=#B22222><B>PORTA_DESTINATARIO</b></font> |
.channel = 0 | .channel = 0 | ||
.note = 64 | .note = 64 | ||
.velocity = 100 | .velocity = 100 | ||
End With | End With | ||
− | + | ||
− | snd_seq_event_output_direct(seq, evento) | + | <FONT Color=blue><B>snd_seq_event_output_direct</b></font>(seq, evento) |
− | + | ||
+ | <FONT Color=gray>' ''Imposta la durata dell'esecuzione della nota.'' | ||
+ | ' ''Va precisato che l'uso della funzione "Wait" NON è un modo coerente con il protocollo di ALSA per la temporizzazione degli Eventi Midi: dato che ALSA usa a tale fine la cosiddetta "marcatura degli Eventi Midi" assegnando degli opportuni valori nel membro "flags" e in quelli del "Timestamp" della Struttura "snd_seq_event_t".'' | ||
+ | ' ''Qui si fa uso della funzione "Wait" soltanto per meri motivi didattici ed esemplificativi del funzionamento degli altri aspetti trattati in questa pagina.</font> | ||
Wait 1 | Wait 1 | ||
− | + | ||
evento.type = SND_SEQ_EVENT_NOTEOFF | evento.type = SND_SEQ_EVENT_NOTEOFF | ||
evento.velocity = 0 | evento.velocity = 0 | ||
− | snd_seq_event_output_direct(seq, evento) | + | <FONT Color=blue><B>snd_seq_event_output_direct</b></font>(seq, evento) |
− | + | ||
snd_seq_close(seq) | snd_seq_close(seq) | ||
− | + | ||
'''End''' | '''End''' |
Versione delle 17:16, 23 gen 2022
Per inviare utilmente dati Midi ad altro Client di ALSA, non è necessario connettere i due Client (l'applicativo Gambas ed il Softsynth) mediante la funzione esterna di ALSA "snd_seq_connect_to()", purché si presti attenzione ad alcuni elementi che integrano due casi.
1° caso
Nel primo caso il codice dovrà prevedere:
- la presenza della funzione di ALSA "snd_seq_alloc_queue()", la quale restituisce l'identificativo della coda (queue )degli Eventi Midi ALSA da assegnarsi al membro "queue" della Struttura snd_seq_event_t;
- al membro dest_client e a quello dest_port della Struttura relativa agli eventi Midi dovranno essere rispettivamente assegnati i valori dell'Id e della porta del Client (Softsynth), al quale l'applicativo Gambas dovrà inviare i dati Midi.
Il dispositivo destinatario degli Eventi Midi ALSA sarà individuato, dunque, esclusivamente attraverso il suo numero identificativo e il numero della sua porta quale Client di ALSA.
Mostriamo un esempio pratico:
Private Const CLIENT_DESTINATARIO As Byte = 128 ' Solitamente l'identificativo è 128 Private Const PORTA_DESTINATARIO As Byte = 0 Library "libasound:2.0.0" Public Struct snd_seq_event_t type As Byte flags 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 note As Byte velocity As Byte off_velocity As Byte param As Integer value As Integer End Struct Private Const SND_SEQ_OPEN_OUTPUT As Integer = 1 Private Const SND_SEQ_PORT_CAP_READ As Integer = 1 Private Const SND_SEQ_PORT_TYPE_MIDI_GENERIC As Integer = 2 Private Const SND_SEQ_PORT_TYPE_APPLICATION As Integer = 1048576 Private Enum SND_SEQ_EVENT_NOTEON = 6, SND_SEQ_EVENT_NOTEOFF, SND_SEQ_EVENT_KEYPRESS, SND_SEQ_EVENT_CONTROLLER = 10, SND_SEQ_EVENT_PGMCHANGE ' 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 ' const char * snd_strerror (int errnum) ' Returns the message for an error code. Private Extern snd_strerror(err As Integer) As Pointer ' int snd_seq_client_id (snd_seq_t * seq) ' Get the client id. Private Extern snd_seq_client_id(seq 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(seq As Pointer, name As String, caps As Integer, type As Integer) As Integer ' int snd_seq_alloc_queue (snd_seq_t *handle) ' Allocate a queue. Private Extern snd_seq_alloc_queue(handle As Pointer) As Integer ' int snd_seq_event_output (snd_seq_t *handle, snd_seq_event_t *ev) ' Output an event. Private Extern snd_seq_event_output(handle As Pointer, ev As Snd_seq_event_t) ' int snd_seq_drain_output (snd_seq_t * seq) ' Drain output buffer to sequencer. Private Extern snd_seq_drain_output(seq As Pointer) As Integer ' int snd_seq_close (snd_seq_t *handle) ' Close the sequencer. Private Extern snd_seq_close(handle As Pointer) As Integer Public Sub Main() Dim handle As Pointer Dim rit As Integer Dim note As Byte[] = [60, 62, 64, 65, 67, 69, 71, 72] Dim id, porta, que, b As Byte Dim ev As New Snd_seq_event_t rit = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_OUTPUT, 0) If rit < 0 Then Error.Raise("Impossibile aprire il subsistema 'seq' di ALSA: " & String@(snd_strerror(rit))) id = snd_seq_client_id(handle) If id < 0 Then Error.Raise("Errore: " & String@(snd_strerror(rit))) porta = snd_seq_create_simple_port(handle, "porta_", SND_SEQ_PORT_CAP_READ, SND_SEQ_PORT_TYPE_MIDI_GENERIC Or SND_SEQ_PORT_TYPE_APPLICATION) If porta < 0 Then Error.Raise("Impossibile creare la porta di uscita dati ALSA !") que = snd_seq_alloc_queue(handle) With ev .queue = que .source_client = id .source_port = porta .dest_client = CLIENT_DESTINATARIO .dest_port = PORTA_DESTINATARIO .channel = 0 End With program_change(handle, ev, 48) For b = 0 To note.Max Note_On(handle, ev, note[b]) ' Imposta la durata dell'esecuzione della nota. ' Va precisato che l'uso della funzione "Wait" NON è un modo coerente con il protocollo di ALSA per la temporizzazione degli Eventi Midi: dato che ALSA usa a tale fine la cosiddetta "marcatura degli Eventi Midi" assegnando degli opportuni valori nel membro "flags" e in quelli del "Timestamp" della Struttura "snd_seq_event_t". ' Qui si fa uso della funzione "Wait" soltanto per meri motivi didattici ed esemplificativi del funzionamento degli altri aspetti trattati in questa pagina. Wait 1 Note_Off(handle, ev, note[b]) Next snd_seq_close(handle) End Private Procedure program_change(p As Pointer, ev_prch As Snd_seq_event_t, strum As Integer) With ev_prch .type = SND_SEQ_EVENT_PGMCHANGE .value = strum End With InviaEvento(p, ev_prch) End Private Procedure Note_On(p As Pointer, ev_nton As Snd_seq_event_t, nota As Byte) With ev_nton .type = SND_SEQ_EVENT_NOTEON .note = nota .velocity = 64 End With InviaEvento(p, ev_nton) End Private Procedure Note_Off(p As Pointer, ev_ntoff As Snd_seq_event_t, nota As Byte) With ev_ntoff .type = SND_SEQ_EVENT_NOTEOFF .note = nota .velocity = 0 End With InviaEvento(p, ev_ntoff) End Private Procedure InviaEvento(p As Pointer, evento As Snd_seq_event_t) snd_seq_event_output(p, evento) snd_seq_drain_output(p) End
2° caso
Nel secondo caso si dovrà tenere conto di quanto segue:
- non viene utilizzata la funzione esterna (usata invece nel caso precedente) specifica di ALSA per la creazione della porta del Client applicativo;
- al membro queue della Struttura relativa agli eventi Midi dovrà essere assegnato il valore della Costante Alsa "SND_SEQ_QUEUE_DIRECT" (253);
- al membro dest_id e a quello dest_port della Struttura relativa agli eventi Midi dovranno essere rispettivamente assegnati i valori dell'Id e della porta del Client (Softsynth), al quale l'applicativo Gambas dovrà inviare i dati Midi.
Mostriamo un esempio pratico:
Private Const CLIENT_DESTINATARIO As Byte = 128 ' Solitamente l'identificativo è 128 Private Const PORTA_DESTINATARIO As Byte = 0 Library "libasound:2.0.0" Public Struct snd_seq_event_t type As Byte flags 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 note As Byte velocity As Byte off_velocity As Byte param As Integer value As Integer End Struct Private Const SND_SEQ_OPEN_OUTPUT As Integer = 1 Private Const SND_SEQ_PORT_CAP_READ As Integer = 1 Private Const SND_SEQ_PORT_TYPE_MIDI_GENERIC As Integer = 2 Private Const SND_SEQ_QUEUE_DIRECT As Byte = 253 Private Const SND_SEQ_PORT_TYPE_APPLICATION As Integer = 1048576 Private Enum SND_SEQ_EVENT_NOTEON = 6, SND_SEQ_EVENT_NOTEOFF, SND_SEQ_EVENT_KEYPRESS, SND_SEQ_EVENT_CONTROLLER = 10, SND_SEQ_EVENT_PGMCHANGE ' 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 ' const char * snd_strerror (int errnum) ' Returns the message for an error code. Private Extern snd_strerror(err As Integer) As Pointer ' int snd_seq_client_id (snd_seq_t * seq) ' Get the client id. Private Extern snd_seq_client_id(seq 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(seq As Pointer, name As String, caps As Integer, type As Integer) As Integer ' int snd_seq_alloc_queue (snd_seq_t *handle) ' Allocate a queue. Private Extern snd_seq_alloc_queue(handle As Pointer) As Integer ' int snd_seq_event_output (snd_seq_t *handle, snd_seq_event_t *ev) ' Output an event. Private Extern snd_seq_event_output(handle As Pointer, ev As Snd_seq_event_t) ' int snd_seq_drain_output (snd_seq_t * seq) ' Drain output buffer to sequencer. Private Extern snd_seq_drain_output(seq As Pointer) As Integer ' int snd_seq_close (snd_seq_t *handle) ' Close the sequencer. Private Extern snd_seq_close(handle As Pointer) As Integer Public Sub Main() Dim handle As Pointer Dim rit As Integer Dim note As Byte[] = [60, 62, 64, 65, 67, 69, 71, 72] Dim id, porta, b As Byte Dim ev As New Snd_seq_event_t rit = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_OUTPUT, 0) If rit < 0 Then Error.Raise("Impossibile aprire il subsistema 'seq' di ALSA: " & String@(snd_strerror(rit))) id = snd_seq_client_id(handle) If id < 0 Then Error.Raise("Errore: " & String@(snd_strerror(rit))) porta = snd_seq_create_simple_port(handle, "porta_", SND_SEQ_PORT_CAP_READ, SND_SEQ_PORT_TYPE_MIDI_GENERIC Or SND_SEQ_PORT_TYPE_APPLICATION) If porta < 0 Then Error.Raise("Impossibile creare la porta di uscita dati ALSA !") With ev .queue = SND_SEQ_QUEUE_DIRECT .source_client = id .source_port = porta .dest_client = CLIENT_DESTINATARIO .dest_port = PORTA_DESTINATARIO .channel = 0 End With program_change(handle, ev, 48) For b = 0 To note.Max Note_On(handle, ev, note[b]) ' Imposta la durata dell'esecuzione della nota. ' Va precisato che l'uso della funzione "Wait" NON è un modo coerente con il protocollo di ALSA per la temporizzazione degli Eventi Midi: dato che ALSA usa a tale fine la cosiddetta "marcatura degli Eventi Midi" assegnando degli opportuni valori nel membro "flags" e in quelli del "Timestamp" della Struttura "snd_seq_event_t". ' Qui si fa uso della funzione "Wait" soltanto per meri motivi didattici ed esemplificativi del funzionamento degli altri aspetti trattati in questa pagina. Wait 1 Note_Off(handle, ev, note[b]) Next snd_seq_close(handle) End Private Procedure program_change(p As Pointer, ev_prch As Snd_seq_event_t, strum As Integer) With ev_prch .type = SND_SEQ_EVENT_PGMCHANGE .value = strum End With InviaEvento(p, ev_prch) End Private Procedure Note_On(p As Pointer, ev_nton As Snd_seq_event_t, nota As Byte) With ev_nton .type = SND_SEQ_EVENT_NOTEON .note = nota .velocity = 64 End With InviaEvento(p, ev_nton) End Private Procedure Note_Off(p As Pointer, ev_ntoff As Snd_seq_event_t, nota As Byte) With ev_ntoff .type = SND_SEQ_EVENT_NOTEOFF .note = nota .velocity = 0 End With InviaEvento(p, ev_ntoff) End Private Procedure InviaEvento(p As Pointer, evento As Snd_seq_event_t) snd_seq_event_output(p, evento) snd_seq_drain_output(p) End
Esempio più essenziale per invio diretto degli Eventi Midi ALSA
Mostriamo ora la modalità più breve ed essenziale per inviare ad altro Client di Alsa alcuni eventi Midi, e nella quale non è prevista la creazione di porte del nostro Client applicativo.
Si utilizzerà in questo caso la funzione esterna di ALSA: "snd_seq_event_output_direct()" per invio diretto di ciaascun Evento Midi ALSA.
Private Const CLIENT_DESTINATARIO As Byte = 128 ' Solitamente l'identificativo è 128 Private Const PORTA_DESTINATARIO As Byte = 0 Library "libasound:2.0.0" Public Struct snd_seq_event_t type As Byte flags 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 note As Byte velocity As Byte off_velocity As Byte param As Integer value As Integer End Struct 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 ' 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 ' const char * snd_strerror (int errnum) ' Returns the message for an error code. Private Extern snd_strerror(err As Integer) As Pointer ' 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 ' int snd_seq_close (snd_seq_t *handle) ' Close the sequencer. Private Extern snd_seq_close(handle As Pointer) As Integer Public Sub Main() Dim seq As Pointer Dim evento As New Snd_seq_event_t Dim rit As Integer snd_seq_open(VarPtr(seq), "default", SND_SEQ_OPEN_OUTPUT, 0) If rit < 0 Then Error.Raise("Impossibile aprire il subsistema 'seq' di ALSA: " & String@(snd_strerror(rit))) With evento .type = SND_SEQ_EVENT_NOTEON .queue = SND_SEQ_QUEUE_DIRECT .dest_client = CLIENT_DESTINATARIO .dest_port = PORTA_DESTINATARIO .channel = 0 .note = 64 .velocity = 100 End With snd_seq_event_output_direct(seq, evento) ' Imposta la durata dell'esecuzione della nota. ' Va precisato che l'uso della funzione "Wait" NON è un modo coerente con il protocollo di ALSA per la temporizzazione degli Eventi Midi: dato che ALSA usa a tale fine la cosiddetta "marcatura degli Eventi Midi" assegnando degli opportuni valori nel membro "flags" e in quelli del "Timestamp" della Struttura "snd_seq_event_t". ' Qui si fa uso della funzione "Wait" soltanto per meri motivi didattici ed esemplificativi del funzionamento degli altri aspetti trattati in questa pagina. Wait 1 evento.type = SND_SEQ_EVENT_NOTEOFF evento.velocity = 0 snd_seq_event_output_direct(seq, evento) snd_seq_close(seq) End