Ricevere dati Midi senza funzioni di ALSA e inviarli mediante le funzioni di Alsa
Non è necessario in modo assoluto utilizzare le risorse esterne di ALSA per ricevere dati Midi inviati da un dispositivo esterno (ad esempio da una tastiera Midi).
I dati Midi, inviati da tale dispositivo, possono essere intercettati aprendo e leggendo il file-device specifico, creato all'atto della connessione di quel dispositivo esterno al computer.
In questo caso si intercetteranno dal dispositivo esterno i dati grezzi inviati.
In questa pagina vedremo come ricevere dati grezzi, dunque, da un dispositivo esterno e come inviarli subito attraverso ALSA ad un softsynth (ad esempio QSynth), affinché siano riprodotti i suoni delle note e dello strumento prescelti.
In particolare, il programma intercetterà i dati Midi grezzi, inviati dal dispositivo Midi esterno, aprendo in lettura e ponendo sotto "osservazione" il file-device creato appositamente dal sistema, senza l'uso di risorse esterne di ALSA. Opportunamente elaborati secondo il protocollo di ALSA, tali dati ricevuti saranno inviati al softhsynth mediante le funzioni esterne di ALSA per la riproduzione sonora.
A tal proposito, come sappiamo, ALSA non accetta semplici dati Midi grezzi, come quelli che arrivano al file-device della tastiera Midi esterna connessa ed inviati da appunto questo dispositivo.
ALSA accetta solo i dati Midi inviati ad essa rigorosamente nella forma dei suoi "Eventi Midi", come stabiliti e descritti dalle specificazioni di ALSA.
Così, ogni "Messaggio", previsto dal protocollo Midi, deve essere organizzato in una porzione di memoria assegnata, introducendo diversi specifici valori, e solo dopo possono essere inviati ad ALSA.
Sappiamo anche che la prima parte (16 byte) di questa porzione di memoria sarà occupata da valori comuni a tutti gli "Eventi Midi" di ALSA; mentre la parte restante e ultima sarà occupata dai valori inerenti agli specifici "Messaggi" previsti dal protocollo Midi.
Dunque, per organizzare un "Evento Midi " di ALSA, terremo conto della Struttura snd_seq_event_t prevista dall'API di ALSA, e in Gambas potremo utilizzare l'Oggetto Struttura come astrattamente segue:
Public Struct Snd_seq_event_t type As Byte flags As Byte tag As Byte queue As Byte Dati comuni tick_time As Integer a tutti real_time As Integer gli Eventi Midi di ALSA source_client As Byte source_port As Byte dest_client As Byte dest_port As Byte channel As Byte note As Byte Dati appartenenti velocity As Byte agli specifici "Messaggi" off_velocity As Byte del protocollo Midi param As Integer value As Integer End Struct
Esempi pratici
Ricevere dati Midi grezzi inviati da un dispositivo Midi interno
Mostriamo un semplice esempio, in ambiente grafico, per ricevere dati Midi grezzi, inviati dal codice interno dell'esempio medesimo, che saranno inviati subito attraverso ALSA al Client-softsynth (ad esempio QSynth).
Sul Form è rappresentata una tastiera musicale. Premendo un tasto di questa tastiera Midi interna, udiremo il suono della sua nota con il timbro dello strumento musicale prescelto in un ComboBox sul Form.
Come si potrà notare, utilizzeremo uno speciale codice per la gestione delle risorse di ALSA, nel quale non si creerà arbitrariamente una Porta del nostro Client Midi mediante la specifica funzione esterna di ALSA. Conseguentemente, dovremo assegnare al membro "queue " della Struttura degli "Eventi Midi" di ALSA, sopra illustrata, il valore della Costante "SND_SEQ_QUEUE_DIRECT" delle risorse di ALSA che ha il valore 253.
Private cb As ComboBox Private bb As New Byte[3] 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() With Me .W = Screen.AvailableWidth * 0.6 .H = Screen.AvailableHeight * 0.3 .Center End With CreaClient() With cb = New ComboBox(Me) As "Combo" .W = 160 .H = 25 .X = (Me.W * 0.93) - .W .Y = 0 .List = instrumenta .Index = 0 End With CreaTastiera() End Private Procedure CreaTastiera() Dim pn As Panel Dim neri As New Byte[40] Dim tasti As Button[] Dim b, c, n As Byte With pn = New Panel(Me) .W = Me.W * 0.86 .H = Me.H * 0.2 .X = (Me.W / 2) - (pn.W / 2) .Y = Me.H * 0.2 .Border = Border.Sunken .Background = &8b4513 End With Repeat neri[b] = 25 + (12 * b / 5) neri[b + 1] = 27 + (12 * b / 5) neri[b + 2] = 30 + (12 * b / 5) neri[b + 3] = 32 + (12 * b / 5) neri[b + 4] = 34 + (12 * b / 5) b += 5 Until b == neri.Count tasti = New Button[109] For t As Short = 0 To tasti.Max With tasti[t] = New Button(Me) As "Tasti" .W = 0 If t > 23 Then If neri.Exist(t) Then ' Imposta i tasti neri .W = Me.W * 0.0122 .H = Me.H * 0.16 Select Case t Case 25 + (12 * n) .X = tasti[t - 1].X + (((Me.W * 0.0245) / 2)) Case 27 + (12 * n) .X = tasti[t - 1].X + (((Me.W * 0.0245) / 2)) Case 30 + (12 * n) .X = tasti[t - 1].X + (((Me.W * 0.0245) / 2)) Case 32 + (12 * n) .X = tasti[t - 1].X + (((Me.W * 0.0245) / 2)) Case 34 + (12 * n) .X = tasti[t - 1].X + (((Me.W * 0.0245) / 2)) Inc n End Select .Y = Me.H * 0.38 .Background = Color.Black .Tag = t Else ' Imposta i tasti bianchi: .W = Me.W * 0.018 .H = Me.H * 0.25 .X = (.W * c) + (Me.W / 14) + 1 .Y = Me.H * 0.37 .Background = Color.White .Tag = t .Lower Inc c Endif Endif End With Next End Public Sub Tasti_MouseDown() bb[0] = 0 bb[1] = Last.Tag bb[2] = &64 InvioMIDI(bb) If Last.Background = Color.Black Then Last.Background = Color.DarkGray Me.Title = "Nota Midi: " & Last.Tag End Public Sub Tasti_MouseUp() bb[0] = 0 bb[1] = Last.Tag bb[2] = 0 InvioMIDI(bb) If Last.Background = Color.DarkGray Then Last.Background = Color.Black Me.Title = Null End ''''''''''''''''''''''''''''''''''''''''''''''' 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 ' int snd_seq_open (snd_seq_t **handle, const char * name, int streams, int mode) ' Open the ALSA sequencer. Private Extern snd_seq_open(handle As Pointer, name As String, streams As Integer, mode As Integer) As Integer ' const char * snd_strerror (int errnum) ' Returns the message for an error code. Private Extern snd_strerror(err As Integer) As String ' int snd_seq_event_output (snd_seq_t *handle, snd_seq_event_t *ev) ' Output an event. Private Extern snd_seq_event_output(handle As Pointer, ev As Snd_seq_event_t) ' int snd_seq_drain_output (snd_seq_t * seq) ' Drain output buffer to sequencer. Private Extern snd_seq_drain_output(seq As Pointer) As Integer ' int snd_seq_close (snd_seq_t *handle) ' Close the sequencer. Private Extern snd_seq_close(handle As Pointer) As Integer 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() ' Imposta lo strumento musicale 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 ' Imposta il tipo di strumento musicale mediante il Messaggio Midi "Program-Change": Messaggio(evento, SND_SEQ_EVENT_PGMCHANGE, [0, 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 ' Imposta il Messaggio Midi "Note-ON": Messaggio(evento, SND_SEQ_EVENT_NOTEON, mid, 0) End Private Procedure Messaggio(ev As Snd_seq_event_t, tipo As Byte, nota As Byte[], strum As Integer) With ev .type = tipo .channel = nota[0] .note = nota[1] .velocity = nota[2] .value = strum End With ' Inserisce l'Evento di ALSA nel buffer: snd_seq_event_output(seq, ev) ' Invia l'Evento: snd_seq_drain_output(seq) End Public Sub Form_Close() snd_seq_close(seq) End
Ricevere dati Midi grezzi inviati da un dispositivo Midi esterno
Il seguente semplice codice è simile a quello precedente, ma con la differenza che in questo caso i dati Midi grezzi saranno ricevuti da un dispositivo Midi esterno (ad esempio una tastiera Midi).
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() ' Apre il file-device Midi in "Lettura" e lo pone in "Osservazione": fl = Open "/dev" &/ dev For Read Watch End Public Sub File_Read() Dim b As Byte ' Se c'è qualcosa da leggere dal file-device, lo legge": Read #fl, b ' Prende in considerazione soltanto Messaggi-Midi "Note-OFF" e "Note-ON": If b > 159 Then Return bb.Push(b) If bb.Count == 3 Then InvioMIDI(bb) bb = New Byte[] Endif End ''''''''''''''''''''''''''''''''''''''''''''' 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 ' int snd_seq_open (snd_seq_t **handle, const char * name, int streams, int mode) ' Open the ALSA sequencer. Private Extern snd_seq_open(handle As Pointer, name As String, streams As Integer, mode As Integer) As Integer ' const char * snd_strerror (int errnum) ' Returns the message for an error code. Private Extern snd_strerror(err As Integer) As String ' int snd_seq_event_output (snd_seq_t *handle, snd_seq_event_t *ev) ' Output an event. Private Extern snd_seq_event_output(handle As Pointer, ev As Snd_seq_event_t) ' int snd_seq_drain_output (snd_seq_t * seq) ' Drain output buffer to sequencer. Private Extern snd_seq_drain_output(seq As Pointer) As Integer ' int snd_seq_close (snd_seq_t *handle) ' Close the sequencer. Private Extern snd_seq_close(handle As Pointer) As Integer 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() ' Imposta lo strumento musicale 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 ' Imposta il tipo di strumento musicale mediante il Messaggio Midi "Program-Change": 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 ' Imposta il Messaggio Midi "Note-ON": Messaggio(evento, SND_SEQ_EVENT_NOTEON, mid, 0) End Private Procedure Messaggio(ev As Snd_seq_event_t, tipo As Byte, nota As Byte[], strum As Integer) With ev .type = tipo .channel = nota[0] .note = nota[1] .velocity = nota[2] .value = strum End With ' Inserisce l'Evento di ALSA nel buffer: snd_seq_event_output(seq, ev) ' Invia l'Evento: snd_seq_drain_output(seq) End Public Sub Form_Close() snd_seq_close(seq) fl.Close End