Differenze tra le versioni di "Alsa e Gambas: Invio dati con l'uso delle Strutture"
(32 versioni intermedie di uno stesso utente non sono mostrate) | |||
Riga 2: | Riga 2: | ||
In questa pagina verrà preso in considerazione l'uso delle ''[[Strutture:_dichiarazione_ed_uso|Strutture]]'' per l'invio degli Eventi Midi ad ALSA. | In questa pagina verrà preso in considerazione l'uso delle ''[[Strutture:_dichiarazione_ed_uso|Strutture]]'' per l'invio degli Eventi Midi ad ALSA. | ||
− | <BR>Le '''Strutture''', introdotte in [[GAMBAS|Gambas]] con la versione 3.0, possono essere utilizzate per scrivere e gestire i messaggi Midi. Infatti dichiarando una Struttura (''Struct'') ci si riserva una quantità di memoria, definita dal tipo (byte, short, integer, etc.) dei valori dichiarati in ciascun membro della [[Strutture:_dichiarazione_ed_uso|Struttura]]. Dichiarando poi una variabile del tipo di quella Struttura si ha automaticamente una zona di memoria utilizzabile | + | <BR>Le '''Strutture''', introdotte in [[GAMBAS|Gambas]] con la versione 3.0, possono essere utilizzate per scrivere e gestire i messaggi Midi. Infatti dichiarando una Struttura (''Struct'') ci si riserva una quantità di memoria, definita dal tipo (byte, short, integer, etc.) dei valori dichiarati in ciascun membro della [[Strutture:_dichiarazione_ed_uso|Struttura]]. |
− | + | <BR>Dichiarando poi una variabile del tipo di quella Struttura, si ha automaticamente una zona di memoria utilizzabile. | |
− | |||
=Scrittura dei dati dei messaggi Midi nelle Strutture= | =Scrittura dei dati dei messaggi Midi nelle Strutture= | ||
Inseriremo una [[Strutture:_dichiarazione_ed_uso|Struttura]] ''modello'' all'inizio della classe secondaria CAlsa.class. Porremo quindi, quali ''campi'' della struttura-tipo, i dati necessari per la definizione degli eventi Midi, come richiesti da ALSA, attribuendo a ciascun campo il tipo di dato desiderato da ALSA nel rispetto dell'ordine previsto dei dati, nonché del numero del byte al quale ciascun dato dovrà essere assegnato. Inseriremo non solo i dati comuni per tutti i messaggi Midi, ma anche quelli previsti per gli specifici messaggi: | Inseriremo una [[Strutture:_dichiarazione_ed_uso|Struttura]] ''modello'' all'inizio della classe secondaria CAlsa.class. Porremo quindi, quali ''campi'' della struttura-tipo, i dati necessari per la definizione degli eventi Midi, come richiesti da ALSA, attribuendo a ciascun campo il tipo di dato desiderato da ALSA nel rispetto dell'ordine previsto dei dati, nonché del numero del byte al quale ciascun dato dovrà essere assegnato. Inseriremo non solo i dati comuni per tutti i messaggi Midi, ma anche quelli previsti per gli specifici messaggi: | ||
− | + | In <FONT Color=red>rosso</font> sono rappresentati i membri che contengono dati identici per qualunque ''Evento Midi ALSA''; | |
− | Public Struct | + | In <FONT Color=green>verde</font> sono rappresentati i membri che contengono dati relativi allo specifico ''Evento Midi ALSA''. |
+ | |||
+ | '''struct snd_seq_event_t { Public Struct Snd_seq_event_t''' | ||
+ | |||
+ | snd_seq_event_type_t type <FONT Color=green>type</font> As Byte byte di indice 0 | ||
+ | unsigned char flags <FONT Color=red>flags</font> As Byte | ||
+ | unsigned char tag <FONT Color=red>tag</font> As Byte | ||
+ | unsigned char queue <FONT Color=red>queue</font> As Byte | ||
+ | snd_seq_timestamp_t time | ||
+ | ↳ snd_seq_tick_time_t / snd_seq_real_time_t | ||
+ | ↳ tick ↳ tv_sec <FONT Color=green>tick_o_tv_sec</font> As Integer | ||
+ | ↳ tv_nsec <FONT Color=green>tv_nsec</font> As Integer | ||
+ | snd_seq_addr_t source | ||
+ | ↳ unsigned char client <FONT Color=red>source_client</font> As Byte | ||
+ | ↳ unsigned char port <FONT Color=red>source_port</font> As Byte | ||
+ | snd_seq_addr_t dest | ||
+ | ↳ unsigned char client <FONT Color=red>dest_client</font> As Byte | ||
+ | ↳ unsigned char port <FONT Color=red>dest_port</font> As Byte | ||
+ | snd_seq_ev_note_t note | ||
+ | ↳ unsigned char channel <FONT Color=green>channel</font> As Byte byte di indice 16 | ||
+ | ↳ unsigned char note <FONT Color=green>note</font> As Byte byte di indice 17 | ||
+ | ↳ unsigned char velocity <FONT Color=green>velocity</font> As Byte byte di indice 18 | ||
+ | ↳ unsigned char off_velocity <FONT Color=green>off_velocity</font> As Byte byte di indice 19 | ||
+ | unsigned int param <FONT Color=green>param</font> As Integer byte di indice 20 | ||
+ | signed int value <FONT Color=green>value</font> As Integer byte di indice 24 | ||
− | + | '''} End Struct''' | |
− | + | I dati appartenenti agli specifici ''Messaggi Midi'' non vengono utilizzati tutti insieme da ciascun ''Evento Midi ALSA'', ma solo alcuni di essi a seconda dello specifico Messaggio. Ovviamente nella Struttura-tipo essi saranno comunque posti e dichiarati, al fine di consentirne l'uso. | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | End Struct | ||
− | |||
+ | In Gambas si istanzierà una variabile del tipo della Struttura ''snd_seq_event_t'', sopra descritta, con la quale gestire gli specifici ''Eventi Midi'' di ALSA, assegnando a ciascun membro (sia quelli comuni, sia quelli specifici di ogni tipo di ''Evento Midi ALSA'') il relativo valore. Ovviamente si potrà evitare di richiamare i membri che eventualmente non devono essere lasciati con valore zero. | ||
− | |||
− | |||
− | + | =Riassunto delle fasi di creazione, allocamento e invio degli Eventi Midi ad ALSA= | |
+ | Prima di mostrare dei codici esemplificativi sulle varie modalità di invio degli'<I>Eventi Midi</i> al sistema ALSA, è opportuno ricordare quanto descritto nela capitolo precedente "[[La_gestione_dei_dati_Midi_con_il_subsistema_Seq#ALSA_e_Gambas_-_Gestione_dei_Messaggi_Midi_standard|ALSA e Gambas - Gestione dei Messaggi Midi standard]]". | ||
+ | Dunque possiamo elencare per sommicapi le seguenti fasi da seguire: | ||
+ | <BR><B>1</b>) comporre l'<I>Evento Midi ALSA</i> nei membri essenziali della sua Struttura [[Alsa_e_Gambas_-_Dai_Messaggi_Midi_standard_all'Evento_Midi_di_ALSA#Confronto_fra_la_rappresentazione_in_C_e_quella_in_Gambas_della_Struttura_di_ALSA_.22seq_event_t.22|snd_seq_event_t]], ed in particolare attribuire la temporizzazione mediante il "[[ALSA_e_Gambas_-_La_marcatura_temporale_degli_eventi:_il_Timestamp|Timestamp]]" in "tick Midi" o in "real time"; | ||
+ | <BR><B>2</b>) allocare la ''Coda'' degli ''Eventi Midi ALSA'' con la funzione esterna [https://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_queue.html#ga036e62b321b56bee2e8d2e8280a6416a snd_seq_alloc_queue()]; | ||
+ | <BR><B>3</b>) avviare il controllo della ''Coda'' degli ''Eventi Midi ALSA'' con la funzione esterna [https://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_middle.html#ga36dee7982baa47ad22ab8437541ea5ef snd_seq_control_queue(seq, q, type, value, 0, ev)], passandole come 3° argomento la Costante di ALSA "SND_SEQ_EVENT_START"; | ||
+ | <BR><B>4</b>) accodare gli ''Eventi Midi'' nel buffer mediante la funzione esterna [https://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_event.html#ga41be1e09173957944352c50067a686ea snd_seq_event_output_buffer()]; | ||
+ | <BR><B>5</b>) inviare al sistema ALSA mediante la funzione esterna [https://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_event.html#ga64a0ed5488504ef91b5b6b92172bc0aa snd_seq_drain_output()] l'intera ''Coda'' degli ''Eventi Midi'', memorizzati nel buffer, affinche siano processati; | ||
+ | <BR><B>6</b>) arrestare la ''Coda'' degli ''Eventi Midi ALSA'' richiamando la funzione esterna [https://www.alsa-project.org/alsa-doc/alsa-lib/group___seq_middle.html#ga36dee7982baa47ad22ab8437541ea5ef snd_seq_control_queue(seq, q, type, value, 0, ev)], passandole però questa volta come suo 3° argomento la Costante di ALSA "SND_SEQ_EVENT_STOP". | ||
− | |||
− | |||
− | < | + | =Esempi di codice= |
+ | ====1° esempio pratico==== | ||
+ | Nel seguente esempio in ambiente grafico avremo la Classe principale "FMain.Class" e una Classe secondaria, che incapsula le risorse di ALSA da utilizzare. | ||
+ | <BR>Dichiareremo una [[Strutture:_dichiarazione_ed_uso|Struttura]], chiamata ''[[Alsa_e_Gambas_-_Dai_Messaggi_Midi_standard_all'Evento_Midi_di_ALSA#Confronto_fra_la_rappresentazione_in_C_e_quella_in_Gambas_della_Struttura_di_ALSA_.22seq_event_t.22|snd_seq_event_t]]'', che farà da ''modello'' alla particolare variabile di quel tipo di ''Struttura'' da usare per la organizzazione di ciascun ''Evento Midi ALSA''. | ||
+ | <BR>In questo esempio sarà effettuato un invio diretto e immediato di un singolo ''Evento Midi'' per volta: cliccando su un qualunque tasto della tastiera del computer, si invierà un ''Evento Midi'' "NoteON"; rilasciando il tasto si invierà un ''Evento Midi'' "NoteOFF", per far cesssare l'esecuzione della nota Midi prima avviata. | ||
+ | <BR>Saranno gestiti comunque in apertura del programma anche gli <I>Eventi Midi</i> "Control Change" e "Program Change". | ||
+ | <BR>Riguardo al ''Timestamp'' di ogni ''Evento Midi ALSA'' inviato, trattandosi di invio singolo e diretto con esecuzione immediata è indifferente la modalità scelta: il membro ''time'' della predetta Struttura ''[[Alsa_e_Gambas_-_Dai_Messaggi_Midi_standard_all'Evento_Midi_di_ALSA#Confronto_fra_la_rappresentazione_in_C_e_quella_in_Gambas_della_Struttura_di_ALSA_.22seq_event_t.22|snd_seq_event_t]]'' relativo al ''Timestamp'' del singolo ''Evento Midi'' sarà posto a zero. | ||
− | + | Dunque avremo la Classe principale "FMain.Class": | |
+ | Private Const id_dev As Integer = 128 <FONT Color=gray>' ''14 = midi out oppure 128 (solitamente) = softsynth''</font> | ||
+ | Private Const p_dev As Integer = 0 <FONT Color=gray>' ''porta del Client destinatario dei dati Midi: solitamente 0''</font> | ||
+ | Public alsa As CAlsa <FONT Color=gray>' ''Classe che incapsula le funzioni ALSA''</font> | ||
+ | Private bo As Boolean | ||
+ | |||
+ | Public Sub Form_Open() | ||
+ | |||
+ | <FONT Color=gray>' ''Crea ("istanzia") un Oggetto della Classe "CAlsa" per poterla usare:''</font> | ||
+ | With alsa = New CAlsa As "Alsa" | ||
+ | <FONT Color=gray>' ''Apre ALSA e assegna un nome al Client:''</font> | ||
+ | Me.Title = "|| Client ALSA " & .AlsaIni("Programma di prova") | ||
+ | <FONT Color=gray>' ''Sceglie la periferica (Softsynth) su cui suonare:''</font> | ||
+ | .ImpostaDispositivo(id_dev, p_dev) | ||
+ | End With | ||
+ | |||
+ | <FONT Color=gray>' ''Imposta il Volume gestendo l'Evento Midi "Control Change":''</font> | ||
+ | alsa.ControlChange(0, 7, 64) | ||
+ | <FONT Color=gray>' ''Imposta il suono dello strumento musicale gestendo l'Evento Midi "Program Change":''</font> | ||
+ | alsa.ProgramChange(0, 0, 44) | ||
+ | |||
+ | End | ||
+ | |||
+ | |||
+ | Public Sub Form_KeyPress() | ||
+ | |||
+ | If bo Then Return | ||
+ | <FONT Color=gray>' ''Imposta la nota midi da eseguire gestendo l'Evento Midi "Note-ON":''</font> | ||
+ | alsa.NoteON(0, 64, 100) | ||
+ | alsa.Flush() | ||
+ | bo = True | ||
+ | |||
+ | End | ||
+ | |||
+ | |||
+ | Public Sub Form_KeyRelease() | ||
+ | |||
+ | <FONT Color=gray>' ''Imposta la nota midi da eseguire gestendo l'Evento Midi "Note-OFF":''</font> | ||
+ | alsa.NoteOFF(0, 64, 0) | ||
+ | alsa.Flush() | ||
+ | bo = False | ||
+ | |||
+ | End | ||
+ | |||
+ | |||
+ | Public Sub Form_Close() | ||
+ | |||
+ | alsa.Chiude() | ||
+ | |||
+ | End | ||
− | < | + | Nella Classe secondaria, chiamata "CAlsa", avremo il seguente codice: |
− | < | + | Private handle As Pointer |
− | + | Private id As Integer | |
− | < | + | Private s_port As Integer |
− | + | Private que As Integer | |
− | Public | + | Private dclient As Byte |
+ | Private dport As Byte | ||
+ | |||
+ | |||
+ | Library "libasound:2.0.0" | ||
+ | |||
+ | Public Struct <Font Color= #B22222>snd_seq_event_t</font> | ||
+ | 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, SND_SEQ_EVENT_CHANPRESS, SND_SEQ_EVENT_PITCHBEND | ||
+ | |||
+ | <FONT Color=gray>' ''int snd_seq_open (snd_seq_t **seqp, const char * name, int streams, int mode)'' | ||
+ | ' ''Open the ALSA sequencer.''</font> | ||
+ | Private Extern snd_seq_open(seqp As Pointer, name As String, streams As Integer, mode As Integer) As Integer | ||
+ | |||
+ | <FONT Color=gray>' ''int snd_seq_set_client_name (snd_seq_t* seq, const char* name)'' | ||
+ | ' ''Set client name.''</font> | ||
+ | Private Extern snd_seq_set_client_name(seq As Pointer, name As String) As Integer | ||
+ | |||
+ | <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)'' | ||
+ | ' ''Creates a port with the given capability and type bits.''</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 * seq)'' | ||
+ | ' ''Allocate a queue.''</font> | ||
+ | Private Extern snd_seq_alloc_queue(seq As Pointer) As Integer | ||
+ | |||
+ | <FONT Color=gray>' ''int snd_seq_event_output_buffer (snd_seq_t * seq, snd_seq_event_t *ev)'' | ||
+ | ' ''Output an event.''</font> | ||
+ | Private Extern snd_seq_event_output_buffer(seq As Pointer, ev As Snd_seq_event_t) As Integer | ||
+ | |||
+ | <FONT Color=gray>' ''int snd_seq_drain_output (snd_seq_t * seq)'' | ||
+ | ' ''Drain output buffer to sequencer.''</font> | ||
+ | Private Extern snd_seq_drain_output(seq As Pointer) As Integer | ||
+ | |||
+ | <FONT Color=gray>' ''const char * snd_strerror (int errnum)'' | ||
+ | ' ''Returns the message for an error code.''</font> | ||
+ | Private Extern snd_strerror(errnum As Integer) As String | ||
+ | |||
+ | <FONT Color=gray>' ''int snd_seq_close (snd_seq_t * seq)'' | ||
+ | ' ''Close the sequencer.''</font> | ||
+ | Private Extern snd_seq_close(seq As Pointer) As Integer | ||
+ | |||
+ | |||
+ | Public Function AlsaIni(nome As String) As String | ||
+ | |||
+ | Dim err As Integer | ||
+ | |||
+ | err = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_OUTPUT, 0) | ||
+ | Print "Apertura del subsistema 'seq' di ALSA = "; IIf(err == 0, "corretta !", "errata !") | ||
+ | If err < 0 Then error.RAISE("Errore: " & snd_strerror(err)) | ||
+ | |||
+ | snd_seq_set_client_name(handle, nome) | ||
+ | id = snd_seq_client_id(handle) | ||
+ | Print "Alsa ClientID = "; id | ||
+ | |||
+ | s_port = snd_seq_create_simple_port(handle, "Uscita", SND_SEQ_PORT_CAP_READ, SND_SEQ_PORT_TYPE_MIDI_GENERIC + SND_SEQ_PORT_TYPE_APPLICATION) | ||
+ | Print "Porta del programma = "; s_port | ||
+ | If s_port < 0 Then error.Raise("Errore: " & snd_strerror(s_port)) | ||
+ | |||
+ | que = snd_seq_alloc_queue(handle) | ||
+ | If que < 0 Then error.Raise("Errore: " & snd_strerror(que)) | ||
+ | |||
+ | Return CStr(id) & ":" & CStr(s_port) | ||
+ | |||
+ | End | ||
+ | |||
+ | |||
+ | Public Procedure ImpostaDispositivo(client As Integer, port As Integer) | ||
+ | |||
+ | dclient = client | ||
+ | dport = port | ||
+ | |||
+ | End | ||
+ | |||
+ | |||
+ | Public Procedure ControlChange(canale As Byte, prm As Integer, value As Integer) | ||
+ | |||
+ | Dim ev_midi As New Snd_seq_event_t | ||
+ | Dim err As Integer | ||
+ | |||
+ | With ev_midi | ||
+ | .type = SND_SEQ_EVENT_CONTROLLER | ||
+ | .queue = que | ||
+ | .source_client = id | ||
+ | .source_port = s_port | ||
+ | .dest_client = dclient | ||
+ | .dest_port = dport | ||
+ | .channel = canale | ||
+ | .param = prm | ||
+ | .value = value | ||
+ | End With | ||
+ | |||
+ | err = snd_seq_event_output_buffer(handle, ev_midi) | ||
+ | If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) | ||
+ | |||
+ | End | ||
+ | |||
+ | |||
+ | Public Procedure ProgramChange(canale As Byte, non_usato As Byte, value As Integer) | ||
+ | |||
+ | Dim err As Integer | ||
+ | Dim ev_midi As New Snd_seq_event_t | ||
+ | |||
+ | With ev_midi | ||
+ | .type = SND_SEQ_EVENT_PGMCHANGE | ||
+ | .queue = que | ||
+ | .source_client = id | ||
+ | .source_port = s_port | ||
+ | .dest_client = dclient | ||
+ | .dest_port = dport | ||
+ | .channel = canale | ||
+ | .value = value | ||
+ | End With | ||
+ | |||
+ | err = snd_seq_event_output_buffer(handle, ev_midi) | ||
+ | If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) | ||
+ | |||
+ | End | ||
+ | |||
+ | |||
+ | Public Procedure NoteON(canale As Byte, nota As Byte, vel As Byte) | ||
+ | |||
+ | Dim ev_midi As New Snd_seq_event_t | ||
+ | Dim err As Integer | ||
+ | |||
+ | With ev_midi | ||
+ | .type = SND_SEQ_EVENT_NOTEON | ||
+ | .queue = que | ||
+ | .source_client = id | ||
+ | .source_port = s_port | ||
+ | .dest_client = dclient | ||
+ | .dest_port = dport | ||
+ | .channel = canale | ||
+ | .note = nota | ||
+ | .velocity = vel | ||
+ | End With | ||
+ | |||
+ | err = snd_seq_event_output_buffer(handle, ev_midi) | ||
+ | If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) | ||
+ | |||
+ | End | ||
+ | |||
+ | |||
+ | Public Procedure NoteOFF(canale As Byte, nota As Byte, vel As Byte) | ||
+ | |||
+ | Dim ev_midi As New Snd_seq_event_t | ||
+ | Dim err As Integer | ||
+ | |||
+ | With ev_midi | ||
+ | .type = SND_SEQ_EVENT_NOTEOFF | ||
+ | .queue = que | ||
+ | .source_client = id | ||
+ | .source_port = s_port | ||
+ | .dest_client = dclient | ||
+ | .dest_port = dport | ||
+ | .channel = canale | ||
+ | .note = nota | ||
+ | .velocity = vel | ||
+ | End With | ||
+ | |||
+ | err = snd_seq_event_output_buffer(handle, ev_midi) | ||
+ | If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) | ||
+ | |||
+ | End | ||
+ | |||
+ | |||
+ | Public Procedure Flush() | ||
+ | |||
+ | Dim err As Integer | ||
+ | |||
+ | err = snd_seq_drain_output(handle) | ||
+ | If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) | ||
+ | |||
+ | End | ||
− | |||
− | + | Public Procedure Chiude() | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | snd_seq_close(handle) | |
− | |||
End | End | ||
− | == | + | ====2° esempio pratico==== |
− | + | Di seguito un programma simile al precedente, nel quale si farà uso di uno ''Slider'' per agire sul Messaggio Midi ''Pitch Bend''. | |
− | < | + | Nella Classe principale avremo il seguente codice: |
− | + | Private Const id_dev As Integer = 128 <FONT Color=gray>' ''14 = midi out oppure 128 (solitamente) = softsynth''</font> | |
− | + | Private Const p_dev As Integer = 0 <FONT Color=gray>' ''porta del Client destinatario dei dati Midi: solitamente 0''</font> | |
− | + | Public alsa As CAlsa <FONT Color=gray>' ''Classe che incapsula le funzioni ALSA''</font> | |
− | < | + | Private bo As Boolean |
− | < | + | |
− | + | Public Sub Form_Open() | |
− | < | + | |
− | + | With Slider1 | |
− | Public Sub | + | .MinValue = -8192 |
+ | .MaxValue = 8192 | ||
+ | End With | ||
+ | |||
+ | <FONT Color=gray>' ''Crea ("istanzia") un Oggetto della Classe "CAlsa" per poterla usare:''</font> | ||
+ | With alsa = New CAlsa As "Alsa" | ||
+ | <FONT Color=gray>' ''Apre ALSA e assegna un nome al Client:''</font> | ||
+ | Me.Title = "|| Client ALSA " & .AlsaIni("Programma di prova") | ||
+ | <FONT Color=gray>' ''Sceglie la periferica (Softsynth) su cui suonare:''</font> | ||
+ | .ImpostaDispositivo(id_dev, p_dev) | ||
+ | End With | ||
+ | |||
+ | <FONT Color=gray>' ''Imposta il Volume gestendo l'Evento Midi "Control Change":''</font> | ||
+ | alsa.ControlChange(0, 7, 64) | ||
+ | <FONT Color=gray>' ''Imposta il suono dello strumento musicale gestendo l'Evento Midi "Program Change":''</font> | ||
+ | alsa.ProgramChange(0, 0, 44) | ||
+ | |||
+ | End | ||
+ | |||
+ | Public Sub Form_KeyPress() | ||
+ | |||
+ | If bo Then Return | ||
+ | <FONT Color=gray>' ''Imposta la nota midi da eseguire gestendo l'Evento Midi "Note-ON":''</font> | ||
+ | alsa.NoteON(0, 64, 100) | ||
+ | alsa.Flush() | ||
+ | bo = True | ||
+ | |||
+ | End | ||
+ | |||
+ | |||
+ | Public Sub Form_KeyRelease() | ||
+ | |||
+ | <FONT Color=gray>' ''Imposta la nota midi da eseguire gestendo l'Evento Midi "Note-OFF":''</font> | ||
+ | alsa.NoteOFF(0, 64, 0) | ||
+ | alsa.Flush() | ||
+ | bo = False | ||
+ | |||
+ | End | ||
− | |||
− | + | Public Sub Slider1_Change() | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | alsa.PitchBend(0, 0, Slider1.Value) | |
− | + | alsa.Flush() | |
End | End | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | Public Sub Form_Close() | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | alsa.Chiude() | |
− | |||
End | End | ||
− | |||
− | + | Nella Classe secondaria, chiamata "CAlsa", avremo il seguente codice: | |
− | + | Private handle As Pointer | |
− | + | Private id As Integer | |
− | + | Private s_port As Integer | |
− | Library "libasound:2" | + | Private que As Integer |
+ | Private dclient As Byte | ||
+ | Private dport As Byte | ||
+ | |||
+ | |||
+ | Library "libasound:2.0.0" | ||
− | + | Public Struct <Font Color= #B22222>snd_seq_event_t</font> | |
− | type As Byte | + | type As Byte |
− | + | flags As Byte | |
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_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 | + | 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_MIDI_GENERIC As Integer = 2 | ||
Private Const SND_SEQ_PORT_TYPE_APPLICATION As Integer = 1048576 | 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, | Private Enum SND_SEQ_EVENT_NOTEON = 6, SND_SEQ_EVENT_NOTEOFF, SND_SEQ_EVENT_KEYPRESS, | ||
SND_SEQ_EVENT_CONTROLLER = 10, SND_SEQ_EVENT_PGMCHANGE, SND_SEQ_EVENT_CHANPRESS, SND_SEQ_EVENT_PITCHBEND | SND_SEQ_EVENT_CONTROLLER = 10, SND_SEQ_EVENT_PGMCHANGE, SND_SEQ_EVENT_CHANPRESS, SND_SEQ_EVENT_PITCHBEND | ||
Riga 173: | Riga 436: | ||
' ''Set client name.''</font> | ' ''Set client name.''</font> | ||
Private Extern snd_seq_set_client_name(seq As Pointer, name As String) As Integer | Private Extern snd_seq_set_client_name(seq As Pointer, name As String) As Integer | ||
+ | |||
+ | <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)'' | ||
' ''Creates a port with the given capability and type bits.''</font> | ' ''Creates a port with the given capability and type bits.''</font> | ||
Private Extern snd_seq_create_simple_port(seq As Pointer, name As String, caps As Integer, type As Integer) As Integer | 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 * seq)'' | ||
+ | ' ''Allocate a queue.''</font> | ||
+ | Private Extern snd_seq_alloc_queue(seq As Pointer) As Integer | ||
+ | |||
+ | <FONT Color=gray>' ''int snd_seq_event_output_buffer (snd_seq_t * seq, snd_seq_event_t *ev)'' | ||
+ | ' ''Output an event.''</font> | ||
+ | Private Extern snd_seq_event_output_buffer(seq As Pointer, ev As Snd_seq_event_t) As Integer | ||
+ | |||
+ | <FONT Color=gray>' ''int snd_seq_drain_output (snd_seq_t * seq)'' | ||
+ | ' ''Drain output buffer to sequencer.''</font> | ||
+ | Private Extern snd_seq_drain_output(seq As Pointer) As Integer | ||
+ | |||
+ | <FONT Color=gray>' ''const char * snd_strerror (int errnum)'' | ||
+ | ' ''Returns the message for an error code.''</font> | ||
+ | Private Extern snd_strerror(errnum As Integer) As String | ||
+ | |||
+ | <FONT Color=gray>' ''int snd_seq_close (snd_seq_t * seq)'' | ||
+ | ' ''Close the sequencer.''</font> | ||
+ | Private Extern snd_seq_close(seq As Pointer) As Integer | ||
+ | |||
+ | |||
+ | Public Function AlsaIni(nome As String) As String | ||
+ | |||
+ | Dim err As Integer | ||
+ | |||
+ | err = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_OUTPUT, 0) | ||
+ | Print "Apertura del subsistema 'seq' di ALSA = "; IIf(err == 0, "corretta !", "errata !") | ||
+ | If err < 0 Then error.RAISE("Errore: " & snd_strerror(err)) | ||
+ | |||
+ | snd_seq_set_client_name(handle, nome) | ||
+ | id = snd_seq_client_id(handle) | ||
+ | Print "Alsa ClientID = "; id | ||
+ | |||
+ | s_port = snd_seq_create_simple_port(handle, "Uscita", SND_SEQ_PORT_CAP_READ, SND_SEQ_PORT_TYPE_MIDI_GENERIC + SND_SEQ_PORT_TYPE_APPLICATION) | ||
+ | Print "Porta del programma = "; s_port | ||
+ | If s_port < 0 Then error.Raise("Errore: " & snd_strerror(s_port)) | ||
+ | |||
+ | que = snd_seq_alloc_queue(handle) | ||
+ | If que < 0 Then error.Raise("Errore: " & snd_strerror(que)) | ||
+ | |||
+ | Return CStr(id) & ":" & CStr(s_port) | ||
+ | |||
+ | End | ||
+ | |||
+ | |||
+ | Public Procedure ImpostaDispositivo(client As Integer, port As Integer) | ||
+ | |||
+ | dclient = client | ||
+ | dport = port | ||
+ | |||
+ | End | ||
+ | |||
+ | |||
+ | Public Procedure ControlChange(canale As Byte, prm As Integer, value As Integer) | ||
+ | |||
+ | Dim ev_midi As New Snd_seq_event_t | ||
+ | Dim err As Integer | ||
+ | |||
+ | With ev_midi | ||
+ | .type = SND_SEQ_EVENT_CONTROLLER | ||
+ | .queue = que | ||
+ | .source_client = id | ||
+ | .source_port = s_port | ||
+ | .dest_client = dclient | ||
+ | .dest_port = dport | ||
+ | .channel = canale | ||
+ | .param = prm | ||
+ | .value = value | ||
+ | End With | ||
+ | |||
+ | err = snd_seq_event_output_buffer(handle, ev_midi) | ||
+ | If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) | ||
+ | |||
+ | End | ||
+ | |||
+ | |||
+ | Public Procedure ProgramChange(canale As Byte, non_usato As Byte, value As Integer) | ||
+ | |||
+ | Dim err As Integer | ||
+ | Dim ev_midi As New Snd_seq_event_t | ||
+ | |||
+ | With ev_midi | ||
+ | .type = SND_SEQ_EVENT_PGMCHANGE | ||
+ | .queue = que | ||
+ | .source_client = id | ||
+ | .source_port = s_port | ||
+ | .dest_client = dclient | ||
+ | .dest_port = dport | ||
+ | .channel = canale | ||
+ | .value = value | ||
+ | End With | ||
+ | |||
+ | err = snd_seq_event_output_buffer(handle, ev_midi) | ||
+ | If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) | ||
+ | |||
+ | End | ||
+ | |||
+ | |||
+ | Public Procedure NoteON(canale As Byte, nota As Byte, vel As Byte) | ||
+ | |||
+ | Dim ev_midi As New Snd_seq_event_t | ||
+ | Dim err As Integer | ||
+ | |||
+ | With ev_midi | ||
+ | .type = SND_SEQ_EVENT_NOTEON | ||
+ | .queue = que | ||
+ | .source_client = id | ||
+ | .source_port = s_port | ||
+ | .dest_client = dclient | ||
+ | .dest_port = dport | ||
+ | .channel = canale | ||
+ | .note = nota | ||
+ | .velocity = vel | ||
+ | End With | ||
+ | |||
+ | err = snd_seq_event_output_buffer(handle, ev_midi) | ||
+ | If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) | ||
+ | |||
+ | End | ||
+ | |||
+ | |||
+ | Public Procedure NoteOFF(canale As Byte, nota As Byte, vel As Byte) | ||
+ | |||
+ | Dim ev_midi As New Snd_seq_event_t | ||
+ | Dim err As Integer | ||
+ | |||
+ | With ev_midi | ||
+ | .type = SND_SEQ_EVENT_NOTEOFF | ||
+ | .queue = que | ||
+ | .source_client = id | ||
+ | .source_port = s_port | ||
+ | .dest_client = dclient | ||
+ | .dest_port = dport | ||
+ | .channel = canale | ||
+ | .note = nota | ||
+ | .velocity = vel | ||
+ | End With | ||
+ | |||
+ | err = snd_seq_event_output_buffer(handle, ev_midi) | ||
+ | If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) | ||
+ | |||
+ | End | ||
+ | |||
+ | |||
+ | Public Procedure Flush() | ||
+ | |||
+ | Dim err As Integer | ||
+ | |||
+ | err = snd_seq_drain_output(handle) | ||
+ | If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) | ||
+ | |||
+ | End | ||
+ | |||
+ | |||
+ | Public Procedure PitchBend(canale As Byte, prm As Integer, val As Integer) | ||
+ | |||
+ | Dim ev_midi As New Snd_seq_event_t | ||
+ | Dim err As Integer | ||
+ | |||
+ | With ev_midi | ||
+ | .type = SND_SEQ_EVENT_PITCHBEND | ||
+ | .queue = que | ||
+ | .source_client = id | ||
+ | .source_port = s_port | ||
+ | .dest_client = dclient | ||
+ | .dest_port = dport | ||
+ | .channel = canale | ||
+ | .param = prm | ||
+ | .value = val | ||
+ | End With | ||
+ | |||
+ | err = snd_seq_event_output_buffer(handle, ev_midi) | ||
+ | If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) | ||
+ | |||
+ | End | ||
+ | |||
+ | |||
+ | Public Procedure Stop() | ||
+ | |||
+ | snd_seq_close(handle) | ||
+ | |||
+ | End | ||
+ | |||
+ | |||
+ | ===Esempi con invio di più Eventi Midi ALSA temporizzati in sequenza=== | ||
+ | Vedremo di seguito due semplici esempi sull'uso del membro ''flags'' della Struttura [[Alsa_e_Gambas_-_Dai_Messaggi_Midi_standard_all'Evento_Midi_di_ALSA#Confronto_fra_la_rappresentazione_in_C_e_quella_in_Gambas_della_Struttura_di_ALSA_.22seq_event_t.22|snd_seq_event_t]] e della [[ALSA_e_Gambas_-_La_marcatura_temporale_degli_eventi:_il_Timestamp|temporizzazione in "tick time" e in "real time"]]. | ||
+ | |||
+ | ====Invio Eventi Midi con temporizzazione in "tick time"==== | ||
+ | Ricordiamo che la temporizzazione in ALSA nella modalità in "<B>tick</b>" Midi è attivata assegnando al membro ''flags'' della [[Alsa_e_Gambas_-_Dai_Messaggi_Midi_standard_all'Evento_Midi_di_ALSA#Confronto_fra_la_rappresentazione_in_C_e_quella_in_Gambas_della_Struttura_di_ALSA_.22seq_event_t.22|snd_seq_event_t]] la Costante "SND_SEQ_TIME_STAMP_TICK" di ALSA, mentre i valori temporali sono espressi appunto in "tick" Midi al membro ''tick'' del membro ''snd_seq_timestamp_t time'' della predetta Struttura. | ||
+ | |||
+ | Mostriamo un esempio pratico in ambiente grafico, nel quale cliccando su un "ToggleButton" si avvierà l'invio ad ALSA degli ''Eventi Midi'' e il loro processamento. Essi saranno eseguiti secondo la propria ''temporizzazione'' impostata nella Struttura [[Alsa_e_Gambas_-_Dai_Messaggi_Midi_standard_all'Evento_Midi_di_ALSA#Confronto_fra_la_rappresentazione_in_C_e_quella_in_Gambas_della_Struttura_di_ALSA_.22seq_event_t.22|snd_seq_event_t]] costitutiva. | ||
+ | <BR>Dunque avremo la Classe principale "FMain.Class": | ||
+ | Private Const id_dev As Integer = 128 <FONT Color=gray>' ''14 = midi out oppure 128 (solitamente) = softsynth''</font> | ||
+ | Private Const p_dev As Integer = 0 <FONT Color=gray>' ''porta del Client destinatario dei dati Midi: solitamente 0''</font> | ||
+ | Public alsa As CAlsa <FONT Color=gray>' ''Classe che incapsula le funzioni ALSA''</font> | ||
+ | |||
+ | |||
+ | Public Sub Form_Open() | ||
+ | |||
+ | <FONT Color=gray>' ''Crea ("istanzia") un Oggetto della Classe "CAlsa" per poterla usare:''</font> | ||
+ | With alsa = New CAlsa As "Alsa" | ||
+ | <FONT Color=gray>' ''Apre ALSA e assegna un nome al Client:''</font> | ||
+ | Me.Title = "|| Client ALSA " & .AlsaIni("Programma di prova") | ||
+ | <FONT Color=gray>' ''Sceglie la periferica (Softsynth) su cui suonare:''</font> | ||
+ | .ImpostaDispositivo(id_dev, p_dev) | ||
+ | End With | ||
+ | |||
+ | End | ||
+ | |||
+ | |||
+ | Public Sub ToggleButton1_Click() | ||
+ | |||
+ | If ToggleButton1.Value Then | ||
+ | With alsa | ||
+ | <FONT Color=gray>' ''Avvia la "Coda" degli Eventi Midi ALSA:''</font> | ||
+ | .AvvioCoda() | ||
+ | <FONT Color=gray>' ''Imposta il Volume gestendo l'Evento Midi "Control Change":''</font> | ||
+ | .ControlChange(0, 0, 0, 7, 50) | ||
+ | <FONT Color=gray>' ''Imposta il suono dello strumento musicale gestendo l'Evento Midi "Program Change":''</font> | ||
+ | .ProgramChange(0, 0, 0, 0, 44) | ||
+ | <FONT Color=gray>' ''Imposta la prima nota Midi da eseguire:''</font> | ||
+ | .NoteON(0, 0, 0, 64, 100) | ||
+ | <FONT Color=gray>' ''Imposta la nota Midi da silenziare dopo 96 tick dall'inizio della "Coda":''</font> | ||
+ | .NoteOFF(0, <FONT Color=#B222222>96</font>, 0, 64, 0) | ||
+ | |||
+ | <FONT Color=gray>' ''Imposta il suono di un altro strumento musicale dopo 150 tick dall'inizio della "Coda" (quindi crea una pausa):''</font> | ||
+ | .ProgramChange(0, <FONT Color=#B222222>182</font>, 0, 0, 18) | ||
+ | <FONT Color=gray>' ''Imposta la nota Midi da eseguire dopo 182 tick dall'inizio della "Coda":''</font> | ||
+ | .NoteON(0, <FONT Color=#B222222>182</font>, 0, 66, 100) | ||
+ | <FONT Color=gray>' ''Imposta la nota Midi da silenziare dopo 300 tick dall'inizio della "Coda":''</font> | ||
+ | .NoteOFF(0, <FONT Color=#B222222>300</font>, 0, 66, 0) | ||
+ | |||
+ | <FONT Color=gray>' ''Imposta il suono di un altro strumento musicale dopo 300 tick dall'inizio della "Coda":''</font> | ||
+ | .ProgramChange(0, <FONT Color=#B222222>300</font>, 0, 0, 18) | ||
+ | <FONT Color=gray>' ''Imposta la nota Midi da eseguire dopo 300 tick dall'inizio della "Coda":''</font> | ||
+ | .NoteON(0, <FONT Color=#B222222>300</font>, 0, 68, 100) | ||
+ | <FONT Color=gray>' ''Imposta la nota Midi da silenziare dopo 500 tick dall'inizio della "Coda":''</font> | ||
+ | .NoteOFF(0, <FONT Color=#B222222>500</font>, 0, 68, 0) | ||
+ | <FONT Color=gray>' ''Dispone infine l'invio di tutti gli Eventi Midi bufferizzati nella "Coda":''</font> | ||
+ | .Flush() | ||
+ | End With | ||
+ | Else | ||
+ | alsa.StopCoda() | ||
+ | Me.Close | ||
+ | Endif | ||
+ | |||
+ | End | ||
+ | Nella Classe secondaria, chiamata "CAlsa", vi sarà invece il seguente codice: | ||
+ | Private handle As Pointer | ||
+ | Private id As Integer | ||
+ | Private s_port As Integer | ||
+ | Private que As Integer | ||
+ | Private dclient As Byte | ||
+ | Private dport As Byte | ||
+ | |||
+ | |||
+ | Library "libasound:2.0.0" | ||
+ | |||
+ | Public Struct <Font Color= #B22222>snd_seq_event_t</font> | ||
+ | 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, SND_SEQ_EVENT_CHANPRESS, | ||
+ | SND_SEQ_EVENT_PITCHBEND, SND_SEQ_EVENT_START = 30, SND_SEQ_EVENT_STOP = 32 | ||
+ | |||
+ | <FONT Color=gray>' ''int snd_seq_open (snd_seq_t **seqp, const char * name, int streams, int mode)'' | ||
+ | ' ''Open the ALSA sequencer.''</font> | ||
+ | Private Extern snd_seq_open(seqp As Pointer, name As String, streams As Integer, mode As Integer) As Integer | ||
+ | |||
+ | <FONT Color=gray>' ''int snd_seq_set_client_name (snd_seq_t* seq, const char* name)'' | ||
+ | ' ''Set client name.''</font> | ||
+ | Private Extern snd_seq_set_client_name(seq As Pointer, name As String) As Integer | ||
<FONT Color=gray>' ''int snd_seq_client_id (snd_seq_t * seq)'' | <FONT Color=gray>' ''int snd_seq_client_id (snd_seq_t * seq)'' | ||
Riga 182: | Riga 742: | ||
Private Extern snd_seq_client_id(seq As Pointer) As Integer | Private Extern snd_seq_client_id(seq As Pointer) As Integer | ||
− | <FONT Color=gray>' ''int | + | <FONT Color=gray>' ''int snd_seq_create_simple_port (snd_seq_t* seq, const char* name, unsigned int caps, unsigned int type)'' |
− | ' '' | + | ' ''Creates a port with the given capability and type bits.''</font> |
− | Private Extern | + | 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 | + | <FONT Color=gray>' ''int snd_seq_alloc_queue (snd_seq_t * seq)'' |
' ''Allocate a queue.''</font> | ' ''Allocate a queue.''</font> | ||
− | Private Extern snd_seq_alloc_queue(seq As Pointer, | + | Private Extern snd_seq_alloc_queue(seq As Pointer) As Integer |
+ | |||
+ | <FONT Color=gray>' ''int snd_seq_control_queue(snd_seq_t * seq, int q, int type, int value, snd_seq_event_t * ev)'' | ||
+ | ' ''Queue controls - start/stop/continue.''</font> | ||
+ | Private Extern snd_seq_control_queue(seq As Pointer, q As Integer, type As Integer, value As Integer, ev As Pointer) As Integer | ||
− | <FONT Color=gray>' ''int | + | <FONT Color=gray>' ''int snd_seq_event_output_buffer (snd_seq_t * seq, snd_seq_event_t *ev)'' |
' ''Output an event.''</font> | ' ''Output an event.''</font> | ||
− | Private Extern | + | Private Extern snd_seq_event_output_buffer(seq As Pointer, ev As Snd_seq_event_t) As Integer |
<FONT Color=gray>' ''int snd_seq_drain_output (snd_seq_t * seq)'' | <FONT Color=gray>' ''int snd_seq_drain_output (snd_seq_t * seq)'' | ||
Riga 207: | Riga 771: | ||
− | + | Public Function AlsaIni(nome As String) As String | |
− | + | Dim err As Integer | |
− | Dim err | ||
− | |||
− | err = snd_seq_open(VarPtr(handle), "default", | + | err = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_OUTPUT, 0) |
− | Print "Apertura del subsistema 'seq' di ALSA = "; err | + | Print "Apertura del subsistema 'seq' di ALSA = "; IIf(err == 0, "corretta !", "errata !") |
If err < 0 Then error.RAISE("Errore: " & snd_strerror(err)) | If err < 0 Then error.RAISE("Errore: " & snd_strerror(err)) | ||
− | snd_seq_set_client_name(handle, | + | snd_seq_set_client_name(handle, nome) |
id = snd_seq_client_id(handle) | id = snd_seq_client_id(handle) | ||
Print "Alsa ClientID = "; id | Print "Alsa ClientID = "; id | ||
− | + | s_port = snd_seq_create_simple_port(handle, "Uscita", SND_SEQ_PORT_CAP_READ, SND_SEQ_PORT_TYPE_MIDI_GENERIC + SND_SEQ_PORT_TYPE_APPLICATION) | |
− | Print "Porta del programma = "; | + | Print "Porta del programma = "; s_port |
− | If | + | If s_port < 0 Then error.Raise("Errore: " & snd_strerror(s_port)) |
− | que = snd_seq_alloc_queue(handle | + | que = snd_seq_alloc_queue(handle) |
If que < 0 Then error.Raise("Errore: " & snd_strerror(que)) | If que < 0 Then error.Raise("Errore: " & snd_strerror(que)) | ||
− | + | Return CStr(id) & ":" & CStr(s_port) | |
− | |||
− | |||
− | + | End | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | Public Procedure ImpostaDispositivo(client As Integer, port As Integer) | |
− | + | ||
− | + | dclient = client | |
− | + | dport = port | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | End | |
+ | |||
+ | |||
+ | Public Procedure AvvioCoda() | ||
+ | |||
+ | Dim err As Integer | ||
+ | |||
+ | err = snd_seq_control_queue(handle, que, SND_SEQ_EVENT_START, 0, 0) | ||
+ | If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) | ||
+ | |||
+ | End | ||
+ | |||
+ | |||
+ | Public Procedure ControlChange(flg As Byte, tick As Integer, canale As Byte, prm As Integer, value As Integer) | ||
+ | |||
+ | Dim ev_midi As New Snd_seq_event_t | ||
+ | Dim err As Integer | ||
+ | |||
+ | With ev_midi | ||
+ | .type = SND_SEQ_EVENT_CONTROLLER | ||
+ | .flags = flg | ||
+ | .tick_o_tv_sec = tick | ||
+ | .queue = que | ||
+ | .source_client = id | ||
+ | .source_port = s_port | ||
+ | .dest_client = dclient | ||
+ | .dest_port = dport | ||
+ | .channel = canale | ||
+ | .param = prm | ||
+ | .value = value | ||
+ | End With | ||
+ | |||
+ | err = snd_seq_event_output_buffer(handle, ev_midi) | ||
+ | If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) | ||
+ | |||
+ | End | ||
+ | |||
+ | |||
+ | Public Procedure ProgramChange(flg As Byte, tick As Integer, canale As Byte, non_usato As Byte, value As Integer) | ||
+ | |||
+ | Dim err As Integer | ||
+ | Dim ev_midi As New Snd_seq_event_t | ||
+ | |||
+ | With ev_midi | ||
+ | .type = SND_SEQ_EVENT_PGMCHANGE | ||
+ | .flags = flg | ||
+ | .tick_o_tv_sec = tick | ||
+ | .queue = que | ||
+ | .source_client = id | ||
+ | .source_port = s_port | ||
+ | .dest_client = dclient | ||
+ | .dest_port = dport | ||
+ | .channel = canale | ||
+ | .value = value | ||
+ | End With | ||
+ | |||
+ | err = snd_seq_event_output_buffer(handle, ev_midi) | ||
+ | If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) | ||
+ | |||
+ | End | ||
+ | |||
+ | |||
+ | Public Procedure NoteON(flg As Byte, tick As Integer, canale As Byte, nota As Byte, vel As Byte) | ||
+ | |||
+ | Dim ev_midi As New Snd_seq_event_t | ||
+ | Dim err As Integer | ||
+ | |||
+ | With ev_midi | ||
+ | .type = SND_SEQ_EVENT_NOTEON | ||
+ | .flags = flg | ||
+ | .tick_o_tv_sec = tick | ||
+ | .queue = que | ||
+ | .source_client = id | ||
+ | .source_port = s_port | ||
+ | .dest_client = dclient | ||
+ | .dest_port = dport | ||
+ | .channel = canale | ||
+ | .note = nota | ||
+ | .velocity = vel | ||
+ | End With | ||
+ | |||
+ | err = snd_seq_event_output_buffer(handle, ev_midi) | ||
+ | If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) | ||
+ | |||
+ | End | ||
+ | |||
+ | |||
+ | Public Procedure NoteOFF(flg As Byte, tick As Integer, canale As Byte, nota As Byte, vel As Byte) | ||
+ | |||
+ | Dim ev_midi As New Snd_seq_event_t | ||
+ | Dim err As Integer | ||
− | + | With ev_midi | |
− | + | .type = SND_SEQ_EVENT_NOTEOFF | |
+ | .flags = flg | ||
+ | .tick_o_tv_sec = tick | ||
+ | .queue = que | ||
+ | .source_client = id | ||
+ | .source_port = s_port | ||
+ | .dest_client = dclient | ||
+ | .dest_port = dport | ||
+ | .channel = canale | ||
+ | .note = nota | ||
+ | .velocity = vel | ||
+ | End With | ||
− | + | err = snd_seq_event_output_buffer(handle, ev_midi) | |
− | + | If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | End | |
− | |||
− | |||
− | + | Public Procedure Flush() | |
Dim err As Integer | Dim err As Integer | ||
− | err = | + | err = snd_seq_drain_output(handle) |
− | If err < 0 Then | + | If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) |
+ | |||
+ | End | ||
+ | |||
+ | |||
+ | Public Procedure StopCoda() | ||
− | err = | + | Dim err As Integer |
− | If err < 0 Then | + | |
+ | err = snd_seq_control_queue(handle, que, SND_SEQ_EVENT_STOP, 0, 0) | ||
+ | If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) | ||
− | ''' | + | snd_seq_close(handle) |
+ | |||
+ | End | ||
+ | |||
+ | ====Invio Eventi Midi con temporizzazione in "real time"==== | ||
+ | La temporizzazione in ALSA nella modalità in "<B>real time</b>" (temporizzazione in formato orario) è attivata assegnando al membro ''flags'' della [[Alsa_e_Gambas_-_Dai_Messaggi_Midi_standard_all'Evento_Midi_di_ALSA#Confronto_fra_la_rappresentazione_in_C_e_quella_in_Gambas_della_Struttura_di_ALSA_.22seq_event_t.22|snd_seq_event_t]] la Costante "SND_SEQ_TIME_STAMP_REAL" di ALSA. | ||
+ | <BR>Ricordiamo che questa modalità di temporizzazione degli ''Eventi Midi'' di ALSA adotta il sistema di misura dell'orario standard (ore, minuti, secondi), assegnando il valore in "secondi" al membro ''tv_sec'' e in "nanosecondi" al membro ''tv_nsec'' della sotto-Struttura ''snd_seq_real_time_t'' del membro ''time'' della Struttura principale [[Alsa_e_Gambas_-_Dai_Messaggi_Midi_standard_all'Evento_Midi_di_ALSA#Confronto_fra_la_rappresentazione_in_C_e_quella_in_Gambas_della_Struttura_di_ALSA_.22seq_event_t.22|snd_seq_event_t]] costitutiva. | ||
− | + | Mostriamo un esempio identico al precedente, ma con impostazione della temporizzazione in ''Clock'' Midi (modalità "orario") espressa in secondi: | |
− | + | <BR>Dunque avremo la Classe principale "FMain.Class": | |
− | + | Private Const id_dev As Integer = 128 <FONT Color=gray>' ''14 = midi out oppure 128 (solitamente) = softsynth''</font> | |
− | <BR> | + | Private Const p_dev As Integer = 0 <FONT Color=gray>' ''porta del Client destinatario dei dati Midi: solitamente 0''</font> |
− | < | + | Public alsa As CAlsa <FONT Color=gray>' ''Classe che incapsula le funzioni ALSA''</font> |
− | + | ||
− | |||
− | |||
− | Public Struct snd_seq_event_t | + | Public Sub Form_Open() |
− | + | ||
− | + | <FONT Color=gray>' ''Crea ("istanzia") un Oggetto della Classe "CAlsa" per poterla usare:''</font> | |
− | + | With alsa = New CAlsa As "Alsa" | |
− | + | <FONT Color=gray>' ''Apre ALSA e assegna un nome al Client:''</font> | |
− | + | Me.Title = "|| Client ALSA " & .AlsaIni("Programma di prova") | |
− | + | <FONT Color=gray>' ''Sceglie la periferica (Softsynth) su cui suonare:''</font> | |
− | + | .ImpostaDispositivo(id_dev, p_dev) | |
− | + | End With | |
− | + | ||
− | + | End | |
− | + | ||
− | + | ||
− | + | Public Sub ToggleButton1_Click() | |
− | + | ||
− | + | If ToggleButton1.Value Then | |
− | + | With alsa | |
+ | <FONT Color=gray>' ''Avvia la "Coda" degli Eventi Midi ALSA:''</font> | ||
+ | .AvvioCoda() | ||
+ | <FONT Color=gray>' ''Imposta il Volume gestendo l'Evento Midi "Control Change":''</font> | ||
+ | .ControlChange(<FONT Color=#B222222>1</font>, 0, 0, 7, 50) | ||
+ | <FONT Color=gray>' ''Imposta il suono dello strumento musicale gestendo l'Evento Midi "Program Change":''</font> | ||
+ | .ProgramChange(<FONT Color=#B222222>1</font>, 0, 0, 0, 44) | ||
+ | <FONT Color=gray>' ''Imposta la prima nota Midi da eseguire:''</font> | ||
+ | .NoteON(<FONT Color=#B222222>1</font>, 0, 0, 64, 100) | ||
+ | <FONT Color=gray>' ''Imposta la nota Midi da silenziare dopo 1 secondo dall'inizio della "Coda":''</font> | ||
+ | .NoteOFF(<FONT Color=#B222222>1</font>, <FONT Color=#B222222>1</font>, 0, 64, 0) | ||
+ | |||
+ | <FONT Color=gray>' ''Imposta il suono di un altro strumento musicale dopo 2 secondi dall'inizio della "Coda" (quindi crea una pausa):''</font> | ||
+ | .ProgramChange(<FONT Color=#B222222>1</font>, <FONT Color=#B222222>2</font>, 0, 0, 18) | ||
+ | <FONT Color=gray>' ''Imposta la nota Midi da eseguire dopo 2 secondi dall'inizio della "Coda":''</font> | ||
+ | .NoteON(<FONT Color=#B222222>1</font>, <FONT Color=#B222222>2</font>, 0, 66, 100) | ||
+ | <FONT Color=gray>' ''Imposta la nota Midi da silenziare dopo 3 secondi dall'inizio della "Coda":''</font> | ||
+ | .NoteOFF(<FONT Color=#B222222>1</font>, <FONT Color=#B222222>3</font>, 0, 66, 0) | ||
+ | |||
+ | <FONT Color=gray>' ''Imposta il suono di un altro strumento musicale dopo 3 secondi dall'inizio della "Coda":''</font> | ||
+ | .ProgramChange(<FONT Color=#B222222>1</font>, <FONT Color=#B222222>3</font>, 0, 0, 18) | ||
+ | <FONT Color=gray>' ''Imposta la nota Midi da eseguire dopo 3 secondi dall'inizio della "Coda":''</font> | ||
+ | .NoteON(<FONT Color=#B222222>1</font>, <FONT Color=#B222222>3</font>, 0, 68, 100) | ||
+ | <FONT Color=gray>' ''Imposta la nota Midi da silenziare dopo 5 secondi dall'inizio della "Coda":''</font> | ||
+ | .NoteOFF(<FONT Color=#B222222>1</font>, <FONT Color=#B222222>5</font>, 0, 68, 0) | ||
+ | <FONT Color=gray>' ''Dispone infine l'invio di tutti gli Eventi Midi bufferizzati nella "Coda":''</font> | ||
+ | .Flush() | ||
+ | End With | ||
+ | Else | ||
+ | alsa.StopCoda() | ||
+ | Me.Close | ||
+ | Endif | ||
+ | |||
+ | End | ||
+ | Nella Classe secondaria, chiamata "CAlsa", vi sarà invece il seguente codice: | ||
+ | Private handle As Pointer | ||
+ | Private id As Integer | ||
+ | Private s_port As Integer | ||
+ | Private que As Integer | ||
+ | Private dclient As Byte | ||
+ | Private dport As Byte | ||
+ | |||
+ | |||
+ | Library "libasound:2.0.0" | ||
+ | |||
+ | Public Struct <Font Color= #B22222>snd_seq_event_t</font> | ||
+ | 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 | End Struct | ||
− | + | ||
Private Const SND_SEQ_OPEN_OUTPUT As Integer = 1 | Private Const SND_SEQ_OPEN_OUTPUT As Integer = 1 | ||
− | Private Const SND_SEQ_PORT_CAP_READ 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_MIDI_GENERIC As Integer = 2 | ||
Private Const SND_SEQ_PORT_TYPE_APPLICATION As Integer = 1048576 | 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, SND_SEQ_EVENT_CHANPRESS, | |
− | Private Enum SND_SEQ_EVENT_NOTEON = 6, SND_SEQ_EVENT_NOTEOFF, SND_SEQ_EVENT_KEYPRESS, SND_SEQ_EVENT_CONTROLLER = 10, SND_SEQ_EVENT_PGMCHANGE | + | SND_SEQ_EVENT_PITCHBEND, SND_SEQ_EVENT_START = 30, SND_SEQ_EVENT_STOP = 32 |
− | + | ||
− | <FONT Color=gray>' ''int snd_seq_open (snd_seq_t ** | + | <FONT Color=gray>' ''int snd_seq_open (snd_seq_t **seqp, const char * name, int streams, int mode)'' |
' ''Open the ALSA sequencer.''</font> | ' ''Open the ALSA sequencer.''</font> | ||
− | Private Extern snd_seq_open( | + | Private Extern snd_seq_open(seqp As Pointer, name As String, streams As Integer, mode As Integer) As Integer |
− | <FONT Color=gray>' ''const char * | + | <FONT Color=gray>' ''int snd_seq_set_client_name (snd_seq_t* seq, const char* name)'' |
− | ' '' | + | ' ''Set client name.''</font> |
− | Private Extern | + | Private Extern snd_seq_set_client_name(seq As Pointer, name As String) As Integer |
− | <FONT Color=gray>' ''int snd_seq_client_id (snd_seq_t *seq)'' | + | <FONT Color=gray>' ''int snd_seq_client_id (snd_seq_t * seq)'' |
' ''Get the client id.''</font> | ' ''Get the client id.''</font> | ||
Private Extern snd_seq_client_id(seq As Pointer) As Integer | 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)'' |
− | ' '' | + | ' ''Creates a port with the given capability and type bits.''</font> |
Private Extern snd_seq_create_simple_port(seq As Pointer, name As String, caps As Integer, type As Integer) As Integer | 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 * | + | <FONT Color=gray>' ''int snd_seq_alloc_queue (snd_seq_t * seq)'' |
' ''Allocate a queue.''</font> | ' ''Allocate a queue.''</font> | ||
− | Private Extern snd_seq_alloc_queue( | + | Private Extern snd_seq_alloc_queue(seq As Pointer) As Integer |
− | <FONT Color=gray>' ''int | + | <FONT Color=gray>' ''int snd_seq_control_queue(snd_seq_t * seq, int q, int type, int value, snd_seq_event_t * ev)'' |
− | ' '' | + | ' ''Queue controls - start/stop/continue.''</font> |
− | Private Extern | + | Private Extern snd_seq_control_queue(seq As Pointer, q As Integer, type As Integer, value As Integer, ev As Pointer) As Integer |
+ | |||
+ | <FONT Color=gray>' ''int snd_seq_event_output_buffer (snd_seq_t * seq, snd_seq_event_t *ev)'' | ||
+ | ' ''Output an event.''</font> | ||
+ | Private Extern snd_seq_event_output_buffer(seq As Pointer, ev As Snd_seq_event_t) As Integer | ||
− | |||
− | |||
− | |||
− | |||
<FONT Color=gray>' ''int snd_seq_drain_output (snd_seq_t * seq)'' | <FONT Color=gray>' ''int snd_seq_drain_output (snd_seq_t * seq)'' | ||
' ''Drain output buffer to sequencer.''</font> | ' ''Drain output buffer to sequencer.''</font> | ||
Private Extern snd_seq_drain_output(seq As Pointer) As Integer | Private Extern snd_seq_drain_output(seq As Pointer) As Integer | ||
− | + | ||
− | <FONT Color=gray>' ''int snd_seq_close (snd_seq_t * | + | <FONT Color=gray>' ''const char * snd_strerror (int errnum)'' |
+ | ' ''Returns the message for an error code.''</font> | ||
+ | Private Extern snd_strerror(errnum As Integer) As String | ||
+ | |||
+ | <FONT Color=gray>' ''int snd_seq_close (snd_seq_t * seq)'' | ||
' ''Close the sequencer.''</font> | ' ''Close the sequencer.''</font> | ||
− | Private Extern snd_seq_close( | + | Private Extern snd_seq_close(seq As Pointer) As Integer |
+ | |||
+ | |||
+ | Public Function AlsaIni(nome As String) As String | ||
+ | |||
+ | Dim err As Integer | ||
+ | |||
+ | err = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_OUTPUT, 0) | ||
+ | Print "Apertura del subsistema 'seq' di ALSA = "; IIf(err == 0, "corretta !", "errata !") | ||
+ | If err < 0 Then error.RAISE("Errore: " & snd_strerror(err)) | ||
+ | |||
+ | snd_seq_set_client_name(handle, nome) | ||
+ | id = snd_seq_client_id(handle) | ||
+ | Print "Alsa ClientID = "; id | ||
+ | |||
+ | s_port = snd_seq_create_simple_port(handle, "Uscita", SND_SEQ_PORT_CAP_READ, SND_SEQ_PORT_TYPE_MIDI_GENERIC + SND_SEQ_PORT_TYPE_APPLICATION) | ||
+ | Print "Porta del programma = "; s_port | ||
+ | If s_port < 0 Then error.Raise("Errore: " & snd_strerror(s_port)) | ||
+ | |||
+ | que = snd_seq_alloc_queue(handle) | ||
+ | If que < 0 Then error.Raise("Errore: " & snd_strerror(que)) | ||
+ | |||
+ | Return CStr(id) & ":" & CStr(s_port) | ||
+ | |||
+ | End | ||
+ | |||
+ | |||
+ | Public Procedure ImpostaDispositivo(client As Integer, port As Integer) | ||
+ | |||
+ | dclient = client | ||
+ | dport = port | ||
+ | End | ||
+ | |||
+ | |||
+ | Public Procedure AvvioCoda() | ||
+ | |||
+ | Dim err As Integer | ||
+ | |||
+ | err = snd_seq_control_queue(handle, que, SND_SEQ_EVENT_START, 0, 0) | ||
+ | If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) | ||
+ | |||
+ | End | ||
+ | |||
+ | |||
+ | Public Procedure ControlChange(flg As Byte, sec As Integer, canale As Byte, prm As Integer, value As Integer) | ||
+ | |||
+ | Dim ev_midi As New Snd_seq_event_t | ||
+ | Dim err As Integer | ||
+ | |||
+ | With ev_midi | ||
+ | .type = SND_SEQ_EVENT_CONTROLLER | ||
+ | .flags = flg | ||
+ | .tick_o_<Font Color= #B22222>tv_sec</font> = sec | ||
+ | .queue = que | ||
+ | .source_client = id | ||
+ | .source_port = s_port | ||
+ | .dest_client = dclient | ||
+ | .dest_port = dport | ||
+ | .channel = canale | ||
+ | .param = prm | ||
+ | .value = value | ||
+ | End With | ||
+ | |||
+ | err = snd_seq_event_output_buffer(handle, ev_midi) | ||
+ | If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) | ||
+ | |||
+ | End | ||
+ | |||
+ | |||
+ | Public Procedure ProgramChange(flg As Byte, sec As Integer, canale As Byte, non_usato As Byte, value As Integer) | ||
+ | |||
+ | Dim err As Integer | ||
+ | Dim ev_midi As New Snd_seq_event_t | ||
+ | |||
+ | With ev_midi | ||
+ | .type = SND_SEQ_EVENT_PGMCHANGE | ||
+ | .flags = flg | ||
+ | .tick_o_<Font Color= #B22222>tv_sec</font> = sec | ||
+ | .queue = que | ||
+ | .source_client = id | ||
+ | .source_port = s_port | ||
+ | .dest_client = dclient | ||
+ | .dest_port = dport | ||
+ | .channel = canale | ||
+ | .value = value | ||
+ | End With | ||
+ | |||
+ | err = snd_seq_event_output_buffer(handle, ev_midi) | ||
+ | If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) | ||
+ | |||
+ | End | ||
+ | |||
+ | |||
+ | Public Procedure NoteON(flg As Byte, sec As Integer, canale As Byte, nota As Byte, vel As Byte) | ||
+ | |||
+ | Dim ev_midi As New Snd_seq_event_t | ||
+ | Dim err As Integer | ||
+ | |||
+ | With ev_midi | ||
+ | .type = SND_SEQ_EVENT_NOTEON | ||
+ | .flags = flg | ||
+ | .tick_o_<Font Color= #B22222>tv_sec</font> = sec | ||
+ | .queue = que | ||
+ | .source_client = id | ||
+ | .source_port = s_port | ||
+ | .dest_client = dclient | ||
+ | .dest_port = dport | ||
+ | .channel = canale | ||
+ | .note = nota | ||
+ | .velocity = vel | ||
+ | End With | ||
+ | |||
+ | err = snd_seq_event_output_buffer(handle, ev_midi) | ||
+ | If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) | ||
+ | |||
+ | End | ||
+ | |||
+ | |||
+ | Public Procedure NoteOFF(flg As Byte, sec As Integer, canale As Byte, nota As Byte, vel As Byte) | ||
+ | |||
+ | Dim ev_midi As New Snd_seq_event_t | ||
+ | Dim err As Integer | ||
+ | |||
+ | With ev_midi | ||
+ | .type = SND_SEQ_EVENT_NOTEOFF | ||
+ | .flags = flg | ||
+ | .tick_o_<Font Color= #B22222>tv_sec</font> = sec | ||
+ | .queue = que | ||
+ | .source_client = id | ||
+ | .source_port = s_port | ||
+ | .dest_client = dclient | ||
+ | .dest_port = dport | ||
+ | .channel = canale | ||
+ | .note = nota | ||
+ | .velocity = vel | ||
+ | End With | ||
+ | |||
+ | err = snd_seq_event_output_buffer(handle, ev_midi) | ||
+ | If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) | ||
+ | |||
+ | End | ||
+ | |||
+ | |||
+ | Public Procedure Flush() | ||
+ | |||
+ | Dim err As Integer | ||
+ | |||
+ | err = snd_seq_drain_output(handle) | ||
+ | If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) | ||
+ | |||
+ | End | ||
+ | |||
+ | |||
+ | Public Procedure StopCoda() | ||
+ | |||
+ | Dim err As Integer | ||
− | + | err = snd_seq_control_queue(handle, que, SND_SEQ_EVENT_STOP, 0, 0) | |
− | + | If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) | |
− | + | ||
− | + | snd_seq_close(handle) | |
− | + | ||
− | + | End | |
− | + | ||
− | + | ====Esempio in modalità temporizzazione in formato orario usando anche i nanosecondi==== | |
− | + | Di seguito ostriamo un semplice esempio simile al precedente, ma usando anche il membro ".tv_nsec" della Struttura ''snd_seq_event_t''. | |
− | + | <BR>La Classe principale sarà la seguente: | |
− | + | Private Const id_dev As Integer = 128 ' 14 = midi out oppure 128 (solitamente) = softsynth | |
− | + | Private Const p_dev As Integer = 0 ' porta del Client destinatario dei dati Midi: solitamente 0 | |
− | + | Public alsa As CAlsa ' Classe che incapsula le funzioni ALSA | |
− | + | ||
− | + | ||
− | + | Public Sub Form_Open() | |
− | + | ||
− | + | With alsa = New CAlsa As "Alsa" | |
− | + | Me.Title = "|| Client ALSA " & .AlsaIni("Programma di prova") | |
− | . | + | .ImpostaDispositivo(id_dev, p_dev) |
− | |||
− | . | ||
End With | End With | ||
− | + | ||
− | <FONT Color=gray>' '' | + | End |
− | + | ||
− | + | ||
− | + | Public Sub ToggleButton1_Click() | |
− | <FONT Color=gray>' '' | + | |
− | + | Dim note As Byte[] = [60, 62, 64, 65, 67, 69, 71, 72] | |
− | <FONT Color=gray>' '' | + | Dim b As Byte |
− | + | Dim ns As Integer | |
− | <FONT Color=gray>' '' | + | |
− | + | If ToggleButton1.Value Then | |
− | + | With alsa | |
− | + | .AvvioCoda() | |
− | + | .ControlChange(1, 0, 7, 50) | |
+ | .ProgramChange(1, 0, 44) | ||
+ | For b = 0 To note.Max | ||
+ | If b == 3 Then ns = 750000000 | ||
+ | <FONT Color=gray>' ''Imposta la nota Midi da eseguire.'' | ||
+ | ' ''Aggiunge 750000000 nanosecondi.''</font> | ||
+ | .NoteON(1, b, ns, note[b], 100) | ||
+ | ns = 0 | ||
+ | <FONT Color=gray>' ''Imposta la nota Midi da silenziare dall'inizio della "Coda".''</font> | ||
+ | ' ''Alla terza nota aggiunge 750000000 nanosecondi.''</font> | ||
+ | If b == 2 Then ns = 750000000 | ||
+ | .NoteOFF(1, b + 1, ns, note[b], 0) | ||
+ | ns = 0 | ||
+ | Next | ||
+ | .Flush() | ||
+ | End With | ||
+ | Else | ||
+ | alsa.StopCoda() | ||
+ | Me.Close | ||
+ | Endif | ||
+ | |||
+ | End | ||
+ | La Classe secondaria per la gestione delle risorse di ALSA: | ||
+ | Private handle As Pointer | ||
+ | Private id As Integer | ||
+ | Private s_port As Integer | ||
+ | Private que As Integer | ||
+ | Private dclient As Byte | ||
+ | Private dport As Byte | ||
+ | |||
+ | |||
+ | Library "libasound:2.0.0" | ||
+ | |||
+ | Public Struct <Font Color= #B22222>snd_seq_event_t</font> | ||
+ | type As Byte | ||
+ | flags As Byte | ||
+ | tag As Byte | ||
+ | queue As Byte | ||
+ | tick_o_tv_sec As Integer | ||
+ | <Font Color= #B22222>tv_nsec</font> 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, SND_SEQ_EVENT_CHANPRESS, | ||
+ | SND_SEQ_EVENT_PITCHBEND, SND_SEQ_EVENT_START = 30, SND_SEQ_EVENT_STOP = 32 | ||
+ | |||
+ | <FONT Color=gray>' ''int snd_seq_open (snd_seq_t **seqp, const char * name, int streams, int mode)'' | ||
+ | ' ''Open the ALSA sequencer.''</font> | ||
+ | Private Extern snd_seq_open(seqp As Pointer, name As String, streams As Integer, mode As Integer) As Integer | ||
+ | |||
+ | <FONT Color=gray>' ''int snd_seq_set_client_name (snd_seq_t* seq, const char* name)'' | ||
+ | ' ''Set client name.''</font> | ||
+ | Private Extern snd_seq_set_client_name(seq As Pointer, name As String) As Integer | ||
+ | |||
+ | <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)'' | ||
+ | ' ''Creates a port with the given capability and type bits.''</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 * seq)'' | ||
+ | ' ''Allocate a queue.''</font> | ||
+ | Private Extern snd_seq_alloc_queue(seq As Pointer) As Integer | ||
+ | |||
+ | <FONT Color=gray>' ''int snd_seq_control_queue(snd_seq_t * seq, int q, int type, int value, snd_seq_event_t * ev)'' | ||
+ | ' ''Queue controls - start/stop/continue.''</font> | ||
+ | Private Extern snd_seq_control_queue(seq As Pointer, q As Integer, type As Integer, value As Integer, ev As Pointer) As Integer | ||
− | ''' | + | <FONT Color=gray>' ''int snd_seq_event_output_buffer (snd_seq_t * seq, snd_seq_event_t *ev)'' |
+ | ' ''Output an event.''</font> | ||
+ | Private Extern snd_seq_event_output_buffer(seq As Pointer, ev As Snd_seq_event_t) As Integer | ||
+ | |||
+ | <FONT Color=gray>' ''int snd_seq_drain_output (snd_seq_t * seq)'' | ||
+ | ' ''Drain output buffer to sequencer.''</font> | ||
+ | Private Extern snd_seq_drain_output(seq As Pointer) As Integer | ||
+ | |||
+ | <FONT Color=gray>' ''const char * snd_strerror (int errnum)'' | ||
+ | ' ''Returns the message for an error code.''</font> | ||
+ | Private Extern snd_strerror(errnum As Integer) As String | ||
+ | |||
+ | <FONT Color=gray>' ''int snd_seq_close (snd_seq_t * seq)'' | ||
+ | ' ''Close the sequencer.''</font> | ||
+ | Private Extern snd_seq_close(seq As Pointer) As Integer | ||
+ | |||
+ | |||
+ | Public Function AlsaIni(nome As String) As String | ||
+ | |||
+ | Dim err As Integer | ||
+ | |||
+ | err = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_OUTPUT, 0) | ||
+ | Print "Apertura del subsistema 'seq' di ALSA = "; IIf(err == 0, "corretta !", "errata !") | ||
+ | If err < 0 Then error.RAISE("Errore: " & snd_strerror(err)) | ||
+ | |||
+ | snd_seq_set_client_name(handle, nome) | ||
+ | id = snd_seq_client_id(handle) | ||
+ | Print "Alsa ClientID = "; id | ||
+ | |||
+ | s_port = snd_seq_create_simple_port(handle, "Uscita", SND_SEQ_PORT_CAP_READ, SND_SEQ_PORT_TYPE_MIDI_GENERIC + SND_SEQ_PORT_TYPE_APPLICATION) | ||
+ | Print "Porta del programma = "; s_port | ||
+ | If s_port < 0 Then error.Raise("Errore: " & snd_strerror(s_port)) | ||
+ | |||
+ | que = snd_seq_alloc_queue(handle) | ||
+ | If que < 0 Then error.Raise("Errore: " & snd_strerror(que)) | ||
+ | |||
+ | Return CStr(id) & ":" & CStr(s_port) | ||
− | + | End | |
− | + | ||
− | + | Public Procedure ImpostaDispositivo(client As Integer, port As Integer) | |
− | + | ||
− | + | dclient = client | |
+ | dport = port | ||
− | + | End | |
− | + | ||
− | + | ||
− | If | + | Public Procedure AvvioCoda() |
− | + | ||
− | + | Dim err As Integer | |
− | + | ||
− | + | err = snd_seq_control_queue(handle, que, SND_SEQ_EVENT_START, 0, 0) | |
− | + | If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) | |
− | If | + | |
− | + | End | |
− | + | ||
− | + | ||
− | + | Public Procedure ControlChange(flg As Byte, sec As Integer, prm As Integer, value As Integer) | |
+ | |||
+ | Dim ev_midi As New Snd_seq_event_t | ||
+ | Dim err As Integer | ||
+ | |||
+ | With ev_midi | ||
+ | .type = SND_SEQ_EVENT_CONTROLLER | ||
+ | .flags = flg | ||
+ | .tick_o_tv_sec = sec | ||
+ | .queue = que | ||
+ | .source_client = id | ||
+ | .source_port = s_port | ||
+ | .dest_client = dclient | ||
+ | .dest_port = dport | ||
+ | .param = prm | ||
+ | .value = value | ||
+ | End With | ||
+ | |||
+ | err = snd_seq_event_output_buffer(handle, ev_midi) | ||
+ | If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) | ||
+ | |||
+ | End | ||
+ | |||
+ | |||
+ | Public Procedure ProgramChange(flg As Byte, sec As Integer, value As Integer) | ||
+ | |||
+ | Dim err As Integer | ||
+ | Dim ev_midi As New Snd_seq_event_t | ||
− | + | With ev_midi | |
− | |||
− | With | ||
− | |||
.type = SND_SEQ_EVENT_PGMCHANGE | .type = SND_SEQ_EVENT_PGMCHANGE | ||
− | + | .flags = flg | |
− | .value = | + | .tick_o_tv_sec = sec |
+ | .queue = que | ||
+ | .source_client = id | ||
+ | .source_port = s_port | ||
+ | .dest_client = dclient | ||
+ | .dest_port = dport | ||
+ | .value = value | ||
End With | End With | ||
− | |||
− | |||
− | |||
− | |||
− | + | err = snd_seq_event_output_buffer(handle, ev_midi) | |
+ | If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) | ||
+ | |||
+ | End | ||
+ | |||
− | + | Public Procedure NoteON(flg As Byte, sec As Integer, ns As Integer, nota As Byte, vel As Byte) | |
− | + | ||
+ | Dim ev_midi As New Snd_seq_event_t | ||
+ | Dim err As Integer | ||
+ | |||
+ | With ev_midi | ||
.type = SND_SEQ_EVENT_NOTEON | .type = SND_SEQ_EVENT_NOTEON | ||
− | + | .flags = flg | |
+ | .tick_o_tv_sec = sec | ||
+ | <FONT Color=#B22222>.tv_nsec = ns</font> | ||
+ | .queue = que | ||
+ | .source_client = id | ||
+ | .source_port = s_port | ||
+ | .dest_client = dclient | ||
+ | .dest_port = dport | ||
.note = nota | .note = nota | ||
− | + | .velocity = vel | |
− | .velocity = | ||
End With | End With | ||
− | |||
− | |||
− | |||
− | |||
− | + | err = snd_seq_event_output_buffer(handle, ev_midi) | |
+ | If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) | ||
+ | |||
+ | End | ||
+ | |||
+ | |||
+ | Public Procedure NoteOFF(flg As Byte, sec As Integer, ns As Integer, nota As Byte, vel As Byte) | ||
− | + | Dim ev_midi As New Snd_seq_event_t | |
− | + | Dim err As Integer | |
+ | |||
+ | With ev_midi | ||
.type = SND_SEQ_EVENT_NOTEOFF | .type = SND_SEQ_EVENT_NOTEOFF | ||
− | + | .flags = flg | |
+ | .tick_o_tv_sec = sec | ||
+ | <FONT Color=#B22222>.tv_nsec = ns</font> | ||
+ | .queue = que | ||
+ | .source_client = id | ||
+ | .source_port = s_port | ||
+ | .dest_client = dclient | ||
+ | .dest_port = dport | ||
.note = nota | .note = nota | ||
− | + | .velocity = vel | |
− | .velocity = | ||
End With | End With | ||
− | |||
− | |||
− | |||
− | |||
− | + | err = snd_seq_event_output_buffer(handle, ev_midi) | |
+ | If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) | ||
+ | |||
+ | End | ||
+ | |||
+ | |||
+ | Public Procedure Flush() | ||
+ | |||
+ | Dim err As Integer | ||
+ | |||
+ | err = snd_seq_drain_output(handle) | ||
+ | If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) | ||
+ | |||
+ | End | ||
+ | |||
+ | |||
+ | Public Procedure StopCoda() | ||
+ | |||
+ | Dim err As Integer | ||
− | + | err = snd_seq_control_queue(handle, que, SND_SEQ_EVENT_STOP, 0, 0) | |
− | + | If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) | |
− | |||
− | |||
− | |||
− | + | snd_seq_close(handle) | |
− | + | End | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− |
Versione attuale delle 15:21, 19 giu 2024
Indice
Preambolo
In questa pagina verrà preso in considerazione l'uso delle Strutture per l'invio degli Eventi Midi ad ALSA.
Le Strutture, introdotte in Gambas con la versione 3.0, possono essere utilizzate per scrivere e gestire i messaggi Midi. Infatti dichiarando una Struttura (Struct) ci si riserva una quantità di memoria, definita dal tipo (byte, short, integer, etc.) dei valori dichiarati in ciascun membro della Struttura.
Dichiarando poi una variabile del tipo di quella Struttura, si ha automaticamente una zona di memoria utilizzabile.
Scrittura dei dati dei messaggi Midi nelle Strutture
Inseriremo una Struttura modello all'inizio della classe secondaria CAlsa.class. Porremo quindi, quali campi della struttura-tipo, i dati necessari per la definizione degli eventi Midi, come richiesti da ALSA, attribuendo a ciascun campo il tipo di dato desiderato da ALSA nel rispetto dell'ordine previsto dei dati, nonché del numero del byte al quale ciascun dato dovrà essere assegnato. Inseriremo non solo i dati comuni per tutti i messaggi Midi, ma anche quelli previsti per gli specifici messaggi:
In rosso sono rappresentati i membri che contengono dati identici per qualunque Evento Midi ALSA; In verde sono rappresentati i membri che contengono dati relativi allo specifico Evento Midi ALSA. struct snd_seq_event_t { Public Struct Snd_seq_event_t snd_seq_event_type_t type type As Byte byte di indice 0 unsigned char flags flags As Byte unsigned char tag tag As Byte unsigned char queue queue As Byte snd_seq_timestamp_t time ↳ snd_seq_tick_time_t / snd_seq_real_time_t ↳ tick ↳ tv_sec tick_o_tv_sec As Integer ↳ tv_nsec tv_nsec As Integer snd_seq_addr_t source ↳ unsigned char client source_client As Byte ↳ unsigned char port source_port As Byte snd_seq_addr_t dest ↳ unsigned char client dest_client As Byte ↳ unsigned char port dest_port As Byte snd_seq_ev_note_t note ↳ unsigned char channel channel As Byte byte di indice 16 ↳ unsigned char note note As Byte byte di indice 17 ↳ unsigned char velocity velocity As Byte byte di indice 18 ↳ unsigned char off_velocity off_velocity As Byte byte di indice 19 unsigned int param param As Integer byte di indice 20 signed int value value As Integer byte di indice 24 } End Struct
I dati appartenenti agli specifici Messaggi Midi non vengono utilizzati tutti insieme da ciascun Evento Midi ALSA, ma solo alcuni di essi a seconda dello specifico Messaggio. Ovviamente nella Struttura-tipo essi saranno comunque posti e dichiarati, al fine di consentirne l'uso.
In Gambas si istanzierà una variabile del tipo della Struttura snd_seq_event_t, sopra descritta, con la quale gestire gli specifici Eventi Midi di ALSA, assegnando a ciascun membro (sia quelli comuni, sia quelli specifici di ogni tipo di Evento Midi ALSA) il relativo valore. Ovviamente si potrà evitare di richiamare i membri che eventualmente non devono essere lasciati con valore zero.
Riassunto delle fasi di creazione, allocamento e invio degli Eventi Midi ad ALSA
Prima di mostrare dei codici esemplificativi sulle varie modalità di invio degli'Eventi Midi al sistema ALSA, è opportuno ricordare quanto descritto nela capitolo precedente "ALSA e Gambas - Gestione dei Messaggi Midi standard".
Dunque possiamo elencare per sommicapi le seguenti fasi da seguire:
1) comporre l'Evento Midi ALSA nei membri essenziali della sua Struttura snd_seq_event_t, ed in particolare attribuire la temporizzazione mediante il "Timestamp" in "tick Midi" o in "real time";
2) allocare la Coda degli Eventi Midi ALSA con la funzione esterna snd_seq_alloc_queue();
3) avviare il controllo della Coda degli Eventi Midi ALSA con la funzione esterna snd_seq_control_queue(seq, q, type, value, 0, ev), passandole come 3° argomento la Costante di ALSA "SND_SEQ_EVENT_START";
4) accodare gli Eventi Midi nel buffer mediante la funzione esterna snd_seq_event_output_buffer();
5) inviare al sistema ALSA mediante la funzione esterna snd_seq_drain_output() l'intera Coda degli Eventi Midi, memorizzati nel buffer, affinche siano processati;
6) arrestare la Coda degli Eventi Midi ALSA richiamando la funzione esterna snd_seq_control_queue(seq, q, type, value, 0, ev), passandole però questa volta come suo 3° argomento la Costante di ALSA "SND_SEQ_EVENT_STOP".
Esempi di codice
1° esempio pratico
Nel seguente esempio in ambiente grafico avremo la Classe principale "FMain.Class" e una Classe secondaria, che incapsula le risorse di ALSA da utilizzare.
Dichiareremo una Struttura, chiamata snd_seq_event_t, che farà da modello alla particolare variabile di quel tipo di Struttura da usare per la organizzazione di ciascun Evento Midi ALSA.
In questo esempio sarà effettuato un invio diretto e immediato di un singolo Evento Midi per volta: cliccando su un qualunque tasto della tastiera del computer, si invierà un Evento Midi "NoteON"; rilasciando il tasto si invierà un Evento Midi "NoteOFF", per far cesssare l'esecuzione della nota Midi prima avviata.
Saranno gestiti comunque in apertura del programma anche gli Eventi Midi "Control Change" e "Program Change".
Riguardo al Timestamp di ogni Evento Midi ALSA inviato, trattandosi di invio singolo e diretto con esecuzione immediata è indifferente la modalità scelta: il membro time della predetta Struttura snd_seq_event_t relativo al Timestamp del singolo Evento Midi sarà posto a zero.
Dunque avremo la Classe principale "FMain.Class":
Private Const id_dev As Integer = 128 ' 14 = midi out oppure 128 (solitamente) = softsynth Private Const p_dev As Integer = 0 ' porta del Client destinatario dei dati Midi: solitamente 0 Public alsa As CAlsa ' Classe che incapsula le funzioni ALSA Private bo As Boolean Public Sub Form_Open() ' Crea ("istanzia") un Oggetto della Classe "CAlsa" per poterla usare: With alsa = New CAlsa As "Alsa" ' Apre ALSA e assegna un nome al Client: Me.Title = "|| Client ALSA " & .AlsaIni("Programma di prova") ' Sceglie la periferica (Softsynth) su cui suonare: .ImpostaDispositivo(id_dev, p_dev) End With ' Imposta il Volume gestendo l'Evento Midi "Control Change": alsa.ControlChange(0, 7, 64) ' Imposta il suono dello strumento musicale gestendo l'Evento Midi "Program Change": alsa.ProgramChange(0, 0, 44) End Public Sub Form_KeyPress() If bo Then Return ' Imposta la nota midi da eseguire gestendo l'Evento Midi "Note-ON": alsa.NoteON(0, 64, 100) alsa.Flush() bo = True End Public Sub Form_KeyRelease() ' Imposta la nota midi da eseguire gestendo l'Evento Midi "Note-OFF": alsa.NoteOFF(0, 64, 0) alsa.Flush() bo = False End Public Sub Form_Close() alsa.Chiude() End
Nella Classe secondaria, chiamata "CAlsa", avremo il seguente codice:
Private handle As Pointer Private id As Integer Private s_port As Integer Private que As Integer Private dclient As Byte Private dport As Byte 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, SND_SEQ_EVENT_CHANPRESS, SND_SEQ_EVENT_PITCHBEND ' int snd_seq_open (snd_seq_t **seqp, const char * name, int streams, int mode) ' Open the ALSA sequencer. Private Extern snd_seq_open(seqp 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(seq 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(seq As Pointer) As Integer ' int snd_seq_create_simple_port (snd_seq_t* seq, const char* name, unsigned int caps, unsigned int type) ' Creates a port with the given capability and type bits. 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 * seq) ' Allocate a queue. Private Extern snd_seq_alloc_queue(seq As Pointer) As Integer ' int snd_seq_event_output_buffer (snd_seq_t * seq, snd_seq_event_t *ev) ' Output an event. Private Extern snd_seq_event_output_buffer(seq As Pointer, ev As Snd_seq_event_t) As Integer ' 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 ' 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_close (snd_seq_t * seq) ' Close the sequencer. Private Extern snd_seq_close(seq As Pointer) As Integer Public Function AlsaIni(nome As String) As String Dim err As Integer err = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_OUTPUT, 0) Print "Apertura del subsistema 'seq' di ALSA = "; IIf(err == 0, "corretta !", "errata !") If err < 0 Then error.RAISE("Errore: " & snd_strerror(err)) snd_seq_set_client_name(handle, nome) id = snd_seq_client_id(handle) Print "Alsa ClientID = "; id s_port = snd_seq_create_simple_port(handle, "Uscita", SND_SEQ_PORT_CAP_READ, SND_SEQ_PORT_TYPE_MIDI_GENERIC + SND_SEQ_PORT_TYPE_APPLICATION) Print "Porta del programma = "; s_port If s_port < 0 Then error.Raise("Errore: " & snd_strerror(s_port)) que = snd_seq_alloc_queue(handle) If que < 0 Then error.Raise("Errore: " & snd_strerror(que)) Return CStr(id) & ":" & CStr(s_port) End Public Procedure ImpostaDispositivo(client As Integer, port As Integer) dclient = client dport = port End Public Procedure ControlChange(canale As Byte, prm As Integer, value As Integer) Dim ev_midi As New Snd_seq_event_t Dim err As Integer With ev_midi .type = SND_SEQ_EVENT_CONTROLLER .queue = que .source_client = id .source_port = s_port .dest_client = dclient .dest_port = dport .channel = canale .param = prm .value = value End With err = snd_seq_event_output_buffer(handle, ev_midi) If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) End Public Procedure ProgramChange(canale As Byte, non_usato As Byte, value As Integer) Dim err As Integer Dim ev_midi As New Snd_seq_event_t With ev_midi .type = SND_SEQ_EVENT_PGMCHANGE .queue = que .source_client = id .source_port = s_port .dest_client = dclient .dest_port = dport .channel = canale .value = value End With err = snd_seq_event_output_buffer(handle, ev_midi) If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) End Public Procedure NoteON(canale As Byte, nota As Byte, vel As Byte) Dim ev_midi As New Snd_seq_event_t Dim err As Integer With ev_midi .type = SND_SEQ_EVENT_NOTEON .queue = que .source_client = id .source_port = s_port .dest_client = dclient .dest_port = dport .channel = canale .note = nota .velocity = vel End With err = snd_seq_event_output_buffer(handle, ev_midi) If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) End Public Procedure NoteOFF(canale As Byte, nota As Byte, vel As Byte) Dim ev_midi As New Snd_seq_event_t Dim err As Integer With ev_midi .type = SND_SEQ_EVENT_NOTEOFF .queue = que .source_client = id .source_port = s_port .dest_client = dclient .dest_port = dport .channel = canale .note = nota .velocity = vel End With err = snd_seq_event_output_buffer(handle, ev_midi) If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) End Public Procedure Flush() Dim err As Integer err = snd_seq_drain_output(handle) If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) End Public Procedure Chiude() snd_seq_close(handle) End
2° esempio pratico
Di seguito un programma simile al precedente, nel quale si farà uso di uno Slider per agire sul Messaggio Midi Pitch Bend.
Nella Classe principale avremo il seguente codice:
Private Const id_dev As Integer = 128 ' 14 = midi out oppure 128 (solitamente) = softsynth Private Const p_dev As Integer = 0 ' porta del Client destinatario dei dati Midi: solitamente 0 Public alsa As CAlsa ' Classe che incapsula le funzioni ALSA Private bo As Boolean Public Sub Form_Open() With Slider1 .MinValue = -8192 .MaxValue = 8192 End With ' Crea ("istanzia") un Oggetto della Classe "CAlsa" per poterla usare: With alsa = New CAlsa As "Alsa" ' Apre ALSA e assegna un nome al Client: Me.Title = "|| Client ALSA " & .AlsaIni("Programma di prova") ' Sceglie la periferica (Softsynth) su cui suonare: .ImpostaDispositivo(id_dev, p_dev) End With ' Imposta il Volume gestendo l'Evento Midi "Control Change": alsa.ControlChange(0, 7, 64) ' Imposta il suono dello strumento musicale gestendo l'Evento Midi "Program Change": alsa.ProgramChange(0, 0, 44) End Public Sub Form_KeyPress() If bo Then Return ' Imposta la nota midi da eseguire gestendo l'Evento Midi "Note-ON": alsa.NoteON(0, 64, 100) alsa.Flush() bo = True End Public Sub Form_KeyRelease() ' Imposta la nota midi da eseguire gestendo l'Evento Midi "Note-OFF": alsa.NoteOFF(0, 64, 0) alsa.Flush() bo = False End Public Sub Slider1_Change() alsa.PitchBend(0, 0, Slider1.Value) alsa.Flush() End Public Sub Form_Close() alsa.Chiude() End
Nella Classe secondaria, chiamata "CAlsa", avremo il seguente codice:
Private handle As Pointer Private id As Integer Private s_port As Integer Private que As Integer Private dclient As Byte Private dport As Byte 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, SND_SEQ_EVENT_CHANPRESS, SND_SEQ_EVENT_PITCHBEND ' int snd_seq_open (snd_seq_t **seqp, const char * name, int streams, int mode) ' Open the ALSA sequencer. Private Extern snd_seq_open(seqp 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(seq 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(seq As Pointer) As Integer ' int snd_seq_create_simple_port (snd_seq_t* seq, const char* name, unsigned int caps, unsigned int type) ' Creates a port with the given capability and type bits. 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 * seq) ' Allocate a queue. Private Extern snd_seq_alloc_queue(seq As Pointer) As Integer ' int snd_seq_event_output_buffer (snd_seq_t * seq, snd_seq_event_t *ev) ' Output an event. Private Extern snd_seq_event_output_buffer(seq As Pointer, ev As Snd_seq_event_t) As Integer ' 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 ' 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_close (snd_seq_t * seq) ' Close the sequencer. Private Extern snd_seq_close(seq As Pointer) As Integer Public Function AlsaIni(nome As String) As String Dim err As Integer err = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_OUTPUT, 0) Print "Apertura del subsistema 'seq' di ALSA = "; IIf(err == 0, "corretta !", "errata !") If err < 0 Then error.RAISE("Errore: " & snd_strerror(err)) snd_seq_set_client_name(handle, nome) id = snd_seq_client_id(handle) Print "Alsa ClientID = "; id s_port = snd_seq_create_simple_port(handle, "Uscita", SND_SEQ_PORT_CAP_READ, SND_SEQ_PORT_TYPE_MIDI_GENERIC + SND_SEQ_PORT_TYPE_APPLICATION) Print "Porta del programma = "; s_port If s_port < 0 Then error.Raise("Errore: " & snd_strerror(s_port)) que = snd_seq_alloc_queue(handle) If que < 0 Then error.Raise("Errore: " & snd_strerror(que)) Return CStr(id) & ":" & CStr(s_port) End Public Procedure ImpostaDispositivo(client As Integer, port As Integer) dclient = client dport = port End Public Procedure ControlChange(canale As Byte, prm As Integer, value As Integer) Dim ev_midi As New Snd_seq_event_t Dim err As Integer With ev_midi .type = SND_SEQ_EVENT_CONTROLLER .queue = que .source_client = id .source_port = s_port .dest_client = dclient .dest_port = dport .channel = canale .param = prm .value = value End With err = snd_seq_event_output_buffer(handle, ev_midi) If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) End Public Procedure ProgramChange(canale As Byte, non_usato As Byte, value As Integer) Dim err As Integer Dim ev_midi As New Snd_seq_event_t With ev_midi .type = SND_SEQ_EVENT_PGMCHANGE .queue = que .source_client = id .source_port = s_port .dest_client = dclient .dest_port = dport .channel = canale .value = value End With err = snd_seq_event_output_buffer(handle, ev_midi) If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) End Public Procedure NoteON(canale As Byte, nota As Byte, vel As Byte) Dim ev_midi As New Snd_seq_event_t Dim err As Integer With ev_midi .type = SND_SEQ_EVENT_NOTEON .queue = que .source_client = id .source_port = s_port .dest_client = dclient .dest_port = dport .channel = canale .note = nota .velocity = vel End With err = snd_seq_event_output_buffer(handle, ev_midi) If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) End Public Procedure NoteOFF(canale As Byte, nota As Byte, vel As Byte) Dim ev_midi As New Snd_seq_event_t Dim err As Integer With ev_midi .type = SND_SEQ_EVENT_NOTEOFF .queue = que .source_client = id .source_port = s_port .dest_client = dclient .dest_port = dport .channel = canale .note = nota .velocity = vel End With err = snd_seq_event_output_buffer(handle, ev_midi) If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) End Public Procedure Flush() Dim err As Integer err = snd_seq_drain_output(handle) If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) End Public Procedure PitchBend(canale As Byte, prm As Integer, val As Integer) Dim ev_midi As New Snd_seq_event_t Dim err As Integer With ev_midi .type = SND_SEQ_EVENT_PITCHBEND .queue = que .source_client = id .source_port = s_port .dest_client = dclient .dest_port = dport .channel = canale .param = prm .value = val End With err = snd_seq_event_output_buffer(handle, ev_midi) If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) End Public Procedure Stop() snd_seq_close(handle) End
Esempi con invio di più Eventi Midi ALSA temporizzati in sequenza
Vedremo di seguito due semplici esempi sull'uso del membro flags della Struttura snd_seq_event_t e della temporizzazione in "tick time" e in "real time".
Invio Eventi Midi con temporizzazione in "tick time"
Ricordiamo che la temporizzazione in ALSA nella modalità in "tick" Midi è attivata assegnando al membro flags della snd_seq_event_t la Costante "SND_SEQ_TIME_STAMP_TICK" di ALSA, mentre i valori temporali sono espressi appunto in "tick" Midi al membro tick del membro snd_seq_timestamp_t time della predetta Struttura.
Mostriamo un esempio pratico in ambiente grafico, nel quale cliccando su un "ToggleButton" si avvierà l'invio ad ALSA degli Eventi Midi e il loro processamento. Essi saranno eseguiti secondo la propria temporizzazione impostata nella Struttura snd_seq_event_t costitutiva.
Dunque avremo la Classe principale "FMain.Class":
Private Const id_dev As Integer = 128 ' 14 = midi out oppure 128 (solitamente) = softsynth Private Const p_dev As Integer = 0 ' porta del Client destinatario dei dati Midi: solitamente 0 Public alsa As CAlsa ' Classe che incapsula le funzioni ALSA Public Sub Form_Open() ' Crea ("istanzia") un Oggetto della Classe "CAlsa" per poterla usare: With alsa = New CAlsa As "Alsa" ' Apre ALSA e assegna un nome al Client: Me.Title = "|| Client ALSA " & .AlsaIni("Programma di prova") ' Sceglie la periferica (Softsynth) su cui suonare: .ImpostaDispositivo(id_dev, p_dev) End With End Public Sub ToggleButton1_Click() If ToggleButton1.Value Then With alsa ' Avvia la "Coda" degli Eventi Midi ALSA: .AvvioCoda() ' Imposta il Volume gestendo l'Evento Midi "Control Change": .ControlChange(0, 0, 0, 7, 50) ' Imposta il suono dello strumento musicale gestendo l'Evento Midi "Program Change": .ProgramChange(0, 0, 0, 0, 44) ' Imposta la prima nota Midi da eseguire: .NoteON(0, 0, 0, 64, 100) ' Imposta la nota Midi da silenziare dopo 96 tick dall'inizio della "Coda": .NoteOFF(0, 96, 0, 64, 0) ' Imposta il suono di un altro strumento musicale dopo 150 tick dall'inizio della "Coda" (quindi crea una pausa): .ProgramChange(0, 182, 0, 0, 18) ' Imposta la nota Midi da eseguire dopo 182 tick dall'inizio della "Coda": .NoteON(0, 182, 0, 66, 100) ' Imposta la nota Midi da silenziare dopo 300 tick dall'inizio della "Coda": .NoteOFF(0, 300, 0, 66, 0) ' Imposta il suono di un altro strumento musicale dopo 300 tick dall'inizio della "Coda": .ProgramChange(0, 300, 0, 0, 18) ' Imposta la nota Midi da eseguire dopo 300 tick dall'inizio della "Coda": .NoteON(0, 300, 0, 68, 100) ' Imposta la nota Midi da silenziare dopo 500 tick dall'inizio della "Coda": .NoteOFF(0, 500, 0, 68, 0) ' Dispone infine l'invio di tutti gli Eventi Midi bufferizzati nella "Coda": .Flush() End With Else alsa.StopCoda() Me.Close Endif End
Nella Classe secondaria, chiamata "CAlsa", vi sarà invece il seguente codice:
Private handle As Pointer Private id As Integer Private s_port As Integer Private que As Integer Private dclient As Byte Private dport As Byte 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, SND_SEQ_EVENT_CHANPRESS, SND_SEQ_EVENT_PITCHBEND, SND_SEQ_EVENT_START = 30, SND_SEQ_EVENT_STOP = 32 ' int snd_seq_open (snd_seq_t **seqp, const char * name, int streams, int mode) ' Open the ALSA sequencer. Private Extern snd_seq_open(seqp 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(seq 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(seq As Pointer) As Integer ' int snd_seq_create_simple_port (snd_seq_t* seq, const char* name, unsigned int caps, unsigned int type) ' Creates a port with the given capability and type bits. 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 * seq) ' Allocate a queue. Private Extern snd_seq_alloc_queue(seq As Pointer) As Integer ' int snd_seq_control_queue(snd_seq_t * seq, int q, int type, int value, snd_seq_event_t * ev) ' Queue controls - start/stop/continue. Private Extern snd_seq_control_queue(seq As Pointer, q As Integer, type As Integer, value As Integer, ev As Pointer) As Integer ' int snd_seq_event_output_buffer (snd_seq_t * seq, snd_seq_event_t *ev) ' Output an event. Private Extern snd_seq_event_output_buffer(seq As Pointer, ev As Snd_seq_event_t) As Integer ' 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 ' 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_close (snd_seq_t * seq) ' Close the sequencer. Private Extern snd_seq_close(seq As Pointer) As Integer Public Function AlsaIni(nome As String) As String Dim err As Integer err = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_OUTPUT, 0) Print "Apertura del subsistema 'seq' di ALSA = "; IIf(err == 0, "corretta !", "errata !") If err < 0 Then error.RAISE("Errore: " & snd_strerror(err)) snd_seq_set_client_name(handle, nome) id = snd_seq_client_id(handle) Print "Alsa ClientID = "; id s_port = snd_seq_create_simple_port(handle, "Uscita", SND_SEQ_PORT_CAP_READ, SND_SEQ_PORT_TYPE_MIDI_GENERIC + SND_SEQ_PORT_TYPE_APPLICATION) Print "Porta del programma = "; s_port If s_port < 0 Then error.Raise("Errore: " & snd_strerror(s_port)) que = snd_seq_alloc_queue(handle) If que < 0 Then error.Raise("Errore: " & snd_strerror(que)) Return CStr(id) & ":" & CStr(s_port) End Public Procedure ImpostaDispositivo(client As Integer, port As Integer) dclient = client dport = port End Public Procedure AvvioCoda() Dim err As Integer err = snd_seq_control_queue(handle, que, SND_SEQ_EVENT_START, 0, 0) If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) End Public Procedure ControlChange(flg As Byte, tick As Integer, canale As Byte, prm As Integer, value As Integer) Dim ev_midi As New Snd_seq_event_t Dim err As Integer With ev_midi .type = SND_SEQ_EVENT_CONTROLLER .flags = flg .tick_o_tv_sec = tick .queue = que .source_client = id .source_port = s_port .dest_client = dclient .dest_port = dport .channel = canale .param = prm .value = value End With err = snd_seq_event_output_buffer(handle, ev_midi) If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) End Public Procedure ProgramChange(flg As Byte, tick As Integer, canale As Byte, non_usato As Byte, value As Integer) Dim err As Integer Dim ev_midi As New Snd_seq_event_t With ev_midi .type = SND_SEQ_EVENT_PGMCHANGE .flags = flg .tick_o_tv_sec = tick .queue = que .source_client = id .source_port = s_port .dest_client = dclient .dest_port = dport .channel = canale .value = value End With err = snd_seq_event_output_buffer(handle, ev_midi) If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) End Public Procedure NoteON(flg As Byte, tick As Integer, canale As Byte, nota As Byte, vel As Byte) Dim ev_midi As New Snd_seq_event_t Dim err As Integer With ev_midi .type = SND_SEQ_EVENT_NOTEON .flags = flg .tick_o_tv_sec = tick .queue = que .source_client = id .source_port = s_port .dest_client = dclient .dest_port = dport .channel = canale .note = nota .velocity = vel End With err = snd_seq_event_output_buffer(handle, ev_midi) If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) End Public Procedure NoteOFF(flg As Byte, tick As Integer, canale As Byte, nota As Byte, vel As Byte) Dim ev_midi As New Snd_seq_event_t Dim err As Integer With ev_midi .type = SND_SEQ_EVENT_NOTEOFF .flags = flg .tick_o_tv_sec = tick .queue = que .source_client = id .source_port = s_port .dest_client = dclient .dest_port = dport .channel = canale .note = nota .velocity = vel End With err = snd_seq_event_output_buffer(handle, ev_midi) If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) End Public Procedure Flush() Dim err As Integer err = snd_seq_drain_output(handle) If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) End Public Procedure StopCoda() Dim err As Integer err = snd_seq_control_queue(handle, que, SND_SEQ_EVENT_STOP, 0, 0) If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) snd_seq_close(handle) End
Invio Eventi Midi con temporizzazione in "real time"
La temporizzazione in ALSA nella modalità in "real time" (temporizzazione in formato orario) è attivata assegnando al membro flags della snd_seq_event_t la Costante "SND_SEQ_TIME_STAMP_REAL" di ALSA.
Ricordiamo che questa modalità di temporizzazione degli Eventi Midi di ALSA adotta il sistema di misura dell'orario standard (ore, minuti, secondi), assegnando il valore in "secondi" al membro tv_sec e in "nanosecondi" al membro tv_nsec della sotto-Struttura snd_seq_real_time_t del membro time della Struttura principale snd_seq_event_t costitutiva.
Mostriamo un esempio identico al precedente, ma con impostazione della temporizzazione in Clock Midi (modalità "orario") espressa in secondi:
Dunque avremo la Classe principale "FMain.Class":
Private Const id_dev As Integer = 128 ' 14 = midi out oppure 128 (solitamente) = softsynth Private Const p_dev As Integer = 0 ' porta del Client destinatario dei dati Midi: solitamente 0 Public alsa As CAlsa ' Classe che incapsula le funzioni ALSA Public Sub Form_Open() ' Crea ("istanzia") un Oggetto della Classe "CAlsa" per poterla usare: With alsa = New CAlsa As "Alsa" ' Apre ALSA e assegna un nome al Client: Me.Title = "|| Client ALSA " & .AlsaIni("Programma di prova") ' Sceglie la periferica (Softsynth) su cui suonare: .ImpostaDispositivo(id_dev, p_dev) End With End Public Sub ToggleButton1_Click() If ToggleButton1.Value Then With alsa ' Avvia la "Coda" degli Eventi Midi ALSA: .AvvioCoda() ' Imposta il Volume gestendo l'Evento Midi "Control Change": .ControlChange(1, 0, 0, 7, 50) ' Imposta il suono dello strumento musicale gestendo l'Evento Midi "Program Change": .ProgramChange(1, 0, 0, 0, 44) ' Imposta la prima nota Midi da eseguire: .NoteON(1, 0, 0, 64, 100) ' Imposta la nota Midi da silenziare dopo 1 secondo dall'inizio della "Coda": .NoteOFF(1, 1, 0, 64, 0) ' Imposta il suono di un altro strumento musicale dopo 2 secondi dall'inizio della "Coda" (quindi crea una pausa): .ProgramChange(1, 2, 0, 0, 18) ' Imposta la nota Midi da eseguire dopo 2 secondi dall'inizio della "Coda": .NoteON(1, 2, 0, 66, 100) ' Imposta la nota Midi da silenziare dopo 3 secondi dall'inizio della "Coda": .NoteOFF(1, 3, 0, 66, 0) ' Imposta il suono di un altro strumento musicale dopo 3 secondi dall'inizio della "Coda": .ProgramChange(1, 3, 0, 0, 18) ' Imposta la nota Midi da eseguire dopo 3 secondi dall'inizio della "Coda": .NoteON(1, 3, 0, 68, 100) ' Imposta la nota Midi da silenziare dopo 5 secondi dall'inizio della "Coda": .NoteOFF(1, 5, 0, 68, 0) ' Dispone infine l'invio di tutti gli Eventi Midi bufferizzati nella "Coda": .Flush() End With Else alsa.StopCoda() Me.Close Endif End
Nella Classe secondaria, chiamata "CAlsa", vi sarà invece il seguente codice:
Private handle As Pointer Private id As Integer Private s_port As Integer Private que As Integer Private dclient As Byte Private dport As Byte 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, SND_SEQ_EVENT_CHANPRESS, SND_SEQ_EVENT_PITCHBEND, SND_SEQ_EVENT_START = 30, SND_SEQ_EVENT_STOP = 32 ' int snd_seq_open (snd_seq_t **seqp, const char * name, int streams, int mode) ' Open the ALSA sequencer. Private Extern snd_seq_open(seqp 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(seq 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(seq As Pointer) As Integer ' int snd_seq_create_simple_port (snd_seq_t* seq, const char* name, unsigned int caps, unsigned int type) ' Creates a port with the given capability and type bits. 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 * seq) ' Allocate a queue. Private Extern snd_seq_alloc_queue(seq As Pointer) As Integer ' int snd_seq_control_queue(snd_seq_t * seq, int q, int type, int value, snd_seq_event_t * ev) ' Queue controls - start/stop/continue. Private Extern snd_seq_control_queue(seq As Pointer, q As Integer, type As Integer, value As Integer, ev As Pointer) As Integer ' int snd_seq_event_output_buffer (snd_seq_t * seq, snd_seq_event_t *ev) ' Output an event. Private Extern snd_seq_event_output_buffer(seq As Pointer, ev As Snd_seq_event_t) As Integer ' 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 ' 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_close (snd_seq_t * seq) ' Close the sequencer. Private Extern snd_seq_close(seq As Pointer) As Integer Public Function AlsaIni(nome As String) As String Dim err As Integer err = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_OUTPUT, 0) Print "Apertura del subsistema 'seq' di ALSA = "; IIf(err == 0, "corretta !", "errata !") If err < 0 Then error.RAISE("Errore: " & snd_strerror(err)) snd_seq_set_client_name(handle, nome) id = snd_seq_client_id(handle) Print "Alsa ClientID = "; id s_port = snd_seq_create_simple_port(handle, "Uscita", SND_SEQ_PORT_CAP_READ, SND_SEQ_PORT_TYPE_MIDI_GENERIC + SND_SEQ_PORT_TYPE_APPLICATION) Print "Porta del programma = "; s_port If s_port < 0 Then error.Raise("Errore: " & snd_strerror(s_port)) que = snd_seq_alloc_queue(handle) If que < 0 Then error.Raise("Errore: " & snd_strerror(que)) Return CStr(id) & ":" & CStr(s_port) End Public Procedure ImpostaDispositivo(client As Integer, port As Integer) dclient = client dport = port End Public Procedure AvvioCoda() Dim err As Integer err = snd_seq_control_queue(handle, que, SND_SEQ_EVENT_START, 0, 0) If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) End Public Procedure ControlChange(flg As Byte, sec As Integer, canale As Byte, prm As Integer, value As Integer) Dim ev_midi As New Snd_seq_event_t Dim err As Integer With ev_midi .type = SND_SEQ_EVENT_CONTROLLER .flags = flg .tick_o_tv_sec = sec .queue = que .source_client = id .source_port = s_port .dest_client = dclient .dest_port = dport .channel = canale .param = prm .value = value End With err = snd_seq_event_output_buffer(handle, ev_midi) If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) End Public Procedure ProgramChange(flg As Byte, sec As Integer, canale As Byte, non_usato As Byte, value As Integer) Dim err As Integer Dim ev_midi As New Snd_seq_event_t With ev_midi .type = SND_SEQ_EVENT_PGMCHANGE .flags = flg .tick_o_tv_sec = sec .queue = que .source_client = id .source_port = s_port .dest_client = dclient .dest_port = dport .channel = canale .value = value End With err = snd_seq_event_output_buffer(handle, ev_midi) If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) End Public Procedure NoteON(flg As Byte, sec As Integer, canale As Byte, nota As Byte, vel As Byte) Dim ev_midi As New Snd_seq_event_t Dim err As Integer With ev_midi .type = SND_SEQ_EVENT_NOTEON .flags = flg .tick_o_tv_sec = sec .queue = que .source_client = id .source_port = s_port .dest_client = dclient .dest_port = dport .channel = canale .note = nota .velocity = vel End With err = snd_seq_event_output_buffer(handle, ev_midi) If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) End Public Procedure NoteOFF(flg As Byte, sec As Integer, canale As Byte, nota As Byte, vel As Byte) Dim ev_midi As New Snd_seq_event_t Dim err As Integer With ev_midi .type = SND_SEQ_EVENT_NOTEOFF .flags = flg .tick_o_tv_sec = sec .queue = que .source_client = id .source_port = s_port .dest_client = dclient .dest_port = dport .channel = canale .note = nota .velocity = vel End With err = snd_seq_event_output_buffer(handle, ev_midi) If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) End Public Procedure Flush() Dim err As Integer err = snd_seq_drain_output(handle) If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) End Public Procedure StopCoda() Dim err As Integer err = snd_seq_control_queue(handle, que, SND_SEQ_EVENT_STOP, 0, 0) If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) snd_seq_close(handle) End
Esempio in modalità temporizzazione in formato orario usando anche i nanosecondi
Di seguito ostriamo un semplice esempio simile al precedente, ma usando anche il membro ".tv_nsec" della Struttura snd_seq_event_t.
La Classe principale sarà la seguente:
Private Const id_dev As Integer = 128 ' 14 = midi out oppure 128 (solitamente) = softsynth Private Const p_dev As Integer = 0 ' porta del Client destinatario dei dati Midi: solitamente 0 Public alsa As CAlsa ' Classe che incapsula le funzioni ALSA Public Sub Form_Open() With alsa = New CAlsa As "Alsa" Me.Title = "|| Client ALSA " & .AlsaIni("Programma di prova") .ImpostaDispositivo(id_dev, p_dev) End With End Public Sub ToggleButton1_Click() Dim note As Byte[] = [60, 62, 64, 65, 67, 69, 71, 72] Dim b As Byte Dim ns As Integer If ToggleButton1.Value Then With alsa .AvvioCoda() .ControlChange(1, 0, 7, 50) .ProgramChange(1, 0, 44) For b = 0 To note.Max If b == 3 Then ns = 750000000 ' Imposta la nota Midi da eseguire. ' Aggiunge 750000000 nanosecondi. .NoteON(1, b, ns, note[b], 100) ns = 0 ' Imposta la nota Midi da silenziare dall'inizio della "Coda". ' Alla terza nota aggiunge 750000000 nanosecondi.</font> If b == 2 Then ns = 750000000 .NoteOFF(1, b + 1, ns, note[b], 0) ns = 0 Next .Flush() End With Else alsa.StopCoda() Me.Close Endif End
La Classe secondaria per la gestione delle risorse di ALSA:
Private handle As Pointer Private id As Integer Private s_port As Integer Private que As Integer Private dclient As Byte Private dport As Byte 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, SND_SEQ_EVENT_CHANPRESS, SND_SEQ_EVENT_PITCHBEND, SND_SEQ_EVENT_START = 30, SND_SEQ_EVENT_STOP = 32 ' int snd_seq_open (snd_seq_t **seqp, const char * name, int streams, int mode) ' Open the ALSA sequencer. Private Extern snd_seq_open(seqp 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(seq 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(seq As Pointer) As Integer ' int snd_seq_create_simple_port (snd_seq_t* seq, const char* name, unsigned int caps, unsigned int type) ' Creates a port with the given capability and type bits. 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 * seq) ' Allocate a queue. Private Extern snd_seq_alloc_queue(seq As Pointer) As Integer ' int snd_seq_control_queue(snd_seq_t * seq, int q, int type, int value, snd_seq_event_t * ev) ' Queue controls - start/stop/continue. Private Extern snd_seq_control_queue(seq As Pointer, q As Integer, type As Integer, value As Integer, ev As Pointer) As Integer ' int snd_seq_event_output_buffer (snd_seq_t * seq, snd_seq_event_t *ev) ' Output an event. Private Extern snd_seq_event_output_buffer(seq As Pointer, ev As Snd_seq_event_t) As Integer ' 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 ' 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_close (snd_seq_t * seq) ' Close the sequencer. Private Extern snd_seq_close(seq As Pointer) As Integer Public Function AlsaIni(nome As String) As String Dim err As Integer err = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_OUTPUT, 0) Print "Apertura del subsistema 'seq' di ALSA = "; IIf(err == 0, "corretta !", "errata !") If err < 0 Then error.RAISE("Errore: " & snd_strerror(err)) snd_seq_set_client_name(handle, nome) id = snd_seq_client_id(handle) Print "Alsa ClientID = "; id s_port = snd_seq_create_simple_port(handle, "Uscita", SND_SEQ_PORT_CAP_READ, SND_SEQ_PORT_TYPE_MIDI_GENERIC + SND_SEQ_PORT_TYPE_APPLICATION) Print "Porta del programma = "; s_port If s_port < 0 Then error.Raise("Errore: " & snd_strerror(s_port)) que = snd_seq_alloc_queue(handle) If que < 0 Then error.Raise("Errore: " & snd_strerror(que)) Return CStr(id) & ":" & CStr(s_port) End Public Procedure ImpostaDispositivo(client As Integer, port As Integer) dclient = client dport = port End Public Procedure AvvioCoda() Dim err As Integer err = snd_seq_control_queue(handle, que, SND_SEQ_EVENT_START, 0, 0) If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) End Public Procedure ControlChange(flg As Byte, sec As Integer, prm As Integer, value As Integer) Dim ev_midi As New Snd_seq_event_t Dim err As Integer With ev_midi .type = SND_SEQ_EVENT_CONTROLLER .flags = flg .tick_o_tv_sec = sec .queue = que .source_client = id .source_port = s_port .dest_client = dclient .dest_port = dport .param = prm .value = value End With err = snd_seq_event_output_buffer(handle, ev_midi) If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) End Public Procedure ProgramChange(flg As Byte, sec As Integer, value As Integer) Dim err As Integer Dim ev_midi As New Snd_seq_event_t With ev_midi .type = SND_SEQ_EVENT_PGMCHANGE .flags = flg .tick_o_tv_sec = sec .queue = que .source_client = id .source_port = s_port .dest_client = dclient .dest_port = dport .value = value End With err = snd_seq_event_output_buffer(handle, ev_midi) If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) End Public Procedure NoteON(flg As Byte, sec As Integer, ns As Integer, nota As Byte, vel As Byte) Dim ev_midi As New Snd_seq_event_t Dim err As Integer With ev_midi .type = SND_SEQ_EVENT_NOTEON .flags = flg .tick_o_tv_sec = sec .tv_nsec = ns .queue = que .source_client = id .source_port = s_port .dest_client = dclient .dest_port = dport .note = nota .velocity = vel End With err = snd_seq_event_output_buffer(handle, ev_midi) If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) End Public Procedure NoteOFF(flg As Byte, sec As Integer, ns As Integer, nota As Byte, vel As Byte) Dim ev_midi As New Snd_seq_event_t Dim err As Integer With ev_midi .type = SND_SEQ_EVENT_NOTEOFF .flags = flg .tick_o_tv_sec = sec .tv_nsec = ns .queue = que .source_client = id .source_port = s_port .dest_client = dclient .dest_port = dport .note = nota .velocity = vel End With err = snd_seq_event_output_buffer(handle, ev_midi) If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) End Public Procedure Flush() Dim err As Integer err = snd_seq_drain_output(handle) If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) End Public Procedure StopCoda() Dim err As Integer err = snd_seq_control_queue(handle, que, SND_SEQ_EVENT_STOP, 0, 0) If err < 0 Then Error.Raise("Errore: " & snd_strerror(err)) snd_seq_close(handle) End