|
|
(Una versione intermedia di uno stesso utente non è mostrata) |
Riga 1: |
Riga 1: |
− | Mostreremo di seguito il codice di un semplice Client Midi di Alsa capace di ricevere dati Midi (a titolo esemplificativo sono stati individuati i quattro tipi più importanti e frequenti) da un altro Client di Alsa (ad esempio da una tastiera Midi esterna) e di inviarli immediatamente ad un altro Client (ad esempio ad un ''softsynth'' per l'ascolto sonoro).
| + | #REDIRECT [[ALSA_e_Gambas:_Esempio_di_semplice_Client_Midi_di_Alsa_in_ricezione_ed_invio_dati]] |
− | | |
− | Al terzo parametro della funzione ''snd_seq_open()'', necessaria per l'apertura del subsistema ''Seq'' di Alsa, va inserita la costante ''SND_SEQ_OPEN_DUPLEX'' per consentire il flusso di dati sia in Entrata che in Uscita.
| |
− | <BR>I dati Midi ricevuti sono assegnati ad una variabile di tipo ''Struttura'' organizzata secondo lo schema tipico di un "''Evento''" Midi Alsa.
| |
− | | |
− | Dopo l'avvio del ''softsynth'' e del seguente applicativo, sarà necessario connettere via Alsa la tastiera Midi ed ''softsynth'' al presente applicativo Midi rispettivamente in entrata ed in uscita.
| |
− | Public Struct snd_seq_event_t
| |
− | type As Byte <FONT color=gray>' ''byte n° 0''</font>
| |
− | flag As Byte
| |
− | tag As Byte
| |
− | queue As Byte
| |
− | timestamp1 As Integer
| |
− | timestamp2 As Integer
| |
− | source_id As Byte
| |
− | source_porta As Byte
| |
− | dest_id As Byte
| |
− | dest_porta As Byte
| |
− | channel As Byte <FONT color=gray>' ''byte n° 16''</font>
| |
− | note As Byte
| |
− | velocity As Byte
| |
− | off_velocity As Byte
| |
− | parametro As Integer <FONT color=gray>' ''byte n° 20''</font>
| |
− | valore As Integer <FONT color=gray>' ''byte n° 24''</font>
| |
− | End Struct
| |
− |
| |
− |
| |
− | Library "libasound:2.0.0"
| |
− |
| |
− | Private Const SND_SEQ_OPEN_DUPLEX As Integer = 3
| |
− | Private Const SND_SEQ_PORT_CAP_WRITE As Integer = 2
| |
− | Private Const SND_SEQ_PORT_CAP_SUBS_WRITE As Integer = 64
| |
− | Private Const SND_SEQ_PORT_CAP_READ As Integer = 1
| |
− | Private Const SND_SEQ_PORT_CAP_SUBS_READ As Integer = 32
| |
− | Private Const SND_SEQ_PORT_TYPE_APPLICATION As Integer = 1048576
| |
− | Private Const SND_SEQ_ADDRESS_UNKNOWN As Byte = 253
| |
− | Private Const SND_SEQ_ADDRESS_SUBSCRIBERS As Byte = 254
| |
− | Private Const SND_SEQ_EVENT_NOTEON As Byte = 6
| |
− | Private Const SND_SEQ_EVENT_NOTEOFF As Byte = 7
| |
− | Private Const SND_SEQ_EVENT_CONTROLLER As Byte = 10
| |
− | Private Const SND_SEQ_EVENT_PGMCHANGE As Byte = 11
| |
− |
| |
− |
| |
− | <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_open (snd_seq_t **handle, const char *name, int streams, int mode)
| |
− | ' ''Open the ALSA sequencer.''</font>
| |
− | Private Extern snd_seq_open(handle 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(handle 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(handle As Pointer) As Integer
| |
− |
| |
− | <FONT color=gray>' ''int snd_seq_create_simple_port(snd_seq_t *seq, const char *name, unsigned int caps, unsigned int type)''
| |
− | ' ''Create a port - simple version.''</font>
| |
− | Private Extern snd_seq_create_simple_port(handle As Pointer, name As String, caps As Integer, type As Integer) As Integer
| |
− |
| |
− | <FONT color=gray>' ''int snd_seq_event_input(snd_seq_t *handle, snd_seq_event_t **ev)''
| |
− | ' ''Retrieve an event from sequencer.''</font>
| |
− | Private Extern snd_seq_event_input(handle As Pointer, ev As Snd_seq_event_t) As Integer
| |
− |
| |
− | <FONT color=gray>' ''int snd_seq_event_output_direct (snd_seq_t *handle, snd_seq_event_t *ev)''
| |
− | ' ''Output an event directly to the sequencer NOT through output buffer.''</font>
| |
− | Private Extern snd_seq_event_output_direct(handle As Pointer, ev As Snd_seq_event_t) As Integer
| |
− |
| |
− |
| |
− | '''Public''' Sub Main()
| |
− |
| |
− | Dim seq As Pointer
| |
− | Dim idportaE, idportaU, err, id As Integer
| |
− |
| |
− | err = snd_seq_open(VarPtr(seq), "default", SND_SEQ_OPEN_DUPLEX, 0)
| |
− | If err < 0 Then Error.Raise("Impossibile inizializzare il subsistema 'seq' di ALSA: " & snd_strerror(err))
| |
− | Print "Apertura del dispositivo 'seq' di Alsa: regolare\nNome del dispositivo 'seq': "; "'default'"
| |
− |
| |
− | err = snd_seq_set_client_name(seq, "Sequencer dimostrativo di ALSA")
| |
− | If err < 0 Then Error.Raise("Impossibile assegnare un nome al Client applicativo di ALSA: " & snd_strerror(err))
| |
− |
| |
− | id = snd_seq_client_id(seq)
| |
− | Print "Numero identificativo del sequencer dimostrativo: "; id
| |
− |
| |
− | idportaE = snd_seq_create_simple_port(seq, "Porta di Entrata del sequencer dimostrativo di ALSA", SND_SEQ_PORT_CAP_WRITE Or SND_SEQ_PORT_CAP_SUBS_WRITE, SND_SEQ_PORT_TYPE_APPLICATION)
| |
− | If idportaE < 0 Then Error.Raise("Impossibile creare una porta del Client applicativo di ALSA: " & snd_strerror(err))
| |
− | Print "Numero identificativo della porta di Entrata: "; idportaE
| |
− |
| |
− | idportaU = snd_seq_create_simple_port(seq, "Porta di Uscita del sequencer dimostrativo di ALSA", SND_SEQ_PORT_CAP_READ Or SND_SEQ_PORT_CAP_SUBS_READ, SND_SEQ_PORT_TYPE_APPLICATION)
| |
− | If idportaU < 0 Then Error.Raise("Impossibile creare una porta del Client applicativo di ALSA: " & snd_strerror(err))
| |
− | Print "Numero identificativo della porta di Uscita: "; idportaU
| |
− | Print
| |
− |
| |
− | intercettaMessaggiMidi(seq, id, idportaU)
| |
− |
| |
− | '''End'''
| |
− |
| |
− | '''Public''' Procedure intercettaMessaggiMidi(seqiM As Pointer, idC As Integer, portEx As Integer)
| |
− |
| |
− | Dim ev As Pointer
| |
− | Dim midi@ As Snd_seq_event_t
| |
− |
| |
− | While True
| |
− | snd_seq_event_input(seqiM, VarPtr(ev))
| |
− | midi@ = ev
| |
− | With midi@
| |
− | Select Case midi@.type
| |
− | Case SND_SEQ_EVENT_NOTEON
| |
− | Print "Evento 'NoteOn' sul canale: "; .channel, .note, .velocity
| |
− | Case SND_SEQ_EVENT_NOTEOFF
| |
− | Print "Evento 'NoteOff' sul canale: "; .channel, .note, .velocity
| |
− | Case SND_SEQ_EVENT_CONTROLLER
| |
− | Print "Evento 'Control' sul canale: "; .channel, .parametro, .valore
| |
− | Case SND_SEQ_EVENT_PGMCHANGE
| |
− | Print "Evento 'Program Change' sul canale: "; .channel, .valore
| |
− | End Select
| |
− | .source_id = idC
| |
− | .source_porta = portEx
| |
− | .dest_id = SND_SEQ_ADDRESS_SUBSCRIBERS
| |
− | .dest_porta = SND_SEQ_ADDRESS_UNKNOWN
| |
− | End With
| |
− | snd_seq_event_output_direct(seqiM, midi@)
| |
− | Wend
| |
− |
| |
− | '''End'''
| |
− | | |
− | Un altro esempio analogo al precedente.
| |
− | <BR>Funzionamento:
| |
− | <BR>1) connettere una tastiera Midi al computer;
| |
− | <BR>2) lanciare il softhsynth (p.e. Qsynth);
| |
− | <BR>3) quindi lanciare il seguente codice;
| |
− | <BR>4) scegliere uno strumento musicale dalla lista "General Midi" contenuta dal "ComboBox";
| |
− | <BR>5) ...suonare la tastiera Midi esterna.
| |
− | Private fl As File
| |
− | Private cb As ComboBox
| |
− | Private bb As Byte[]
| |
− | Private seq As Pointer
| |
− | Private instrumenta As String[] = ["Acustic Grand Piano", "Bright Acustic Piano", "Electric Grand Piano", "Honky-tonk",
| |
− | "Electric Piano 1", "Electric Piano 2", "Harpsichord", "Clavinet", "Celesta", "Glockenspiel", "Music Box", "Vibraphone",
| |
− | "Marimba", "Xylophone", "Tubular Bells", "Dulcimer", "Hammond Organ", "Percussive Organ", "Rock Organ", "Church Organ",
| |
− | "Reed Organ", "Accordion", "Harmonica", "Tango Accordion", "Acoustic Guitar (nylon)", "Acoustic Guitar (steel)",
| |
− | "Electric Guitar (jazz)", "Electric Guitar (clean)", "Electric Guitar(muted)", "Overdriven Guitar", "Distortion Guitar",
| |
− | "Guitar Harmonics", "Acoustic Bass", "Electric Bass (finger)", "Electric Bass (pick)", "Fretless Bass", "Slap Bass 1",
| |
− | "Slap Bass 2", "Synth Bass 1", "Synth Bass 2", "Violin", "Viola", "Cello", "Contrabass", "Tremolo Strings",
| |
− | "Pizzicato Strings", "Orchestral Harp", "Timpani", "String Ensemble 1", "String Ensemble 2", "SynthStrings 1",
| |
− | "SynthStrings 2", "Choir Aahs", "Voice Oohs", "Synth Voice", "Orchestra Hit", "Trumpet", "Trombone", "Tuba", "Muted Trumpet",
| |
− | "French Horn", "Brass Section", "Synth Brass 1", "Synth Brass 2", "Soprano Sax", "Alto Sax", "Tenor Sax", "Baritone Sax",
| |
− | "Oboe", "English Horn", "Basson", "Clarinet", "Piccolo", "Flute", "Recorder", "Pan Flute", "Bottle Blow", "Shakuhachi",
| |
− | "Whistle", "Ocarina", "Lead 1 (square)", "Lead 2 (sawtooth)", "Lead 3 (caliope lead)", "Lead 4 (chiff lead)",
| |
− | "Lead 5 (charang)", "Lead 6 (voice)", "Lead 7 (fifths)", "Lead 8(brass+lead)", "Pad 1 (new age)", "Pad 2 (warm)",
| |
− | "Pad 3 (polysynth)", "Pad 4 (choir)", "Pad 5 (bowed)", "Pad 6 (metallic)", "Pad 7 (halo)", "Pad 8 (sweep)", "FX 1 (rain)",
| |
− | "FX 2 (soundtrack)", "FX 3 (crystal)", "FX 4 (atmosphere)", "FX 5 (brightness)", "FX 6 (goblins)", "FX 7 (echoes)",
| |
− | "FX 8 (sci-fi)", "Sitar", "Banjo", "Shamisen", "Koto", "Kalimba", "Bagpipe", "Fiddle", "Shanai", "Tinkle Bell", "Agogo",
| |
− | "Steel Drums", "Woodblock", "Taiko Drum", "Melodic Tom", "Synth Drum", "Reverse Cymbal", "Guitar Fret Noise", "Breath Noise",
| |
− | "Seashore", "Bird Tweet", "Telephone Ring", "Helicopter", "Applause", "Gunshot"]
| |
− |
| |
− |
| |
− | '''Public''' Sub Form_Open()
| |
− |
| |
− | Dim dev As String
| |
− |
| |
− | dev = Dir("/dev", "*midi*", gb.Device)[0]
| |
− |
| |
− | With Me
| |
− | .W = 400
| |
− | .H = 200
| |
− | .Center
| |
− | End With
| |
− | With cb = New ComboBox(Me) As "Combo"
| |
− | .W = 180
| |
− | .H = 30
| |
− | .X = (Me.W / 2) - (.W / 2)
| |
− | .Y = (Me.H / 2.5) - .H
| |
− | .Text = "Strumenti"
| |
− | .List = instrumenta
| |
− | End With
| |
− |
| |
− | bb = New Byte[]
| |
− | CreaClient()
| |
− |
| |
− | <FONT Color=gray>' ''Apre il file-device Midi in "Lettura" e lo pone in "Osservazione":''</font>
| |
− | fl = Open "/dev" &/ dev For Read Watch
| |
− |
| |
− | '''End'''
| |
− |
| |
− | '''Public''' Sub File_Read()
| |
− |
| |
− | Dim b As Byte
| |
− |
| |
− | <FONT Color=gray>' ''Se c'è qualcosa da leggere dal file-device, lo legge":''</font>
| |
− | Read #fl, b
| |
− |
| |
− | <FONT Color=gray>' ''Prende in considerazione soltanto Messaggi-Midi "Note-ON" con canale 0:''</font>
| |
− | If b > 144 Then Return
| |
− | bb.Push(b)
| |
− | If bb.Count == 3 Then
| |
− | InvioMIDI(bb)
| |
− | bb = New Byte[]
| |
− | Endif
| |
− |
| |
− | '''End'''
| |
− |
| |
− | <FONT Color=gray>''''''''''''''''''''''''''''''''''''''''''''''''''</font>
| |
− |
| |
− | Library "libasound:2"
| |
− |
| |
− | Public Struct snd_seq_event_t ' Struttura dell'Evento Midi di ALSA
| |
− | type As Byte
| |
− | flags As Byte
| |
− | tag As Byte
| |
− | queue As Byte
| |
− | tick_time As Integer
| |
− | real_time As Integer
| |
− | source_client As Byte
| |
− | source_port As Byte
| |
− | dest_client As Byte
| |
− | dest_port As Byte
| |
− | channel As Byte
| |
− | note As Byte
| |
− | velocity As Byte
| |
− | off_velocity As Byte
| |
− | param As Integer
| |
− | value As Integer
| |
− | End Struct
| |
− |
| |
− | Private Const SND_SEQ_OPEN_OUTPUT As Integer = 1
| |
− | Private Const SND_SEQ_QUEUE_DIRECT As Byte = 253
| |
− | Private Const SND_SEQ_EVENT_NOTEON As Byte = 6
| |
− | Private Const SND_SEQ_EVENT_PGMCHANGE As Byte = 11
| |
− |
| |
− | <FONT Color=gray>' ''int snd_seq_open (snd_seq_t **handle, const char * name, int streams, int mode)''
| |
− | ' ''Open the ALSA sequencer.''</font>
| |
− | Private Extern snd_seq_open(handle As Pointer, name As String, streams As Integer, mode As Integer) As Integer
| |
− |
| |
− | <FONT Color=gray>' ''const char * snd_strerror (int errnum)''
| |
− | ' ''Returns the message for an error code.''</font>
| |
− | Private Extern snd_strerror(err As Integer) As String
| |
− |
| |
− | <FONT Color=gray>' ''int snd_seq_event_output (snd_seq_t *handle, snd_seq_event_t *ev)''
| |
− | ' ''Output an event.''</font>
| |
− | Private Extern snd_seq_event_output(handle As Pointer, ev As Snd_seq_event_t)
| |
− |
| |
− | <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>' ''int snd_seq_close (snd_seq_t *handle)''
| |
− | ' ''Close the sequencer.''</font>
| |
− | Private Extern snd_seq_close(handle As Pointer) As Integer
| |
− |
| |
− |
| |
− | '''Private''' Procedure CreaClient()
| |
− |
| |
− | Dim rit As Integer
| |
− |
| |
− | rit = snd_seq_open(VarPtr(seq), "default", SND_SEQ_OPEN_OUTPUT, 0)
| |
− | If rit < 0 Then Error.Raise("Impossibile aprire il subsistema 'seq' di ALSA: " & snd_strerror(rit))
| |
− |
| |
− | '''End'''
| |
− |
| |
− | '''Public''' Sub Combo_Change() <FONT Color=gray>' ''Imposta lo strumento musicale''</font>
| |
− |
| |
− | If cb.Index = -1 Then Return
| |
− | Dim evento As New Snd_seq_event_t
| |
− |
| |
− | With evento
| |
− | .queue = SND_SEQ_QUEUE_DIRECT
| |
− | .dest_client = 128
| |
− | .dest_port = 0
| |
− | .channel = 0
| |
− | End With
| |
− |
| |
− | <FONT Color=gray>' ''Imposta il tipo di strumento musicale mediante il Messaggio Midi "Program-Change":''</font>
| |
− | Messaggio(evento, SND_SEQ_EVENT_PGMCHANGE, [144, 0, 0], cb.Index)
| |
− |
| |
− | '''End'''
| |
− |
| |
− | '''Private''' Procedure InvioMIDI(mid As Byte[])
| |
− |
| |
− | Dim evento As New Snd_seq_event_t
| |
− |
| |
− | With evento
| |
− | .queue = SND_SEQ_QUEUE_DIRECT
| |
− | .dest_client = 128
| |
− | .dest_port = 0
| |
− | .channel = mid[0]
| |
− | End With
| |
− |
| |
− | <FONT Color=gray>' ''Imposta il Messaggio Midi "Note-ON":''</font>
| |
− | Messaggio(evento, SND_SEQ_EVENT_NOTEON, mid, 0)
| |
− |
| |
− | '''End'''
| |
− |
| |
− | '''Private''' Procedure Messaggio(ev As Snd_seq_event_t, tipo As Byte, nota As Byte[], instr As Integer)
| |
− |
| |
− | With ev
| |
− | .type = tipo
| |
− | .channel = nota[0]
| |
− | .note = nota[1]
| |
− | .velocity = nota[2]
| |
− | .value = instr
| |
− | End With
| |
− |
| |
− | <FONT Color=gray>' ''Inserisce l'Evento di ALSA nel buffer:''</font>
| |
− | snd_seq_event_output(seq, ev)
| |
− |
| |
− | <FONT Color=gray>' ''Invia l'Evento:''</font>
| |
− | snd_seq_drain_output(seq)
| |
− |
| |
− | '''End'''
| |
− |
| |
− | '''Public''' Sub Form_Close()
| |
− |
| |
− | snd_seq_close(seq)
| |
− | fl.Close
| |
− |
| |
− | '''End'''
| |