Differenze tra le versioni di "La gestione dei dati Midi senza l'uso delle funzioni di Alsa"

Da Gambas-it.org - Wikipedia.
 
(20 versioni intermedie di uno stesso utente non sono mostrate)
Riga 1: Riga 1:
 
===Introduzione===
 
===Introduzione===
Per mera curiosità presentiamo qui una soluzioni alternativa per ricevere da un dispositivo esterno Midi (ad esempio una tastiera Midi) i messaggi MIDI <SPAN style="text-decoration:underline">senza</span> l'utilizzo delle funzioni esterne di ALSA, anche se, però, si andrà a leggere il flusso di dati grezzi Midi direttamente da un ''file-device'' specifico <SPAN style="text-decoration:underline">comunque generato da Alsa</span>.
+
Presentiamo qui una soluzione alternativa per ricevere da un dispositivo esterno Midi (ad esempio una tastiera Midi) i messaggi MIDI <SPAN style="text-decoration:underline">senza</span> l'utilizzo delle funzioni esterne di ALSA, anche se, però, si andrà a leggere il flusso di dati grezzi Midi direttamente da un ''file-device'' specifico <SPAN style="text-decoration:underline">comunque generato da Alsa</span>.
  
 
Questa modalità di gestione, ancorché senza l'uso delle funzioni dell'API di Alsa, è per taluni aspetti analogo a quell del [[Ricevere_dati_grezzi_Midi_attraverso_il_subsistema_RawMidi|subsistema ''RawMidi'']] di Alsa.
 
Questa modalità di gestione, ancorché senza l'uso delle funzioni dell'API di Alsa, è per taluni aspetti analogo a quell del [[Ricevere_dati_grezzi_Midi_attraverso_il_subsistema_RawMidi|subsistema ''RawMidi'']] di Alsa.
 
  
  
Riga 19: Riga 18:
  
 
e si trova nel seguente percorso: ''/dev/snd/midiCcDd''.
 
e si trova nel seguente percorso: ''/dev/snd/midiCcDd''.
 +
 +
V'è da dire che con alcuni ''kernel'', l'inserimento di un dispositivo Midi esterno genera contestualmente - oltre al file-device sopra descritto (''/dev/snd/midiCcDd'') - anche i file-device "''/dev/midi2''", "''/dev/dmidi2''" o "''/dev/dmmidi2''". Detti ultimi tre file-device possono essere utilizzati e gestiti con le medesime modalità previste, e che saranno di seguito descritte, per il file-device ''/dev/snd/midiCcDd''.
  
  
 
===Lettura del file-device ''rawmidi''===
 
===Lettura del file-device ''rawmidi''===
 
Per leggere il flusso di dati Midi grezzi che passa attraverso il predetto file-device, apriremo il file-device il lettura aggiungendo la parola "''Watch''". Se, dunque, potrà essere letto almeno un byte dal file-device, allora verrà chiamato l'evento ''File_Read()'':
 
Per leggere il flusso di dati Midi grezzi che passa attraverso il predetto file-device, apriremo il file-device il lettura aggiungendo la parola "''Watch''". Se, dunque, potrà essere letto almeno un byte dal file-device, allora verrà chiamato l'evento ''File_Read()'':
  '''Private''' varFile As File
+
  Private mid As File
 
   
 
   
 
   
 
   
  '''Public''' Sub Form_Open()
+
  Public Sub Main()
 
   
 
   
  varFile = Open "/dev/snd/midiC2D0" For Read Watch
+
  mid = Open "/dev/snd/midiC2D0" For Read Watch
 
   
 
   
  '''End'''
+
  End
 
   
 
   
 
   
 
   
  '''Public''' Sub File_Read()
+
  Public Sub File_Read()
 
    
 
    
 
   Dim dato As Byte
 
   Dim dato As Byte
 
   
 
   
  <Font Color= #006400>' ''Leggiamo il file-device e scriviamo il valore ottenuto nella variabile "dato":''</font>
+
  <Font Color=gray>' ''Legge dal file-device e scriviamo il valore ottenuto nella variabile "dato":''</font>
  Read #varFile, dato   <Font Color= #006400>' |[[#Note|1]]|</font>
+
  Read #mid, dato
 
   
 
   
 +
<Font Color=gray>' ''Per fini didattici ci limitiamo qui solo a scrivere il dato in console per vederlo:''</font>
 +
  Print "byte inviato dalla tastiera = "; dato
 
   
 
   
  <Font Color= #006400>' ''Poiché può accadere che la tastiera Midi invii in continuazione il messaggio Midi 254 (Active Sensing) ed altri messaggi''
+
  End
''che a noi qui non interessano, li escludiamo con un filtro:</font>''
 
  If by > 239 Then Return
 
 
   
 
   
<Font Color= #006400>' ''Per fini didattici ci limitiamo qui solo a scrivere il dato in console per vederlo.</font>
 
  Print "byte inviato dalla tastiera = "; dato
 
 
   
 
   
  '''End'''
+
  Public Sub Application_Read()  <Font Color=gray>' ''Questo Evento è sollevato premendo il tasto "Enter" o "Invio" della tastiera.''</font>
 +
 
 +
<Font Color=gray>' ''Chiude il file-device:''</font>
 +
  mid.Close
 +
 
 +
<Font Color=gray>' ''Chiude il programma:''</font>
 +
  Quit
 +
 +
End
 +
Come si può notare, la ricezione dei dati dalla tastiera Midi esterna è avvenuta <Span style="text-decoration:underline">senza</span> l'utilizzo di alcuna funzione ALSA, ma direttamente andando a "raccoglierli" dal file-device (comunque creato da Alsa) della porta virtuale della tastiera.
  
<BR>Come si può notare, la ricezione dei dati dalla tastiera Midi esterna è avvenuta <Span style="text-decoration:underline">senza</span> l'utilizzo di alcuna funzione ALSA, ma direttamente andando a "raccoglierli" dal file-device della porta virtuale della tastiera.
 
  
<BR>Si fa presente, ad ogni modo, che tale soluzione ''non'' è proprio ottimale per almeno le tre seguenti ragioni:
+
Mostriamo ora un esempio più complesso, nel quale verranno eliminati mediante un apposito filtro i dati aventi uno ''status byte'' identificativo superiore al valore 239 (in particolare per elimnare il continuo invio del messaggio Midi ''Active Sensing''). Inoltre, si convertirà lo ''status byte'' dei Messaggi Midi autorizzati, affinché sia evidente il proprio numero di canale, e verranno mostrate altre informazioni relative ai dati ricevuti:
 +
Private fl As File
 +
 
 +
 +
Public Sub Main()
 +
 +
  Dim disp As String = "/dev/snd"
 +
 +
  If Dir(disp, "midi*", gb.Device).Count = 0 Then Error.Raise("Nessun dispositivo Midi esterno trovato !")
 +
 +
  disp &/= Dir(disp, "midi*", gb.Device)[0]
 +
 +
  fl = Open disp For Read Watch
 +
 +
End
 +
 +
 +
Public Sub File_Read()
 +
 
 +
  Dim b As Byte
 +
 
 +
  Read #fl, b
 +
 
 +
  Select Case b
 +
    Case 128 To 143
 +
      b = b And 15
 +
      Print "\nCanale", "Nota", "Velocità", " = NoteOFF"
 +
    Case 144 To 159
 +
      b = b And 15
 +
      Print "\nCanale", "Nota", "Velocità", " = NoteON"
 +
    Case 160 To 175
 +
      b = b And 15
 +
      Print "\nCanale", "Nota", "Velocità", " = Aftertouch Polyphonic"
 +
    Case 176 To 191
 +
      b = b And 15
 +
      Print "\nCanale", "Parametro", "Valore", " = Control Change"
 +
    Case 192 To 207
 +
      b = b And 15
 +
      Print "\nCanale", "parametro", Null, " = Program Change"
 +
    Case 208 To 223
 +
      b = b And 15
 +
      Print "\nCanale", "Parametro", Null, " = Channel Aftertouch"
 +
    Case 224 To 239
 +
      b = b And 15
 +
      Print "\nCanale", "Parametro", "Valore", " = Pitch Wheel"
 +
    Case 240 To 255
 +
      Return
 +
  End Select
 +
 
 +
  Print b,
 +
 
 +
End
 +
 +
 +
Public Sub Application_Read()  <Font Color=gray>' ''Questo Evento è sollevato premendo il tasto "Enter" o "Invio" della tastiera.''</font>
 +
 +
<Font Color= #006400>' ''Chiude il file-device:''</font>
 +
  mid.Close
 +
 
 +
<Font Color= #006400>' ''Chiude il programma:''</font>
 +
  Quit
 +
 +
End
 +
 
 +
Si fa presente, ad ogni modo, che la soluzione, presentata in questa pagina, seppur efficace, ''non'' è proprio ottimale per almeno le tre seguenti ragioni:
 
* si va in ambiente ''extra ALSA'', laddove invece si intendeva programmare utilizzando le API di ALSA;
 
* si va in ambiente ''extra ALSA'', laddove invece si intendeva programmare utilizzando le API di ALSA;
 
* ALSA non riesce ad intercettare più i dati provenienti dalla tastiera esterna Midi, essendo stati intercettati da noi con la presente modalità ''extra ALSA'';
 
* ALSA non riesce ad intercettare più i dati provenienti dalla tastiera esterna Midi, essendo stati intercettati da noi con la presente modalità ''extra ALSA'';
* la presente soluzione ''extra ALSA'' <Span style="text-decoration:underline">potrebbe</span> interferire o comunque creare problemi alle funzionalità del sistema ALSA. Attualmente non si è a conoscenza di  tutti i possibili disturbi.
+
* la presente soluzione ''extra ALSA'' <Span style="text-decoration:underline">potrebbe</span> interferire o comunque creare problemi alle funzionalità del sistema ALSA.
 
<BR>Possiamo riportare, però, il caso di contemporaneo tentativo di ricezione dei dati da un medesimo dispositivo hardware Midi esterno con funzioni ALSA e con il presente metodo ''extra'' ALSA. Ebbene è stato accertato che in tale caso soltanto una delle procedure intercetterà i dati provenienti dal dispositivo Midi esterno. Se, ad esempio, verrà avviato prima il metodo ''extra'' ALSA e successivamente quello mediante le funzioni ALSA di ricezione (magari anche con due applicativi distinti), facendoli comunque funzionare ''contemporaneamente'', l'applicativo che utilizza le funzioni di ALSA per ricevere i dati Midi, segnalerà in console il seguente errore:
 
<BR>Possiamo riportare, però, il caso di contemporaneo tentativo di ricezione dei dati da un medesimo dispositivo hardware Midi esterno con funzioni ALSA e con il presente metodo ''extra'' ALSA. Ebbene è stato accertato che in tale caso soltanto una delle procedure intercetterà i dati provenienti dal dispositivo Midi esterno. Se, ad esempio, verrà avviato prima il metodo ''extra'' ALSA e successivamente quello mediante le funzioni ALSA di ricezione (magari anche con due applicativi distinti), facendoli comunque funzionare ''contemporaneamente'', l'applicativo che utilizza le funzioni di ALSA per ricevere i dati Midi, segnalerà in console il seguente errore:
 
<BR>"''Subscribe inport: err=-11 (Resource temporarily unavailable)''".
 
<BR>"''Subscribe inport: err=-11 (Resource temporarily unavailable)''".
Riga 61: Riga 132:
  
 
===Ricezione dati da ''più'' dispositivi Midi esterni===
 
===Ricezione dati da ''più'' dispositivi Midi esterni===
 
 
Sino ad ora abbiamo considerato l'invio di messaggi Midi da <Span style="text-decoration:underline">un solo</span> dispositivo esterno, per esempio una sola tastiera Midi. Come fare, però, se volessimo, con la modalità che stiamo ora trattando, ricevere e gestire i dati da <Span style="text-decoration:underline">più</span> tastiere Midi esterne ?
 
Sino ad ora abbiamo considerato l'invio di messaggi Midi da <Span style="text-decoration:underline">un solo</span> dispositivo esterno, per esempio una sola tastiera Midi. Come fare, però, se volessimo, con la modalità che stiamo ora trattando, ricevere e gestire i dati da <Span style="text-decoration:underline">più</span> tastiere Midi esterne ?
 
<BR>Dobbiamo premettere che se colleghiamo al computer via USB un secondo dispositivo Midi esterno, possiamo notare che in ''/dev/snd'' comparirà un altro file-device simile a quello che creato dal sistema quando avevamo collegato la prima tastiera Midi esterna. Questa volta il nuovo file-device, che si riferisce alla <Span style="text-decoration:underline">seconda</span> tastiera, avrà come nome: ''midiC3D0''. Qualora collegassimo un terzo dispositivo Midi estreno, avremmo un ulteriore file-device "''midiC4D0''", e così via.
 
<BR>Dobbiamo premettere che se colleghiamo al computer via USB un secondo dispositivo Midi esterno, possiamo notare che in ''/dev/snd'' comparirà un altro file-device simile a quello che creato dal sistema quando avevamo collegato la prima tastiera Midi esterna. Questa volta il nuovo file-device, che si riferisce alla <Span style="text-decoration:underline">seconda</span> tastiera, avrà come nome: ''midiC3D0''. Qualora collegassimo un terzo dispositivo Midi estreno, avremmo un ulteriore file-device "''midiC4D0''", e così via.
Riga 67: Riga 137:
 
A questo punto, però, se proviamo ad inserire due o più ''OPEN.... Watch'', ciascuno per ogni file-device creato e relativo ad un dispositivo Midi esterno, non potremmo intercettare i dati provenienti dalla seconda (o eventualmente anche da una terza tastiera) collegata; sia perché saremmo costretti a prevedere ''una'' sola subroutine '' File_Read()'', sia perché - pur inserendo in detta sub-routine un ''Read'' per lo stream proveniente da ciascun file-device, riusciremmo ad intercettare <Span style="text-decoration:underline">soltanto</span> i dati del primo file-device.
 
A questo punto, però, se proviamo ad inserire due o più ''OPEN.... Watch'', ciascuno per ogni file-device creato e relativo ad un dispositivo Midi esterno, non potremmo intercettare i dati provenienti dalla seconda (o eventualmente anche da una terza tastiera) collegata; sia perché saremmo costretti a prevedere ''una'' sola subroutine '' File_Read()'', sia perché - pur inserendo in detta sub-routine un ''Read'' per lo stream proveniente da ciascun file-device, riusciremmo ad intercettare <Span style="text-decoration:underline">soltanto</span> i dati del primo file-device.
  
<P>Dobbiamo allora trovare un'altra strategia: usare la funzione ''Exec'' per lanciare due sotto-programmi Gambas autonomi, resi ''demoni'' (quindi invisibili), con i quali gestire i dati, intercettati da ciascun file-device (opportunamente aperto in ''Watch'') delle tastiere collegate, inviando i medesimi allo standard output del sistema. Il programma principale si preoccuperà, pertanto, solo di intercettare dallo standard output del sistema i dati provenienti da ciascun <Span style="text-decoration:underline">distinto</span> sotto-programma</p>
+
<P>Dobbiamo allora trovare un'altra strategia.
<P>Scriveremo allora i due piccoli sotto-programmi in questo modo:</p>
+
Vi possono essere diverse modalità che vedremo nel corso della lettura della corrente pagina. Per ora ne cominciamo a vedere un paio.
 +
 
  
''<Font Color= #006400>' Gambas-3 class file''</font>
+
====Utilizzando una Classe specifica aggiuntiva====
''<Font Color= #006400>' '''Primo''' sotto-programma''</font>
+
E' possibile utilizzare una Classe specifica agggiuntiva, da noi appositamente creata, nella quale saranno poste le istruzioni di apertura e di "''osservazione''" del file successivo, o di quelli successivi (se più di due), al primo.
 +
Nella Classe, ovvero nel Modulo principale si porrà sotto osservazione il primo file-device Midi ed avremo questo codice essenziale:
 +
Private fl As File
 +
Public cl As New Class1
 
   
 
   
 
   
 
   
  '''Private''' primo As File
+
  Public Sub Main()
'''Private''' out As Stream
 
 
   
 
   
 +
<FONT Color=gray>' ''Invochiamo la sub-procedura della Classe secondaria per porre sotto osservazione il secondo file-device Midi:''</font>
 +
  cl.OsservaMidi()
 
   
 
   
  '''PUBLIC''' Sub Form_Open()
+
  <FONT Color=gray>' ''Poniamo sotto osservazione il primo file-device Midi:''</font>
 +
  fl = Open "/dev/snd/midiC'''2'''D0" For Read Watch
 +
 +
End
 
   
 
   
''<Font Color= #006400>' rendiamo "demone" il sotto-programma:''</font>
 
  Application.Daemon = True
 
 
   
 
   
  ''<Font Color= #006400>' Apre lo standard output del sistema per la scrittura.''</font>
+
  Public Sub File_Read()
  out = Open "/dev/stdout" For Write
 
 
   
 
   
''<Font Color= #006400>' Apre lo il file-device della prima tastiera esterna collegata e resta in attesa.''</font>
+
  Dim b As Byte
    primo = Open "/dev/snd/midiC'''2'''D0" For Watch
 
 
   
 
   
  '''END'''
+
  Read #fl, b
 +
  If b > 239 then Return
 +
  Print b
 +
 
 +
  End
 
   
 
   
 
   
 
   
  '''PUBLIC''' Sub File_read() ''<Font Color= #006400>' Se potrà essere letto almeno un byte dal file-device, verrà scatenata questa subroutine.''</font>
+
  Public Sub Application_Read()   <Font Color=gray>' ''Questo Evento è sollevato premendo il tasto "Enter" o "Invio" della tastiera.''</font>
 
   
 
   
  Dim by As Byte
+
  <Font Color=gray>' ''Chiude il file-device:''</font>
 +
  fl.Close
 
    
 
    
  ''<Font Color= #006400>' Legge i dati intercettati dal file-device che rappresenta la porta virtuale della prima tastiera esterna collegata.''</font>
+
  <Font Color=gray>' ''Invoca la funzione della Classe secondaria, per chiudere il file-device Midi da essa aperto:''</font>
   Read #primo, by
+
   Chiude2()
 
   
 
   
  ''<Font Color= #006400>' Filtra i byte "''Sensing Active''" e degli altri eventi qui non necessari.''</font>
+
  <Font Color=gray>' ''Chiude il programma:''</font>
   If by > 239 Then
+
   Quit
    Return
 
  Endif
 
 
   
 
   
  ''<Font Color= #006400>' Scrive quei dati nello standard output del sistema.''</font>
+
  End
    Write #out, by As Byte
 
 
'''END'''
 
 
 
<P>Salvato il codice sopra descritto come primo sotto-programma a se stante (lo chiameremo per esempio: "primo_sotto"), scriviamo ora il codice del secondo sotto-programma per la ricezione dei dati provenienti dal secondo dispositivo Midi esterno:</p>
 
  
''<Font Color= #006400>' Gambas-3 class file''</font>
+
Nella Classe secondaria specifica porremo sotto ''osservazione'' il secondo file-device Midi ed avremo questo codice essenziale:
  ''<Font Color= #006400>' '''Secondo''' sotto-programma''</font>
+
  Private fl As File
 
   
 
   
 
   
 
   
  '''Private''' secondo As File
+
  Public Procedure OsservaMidi()
'''Private''' out As Stream
 
 
   
 
   
 +
  fl = Open "/dev/snd/midiC'''3'''D0" For Read Watch
 
   
 
   
  '''PUBLIC''' Sub Form_Open()
+
  End
 
 
''<Font Color= #006400>' rendiamo "demone" il sotto-programma:''</font>
 
  Application.Daemon = True
 
 
   
 
   
''<Font Color= #006400>' Apre lo standard output del sistema per la scrittura.''</font>
 
  out = Open "/dev/stdout" For Write
 
 
   
 
   
  ''<Font Color= #006400>' Apre lo il file-device della seconda tastiera esterna collegata e resta in attesa.''</font>
+
  Public Sub File_Read()
    secondo = Open "/dev/snd/midiC'''3'''D0" For Watch
 
 
   
 
   
'''END'''
+
  Dim b As Byte
 
   
 
   
 +
  Read #fl, b
 +
  If b > 239 then Return
 +
  Print b
 +
 
 +
End
 
   
 
   
'''PUBLIC''' Sub File_read() ''<Font Color= #006400>' Se potrà essere letto almeno un byte dal file-device, verrà scatenata questa subroutine.''</font>
 
 
Dim by As Byte
 
 
    
 
    
  ''<Font Color= #006400>' Legge i dati intercettati dal file-device che rappresenta la porta virtuale della seconda tastiera esterna collegata.''</font>
+
  Public Procedure Chiude2()
  Read #secondo, by
 
 
   
 
   
  ''<Font Color= #006400>' Filtra i byte "''Sensing Active''" e degli altri eventi qui non necessari.''</font>
+
  <Font Color=gray>' ''Chiude il file-device:''</font>
   If by > 239 Then
+
   fl.Close
    Return
 
  Endif
 
 
   
 
   
  ''<Font Color= #006400>' Scrive quei dati nello standard output del sistema.''</font>
+
  End
    Write #out, by As Byte
 
 
'''END'''
 
  
<P>Proceduto a salvare con il nome "secondo_sotto" anche questo secondo sotto-programma autonomo, scriviamo il codice del programma principale, che avrà il compito, come già detto, di intercettare a sua volta i dati inviati dai due sotto-programmi allo standar output del sistema:</p>
 
  
''<Font Color= #006400>' Gambas-3 class file''</font>
+
====Usare la funzione ''Exec'' per lanciare due sotto-programmi Gambas autonomi====
  ''<Font Color= #006400>' Programma '''principale'''''</font>
+
Si potrà, dunque, usare la funzione ''Exec'' per lanciare due sotto-programmi Gambas autonomi, resi ''demoni'' (quindi invisibili), con i quali gestire i dati, intercettati da ciascun file-device (opportunamente aperto in ''Watch'') delle tastiere collegate, inviando i medesimi allo standard output del sistema. Il programma principale si preoccuperà, pertanto, solo di intercettare dallo standard output del sistema i dati provenienti da ciascun <Span style="text-decoration:underline">distinto</span> sotto-programma</p>
 +
<P>Scriveremo allora i due piccoli sotto-programmi in questo modo:</p>
 +
  <Font Color= #006400>' '''''Primo''' sotto-programma''</font>
 
   
 
   
 +
Private primo As File
 +
Private out As Stream
 
   
 
   
'''Private''' prm as process
 
'''Private''' scnd as process
 
 
   
 
   
 +
Public Sub Main()
 
   
 
   
  '''PUBLIC''' Sub Form_Open()
+
  <Font Color=gray>' ''Rende "demone" il sotto-programma:''</font>
 +
  Application.Daemon = True
 
   
 
   
  ''<Font Color= #006400>' Avvia come processo il primo ed il secondo sotto-programma''</font>
+
  <Font Color=gray>' ''Apre lo standard output del sistema per la scrittura.''</font>
''<Font Color= #006400>' che abbiamo salvato, per esempio, nella Home.''</font>
+
  out = Open "/dev/stdout" For Write
  prm = Exec ["''percorso/primo_sotto.gambas''"] For Read As "primo"
 
 
   
 
   
    scnd = Exec ["''percorso/secondo_sotto.gambas''"] For Read As "secondo"
+
<Font Color=gray>' ''Apre lo il file-device della prima tastiera esterna collegata e resta in attesa.''</font>
 +
  primo = Open "/dev/snd/midiC'''2'''D0" For Watch
 
   
 
   
  '''END'''
+
  End
 
   
 
   
 
   
 
   
  '''PUBLIC''' Sub primo_read() ''<Font Color= #006400>' Se potrà essere almeno un byte dallo standard output del processo "primo", verrà sollevata questa subroutine.''</font>
+
  Public Sub File_read() ''<Font Color= #006400>' Se potrà essere letto almeno un byte dal file-device, verrà scatenata questa subroutine.''</font>
 
   
 
   
Dim by As Byte
+
  Dim by As Byte
 
    
 
    
  ''<Font Color= #006400>' Legge i dati scritti dal programma ''primo_sotto''.''</font>
+
  <Font Color= #006400>' ''Legge i dati intercettati dal file-device che rappresenta la porta virtuale della prima tastiera esterna collegata.''</font>
   Read #prm, by
+
  Read #primo, by
 +
 +
<Font Color= #006400>' ''Filtra i byte "''Sensing Active''" e degli altri eventi qui non necessari.''
 +
' ''Scrive quindi quei dati nello standard output del sistema:''</font>
 +
   If by < 240 Then Write #out, by As Byte
 
   
 
   
  ''<Font Color= #006400>' Qui per soli fini didattici semplicemente scriviamo in console i dati intercettati.''</font>
+
End
    Print by
+
 
 
+
Salvato il codice sopra descritto come primo sotto-programma a se stante (lo chiameremo per esempio: "primo_sotto"), scriviamo ora il codice del secondo sotto-programma per la ricezione dei dati provenienti dal secondo dispositivo Midi esterno:
'''END'''
+
<Font Color= #006400>' '''''Secondo''' sotto-programma''</font>
 
   
 
   
 +
Private secondo As File
 +
Private out As Stream
 
   
 
   
'''PUBLIC''' Sub secondo_read() ''<Font Color= #006400>' Se potrà essere almeno un byte dallo standard output del processo "secondo", verrà sollevata questa subroutine.''</font>
 
 
   
 
   
  Dim by As Byte
+
  Public Sub Main()
 
    
 
    
  ''<Font Color= #006400>' Legge i dati scritti dal programma ''secondo_sotto''.''</font>
+
  <Font Color=gray>' ''Rende "demone" il sotto-programma:''</font>
   Read #scnd, by
+
  Application.Daemon = True
 +
 +
<Font Color=gray>' ''Apre lo standard output del sistema per la scrittura.''</font>
 +
  out = Open "/dev/stdout" For Write
 +
 +
<Font Color=gray>' ''Apre lo il file-device della seconda tastiera esterna collegata e resta in attesa.''</font>
 +
   secondo = Open "/dev/snd/midiC'''3'''D0" For Watch
 +
 +
End
 +
 +
 +
Public Sub File_read() ''<Font Color= #006400>' Se potrà essere letto almeno un byte dal file-device, verrà scatenata questa subroutine.''</font>
 
   
 
   
  ''<Font Color= #006400>' Qui per soli fini didattici semplicemente scriviamo in console i dati intercettati.''</font>
+
  Dim by As Byte
    Print by
+
 
 
+
<Font Color=gray>' ''Legge i dati intercettati dal file-device che rappresenta la porta virtuale della seconda tastiera esterna collegata.''</font>
'''END'''
+
  Read #secondo, by
 
   
 
   
 +
<Font Color=gray>' ''Filtra i byte "''Sensing Active''" e degli altri eventi qui non necessari.''
 +
' ''Scrive quei dati nello standard output del sistema:''</font>
 +
  If by < 240 Then Write #out, by As Byte
 
   
 
   
  '''PUBLIC''' Sub Button1_Click() ''<Font Color= #006400>' Per chiudere regolarmente i sotto-programmi ed il programma principale''</font>
+
  End
 +
Proceduto a salvare con il nome "secondo_sotto" anche questo secondo sotto-programma autonomo, scriviamo il codice del programma principale, che avrà il compito, come già detto, di intercettare a sua volta i dati inviati dai due sotto-programmi allo standar output del sistema:</p>
 +
<Font Color= #006400>' ''Programma '''principale'''''</font>
 
   
 
   
  prm.kill()
+
Private prm as process
  scnd.kill()
+
Private scnd as process
  Me.close()
 
 
   
 
   
'''END'''
 
 
 
----
 
 
 
==Ricezione mediante ''Process''==
 
 
E' possibile in alternativa ricevere i dati dal file-device utilizzando non ''Open'', ma ''Process''.
 
 
'''Private''' varProc As Process
 
 
   
 
   
 +
Public Sub Main()
 
   
 
   
  '''Public''' Sub ToggleButton1_Click()
+
  <Font Color=gray>' ''Avvia come processo il primo ed il secondo sotto-programma salvato, per esempio, nella Home.''</font>
 +
  prm = Exec [User.Home &/ "primo_sotto.gambas"] For Read As "primo"
 
   
 
   
  If Last.value Then
+
  scnd = Exec [User.Home &/ "secondo_sotto.gambas''"] For Read As "secondo"
 
   
 
   
    varProc = Exec ["cat", "/dev/snd/midiC2D0"] For Read As "varproc"
+
End
 
   
 
   
' ''<Font Color= #006400>oppure:''
 
' ''varProc = Shell "cat /dev/snd/midiC2D0" For Read As "varproc"''</font>
 
 
   
 
   
    Else
+
  Public Sub primo_Read() ''<Font Color=gray>' Se potrà essere almeno un byte dallo standard output del processo "primo", verrà sollevata questa subroutine.''</font>
  ' ''<Font Color= #006400>cliccando di nuovo sul tasto, il flusso è bloccato,''
 
' ''ma cliccandoci ancora il processo sarà riattivato:''</font>
 
    varProc.Kill
 
 
   
 
   
  Endif
+
  Dim by As Byte
 
    
 
    
  '''End'''
+
  <Font Color=gray>' ''Legge i dati scritti dal programma ''primo_sotto''.''</font>
 +
  Read #prm, by
 +
 +
<Font Color=gray>' ''Qui per soli fini didattici semplicemente scriviamo in console i dati intercettati.''</font>
 +
  Print by
 +
 +
End
 
   
 
   
 
   
 
   
  '''Public''' Sub varproc_Read()
+
  Public Sub secondo_Read() ''<Font Color= #006400>' Se potrà essere almeno un byte dallo standard output del processo "secondo", verrà sollevata questa subroutine.''</font>
 +
 +
  Dim by As Byte
 
    
 
    
   Dim dato As Byte
+
<Font Color=gray>' ''Legge i dati scritti dal programma ''secondo_sotto''.''</font>
 +
   Read #scnd, by
 +
 +
<Font Color=gray>' ''Qui per soli fini didattici semplicemente scriviamo in console i dati intercettati.''</font>
 +
  Print by
 +
 
 +
End
 
   
 
   
' ''<Font Color= #006400>Leggiamo il file-device e scriviamo il valore ottenuto nella variabile "dato".</font>
 
    Read #varProc, dato  ' |[[#Note|1]]|
 
 
   
 
   
 +
Public Sub Button1_Click() ''<Font Color= #006400>' Per chiudere regolarmente i sotto-programmi ed il programma principale''</font>
 
   
 
   
<Font Color=#006400>' ''Poiché la tastiera Midi invia in continuazione il messaggio Midi 254 (Active Sensing) ed altri messaggi che a noi qui non interessano, li escludiamo.</font>''
+
  prm.kill()
    If by < 240 Then
+
  scnd.kill()
<Font Color=#006400>' ''Per fini didattici ci limitiamo qui solo a scrivere il dato in console per vederlo.</font>
+
  Me.close()
    Print "byte inviato dalla tastiera = "; dato
 
    Endif
 
 
   
 
   
  '''End'''
+
  End
  
  
Riga 255: Riga 337:
  
  
==Ricezione mediante ''Pipe''==
+
==Ricezione mediante ''Process''==
 
+
E' possibile in alternativa ricevere i dati dal file-device utilizzando non ''Open'', ma ''Process''.
E' possibile anche utilizzare il comando "''Pipe''". Tutto resta come con ''Open''.
+
  Private pro As Process
 
+
  '''Private''' varFile As File
 
 
   
 
   
  '''Public''' Sub Form_Open()
+
  Public Sub ToggleButton1_Click()
 
   
 
   
  varFile = <Font Color=#B22222>Pipe</font> "/dev/snd/midiC2D0" For Read Watch
+
  If Last.value Then
 +
    varProc = Exec ["cat", "/dev/snd/midiC2D0"] For Read As "Processo"
 +
<Font Color=gray>' ''oppure:''
 +
' '''pro = Shell "cat /dev/snd/midiC2D0" For Read As "Processo"'''</font>
 +
  Else
 +
<Font Color=gray>' ''cliccando di nuovo sul tasto, il flusso è bloccato, ma - cliccandoci ancora - il processo sarà riattivato:''</font>
 +
    pro.Kill
 +
  Endif
 
   
 
   
  '''End'''
+
  End
 
   
 
   
 
   
 
   
  '''Public''' Sub File_Read() ' |[[#Note|2]]|
+
  Public Sub Processo_Read()
 
    
 
    
 
   Dim dato As Byte
 
   Dim dato As Byte
 
   
 
   
  Read #varFile, dato  ' |[[#Note|1]]|
+
  While Not Eof(varProc)
+
<Font Color=gray>' ''Leggiamo il file-device e scriviamo il valore ottenuto nella variabile "dato":''</font>
  If by < 240 Then
+
    Read #pro, dato
    Print "byte inviato dalla tastiera = "; dato
+
  <Font Color=gray>' ''Poiché la tastiera Midi invia in continuazione il messaggio Midi 254 (Active Sensing) ed altri messaggi che a noi qui non interessano, li escludiamo:</font>''
  Endif
+
    If dato < 240 Then Print "byte inviato dalla tastiera = "; dato
   
+
  Wend
'''End'''
+
 
 +
  End
 +
Se non si vuole usare la variabile dichiarata come ''File'', o nell'altro esempio la variabile dichiarata come ''Process'', in alternativa è possibile usare '''#Last''', il quale, come è noto, restituisce un riferimento all'ultimo oggetto chiamato da un evento:
 +
Read #Last, dato
  
  
Riga 286: Riga 377:
 
==Ricezione mediante ''Object.Attach''==
 
==Ricezione mediante ''Object.Attach''==
 
Potremo anche usare il metodo ''Object.Attach'' per gestire l'evento della ricezione dei dati Midi, associando l'oggetto file alla definizione dell'evento.
 
Potremo anche usare il metodo ''Object.Attach'' per gestire l'evento della ricezione dei dati Midi, associando l'oggetto file alla definizione dell'evento.
 
+
  Private fl As File
  '''Private''' varFile As File
 
 
   
 
   
 
   
 
   
  '''Public''' Sub Form_Open()
+
  Public Sub Main()
 
   
 
   
  varFile = Open "/dev/snd/midiC2D0" For Read Watch
+
  fl = Open "/dev/snd/midiC2D0" For Read Watch
 
   
 
   
  Object.Attach(varFile, FMain, "eventoMidi")
+
  Object.Attach(varFile, FMain, "EventoMidi")
 
   
 
   
  '''End'''
+
  End
 
   
 
   
 
   
 
   
  '''Public''' Sub eventoMidi_Read()
+
  Public Sub EventoMidi_Read()
 
    
 
    
Dim b As Byte
+
  Dim b As Byte
 
    
 
    
   Read #varFile, b
+
   Read #fl, b
  <Font Color=#006400>' ''...il solito filtro:''</font>
+
  <Font Color=gray>' ''...il solito filtro:''</font>
   If b > 239 Then Return
+
   If b < 240 Then Print b
<Font Color=#006400>' ''Per fini didattici ci limitiamo qui solo a scrivere il dato in console per vederlo:''</font>
 
  Print b
 
 
   
 
   
  '''End'''
+
  End
  
  
 
===Ricezione dei dati da più dispositivi Midi esterni===
 
===Ricezione dei dati da più dispositivi Midi esterni===
 
Con il metodo ''Object.Attach'' potremo agevolmente ricevere e gestire i dati Midi, provenienti da due o più tastiere esterne.
 
Con il metodo ''Object.Attach'' potremo agevolmente ricevere e gestire i dati Midi, provenienti da due o più tastiere esterne.
 
+
  Private fl1 As File
 
+
  Private fl2 As File
  '''Private''' varFile1 As File
 
  '''Private''' varFile2 As File
 
 
   
 
   
 
   
 
   
  '''Public''' Sub Form_Open()
+
  Public Sub Main()
 
   
 
   
   varFile1 = Open "/dev/snd/midiC2D0" For Read Watch
+
   fl1 = Open "/dev/snd/midiC2D0" For Read Watch
  <Font Color=#006400>' ''Viene associato l'oggetto ''File'' alla denominazione del proprio evento:''</font>
+
  <Font Color=gray>' ''Viene associato l'oggetto ''File'' alla denominazione del proprio evento:''</font>
   Object.Attach(varFile1, FMain, "eventoMidi1")
+
   Object.Attach(fl1, FMain, "EventoMidi1")
 
   
 
   
+
   fl2 = Open "/dev/snd/midiC3D0" For Read Watch
   varFile2 = Open "/dev/snd/midiC3D0" For Read Watch
+
  <Font Color=gray>' ''Viene associato l'oggetto ''File'' alla denominazione del proprio evento:''</font>
  <Font Color=#006400>' ''Viene associato l'oggetto ''File'' alla denominazione del proprio evento:''</font>
+
   Object.Attach(fl2, FMain, "EventoMidi2")
   Object.Attach(varFile2, FMain, "eventoMidi2")
 
 
    
 
    
  '''End'''
+
  End
 
   
 
   
 
   
 
   
 
  <Font Color=#006400>' ''Vengono intercettati i dati provenienti dalla prima tastiera esterna:''</font>  
 
  <Font Color=#006400>' ''Vengono intercettati i dati provenienti dalla prima tastiera esterna:''</font>  
  '''Public''' Sub eventoMidi1_Read()
+
  Public Sub eventoMidi1_Read()
 
    
 
    
 
   Dim b1 As Byte
 
   Dim b1 As Byte
 
    
 
    
   Read #varFile, b1
+
   Read #fl1, b1
  <Font Color=#006400>' ''...il solito filtro:</font>
+
  <Font Color=gray>' ''...il solito filtro:</font>
   If b1 > 239 Then Return
+
   If b1 < 240 Then Print b1
<Font Color=#006400>' ''Per fini didattici ci limitiamo qui solo a scrivere il dato in console per vederlo:</font>
 
  Print b1
 
 
   
 
   
  '''End'''
+
  End
 
   
 
   
 
   
 
   
 
  <Font Color=#006400>' ''Vengono intercettati i dati provenienti dalla seconda tastiera esterna:''</font>
 
  <Font Color=#006400>' ''Vengono intercettati i dati provenienti dalla seconda tastiera esterna:''</font>
  '''Public''' Sub eventoMidi2_Read()
+
  Public Sub EventoMidi2_Read()
 
    
 
    
 
  Dim b2 As Byte
 
  Dim b2 As Byte
 
    
 
    
   Read #varFile2, b2
+
   Read #fl2, b2
  <Font Color=#006400>' ''...il solito filtro:</font>
+
  <Font Color=gray>' ''...il solito filtro:</font>
   If b2 > 239 Then Return
+
   If b2 < 240 Then Print b2
<Font Color=#006400>' ''Per fini didattici ci limitiamo qui solo a scrivere il dato in console per vederlo:</font>
 
  Print b2
 
 
   
 
   
  '''End'''
+
  End
  
  
Riga 367: Riga 448:
 
==Ricezione mediante ''Observer''==
 
==Ricezione mediante ''Observer''==
 
Potremo anche usare il metodo ''Observer'' per gestire l'evento della ricezione dei dati Midi.
 
Potremo anche usare il metodo ''Observer'' per gestire l'evento della ricezione dei dati Midi.
 
+
  Private varFile As File
  '''Private''' varFile As File
 
 
   
 
   
 
   
 
   
  '''Public''' Sub Form_Open()
+
Public Sub Main()
 
   
 
   
Dim oss As Observer
+
  Dim oss As Observer
 
   
 
   
   varFile = Open "/dev/snd/midiC2D0" For Read Watch
+
   fl = Open "/dev/snd/midiC2D0" For Read Watch
 
   
 
   
   oss = New Observer(varFile) As "eventoMidi"
+
   oss = New Observer(fl) As "EventoMidi"
 
   
 
   
  '''End'''
+
  End
 
   
 
   
 
   
 
   
  '''Public''' Sub eventoMidi_Read()
+
  Public Sub eventoMidi_Read()
 
    
 
    
Dim b As Byte
+
  Dim b As Byte
 
    
 
    
   Read #varFile, b
+
   Read #fl, b
  <Font Color=#006400>' ''...il solito filtro:</font>
+
  <Font Color=gray>' ''...il solito filtro:</font>
   If b > 239 Then Return
+
   If b < 240 Print b
<Font Color=#006400>' ''Per fini didattici ci limitiamo qui solo a scrivere il dato in console per vederlo:</font>
 
  Print b
 
 
   
 
   
  '''End'''
+
  End
  
  
 
===Ricezione dei dati da più dispositivi Midi esterni===
 
===Ricezione dei dati da più dispositivi Midi esterni===
 
Ugualmente, con ''Observer'' potremo ricevere e gestire i dati Midi, provenienti da due o più tastiere esterne.
 
Ugualmente, con ''Observer'' potremo ricevere e gestire i dati Midi, provenienti da due o più tastiere esterne.
 
+
  Private fl1 As File
 
+
  Private fl2 As FileForm_Open
  '''Private''' varFile1 As File
 
  '''Private''' varFile2 As File
 
 
   
 
   
 
   
 
   
  '''Public''' Sub Form_Open()
+
Public Sub Form_Open()
 
   
 
   
Dim oss1 As Observer
+
  Dim obs1 As Observer
Dim oss2 As Observer
+
  Dim obs2 As Observer
 
   
 
   
   varFile1 = Open "/dev/snd/midiC2D0" For Read Watch
+
   fl1 = Open "/dev/snd/midiC2D0" For Read Watch
 
   
 
   
  <Font Color=#006400>' ''Viene "osservato" l'oggetto ''File'' per l'evento:''</font>
+
  <Font Color=gray>' ''Viene "osservato" l'Oggetto ''File'' per l'evento:''</font>
   oss1 = New Observer(varFile1) As "eventoMidi1"
+
   obs1 = New Observer(varFile1) As "EventoMidi1"
 +
 
 +
  fl2 = Open "/dev/snd/midiC3D0" For Read Watch
 
   
 
   
 +
<Font Color=gray>' ''Viene "osservato" l'oggetto ''File'' per l'evento:''</font>
 +
  obs2 = New Observer(fl2) As "EventoMidi2"
 +
 
 +
End
 
   
 
   
  varFile2 = Open "/dev/snd/midiC3D0" For Read Watch
 
 
   
 
   
  <Font Color=#006400>' ''Viene "osservato" l'oggetto ''File'' per l'evento:''</font>
+
  <Font Color=gray>' ''Vengono intercettati i dati provenienti dalla prima tastiera esterna:''</font>  
  oss2 = New Observer(varFile2) As "eventoMidi2"
+
Public Sub EventoMidi1_Read()
 
    
 
    
'''End'''
+
  Dim b1 As Byte
 
   
 
   
 +
  Read #fl1, b1
 +
<Font Color=gray>' ''...il solito filtro:</font>
 +
  If b1 < 240 Print b1
 
   
 
   
  <Font Color=#006400>' ''Vengono intercettati i dati provenienti dalla prima tastiera esterna:''</font>
+
  End
'''Public''' Sub eventoMidi1_Read()
 
 
 
Dim b1 As Byte
 
 
 
  Read #varFile1, b1
 
<Font Color=#006400>' ''...il solito filtro:</font>
 
  If b1 > 239 Then Return
 
<Font Color=#006400>' ''Per fini didattici ci limitiamo qui solo a scrivere il dato in console per vederlo:</font>
 
  Print b1
 
 
'''End'''
 
 
   
 
   
 
   
 
   
 
  <Font Color=#006400>' ''Vengono intercettati i dati provenienti dalla seconda tastiera esterna:''</font>
 
  <Font Color=#006400>' ''Vengono intercettati i dati provenienti dalla seconda tastiera esterna:''</font>
  '''Public''' Sub eventoMidi2_Read()
+
  Public Sub EventoMidi2_Read()
 
    
 
    
 
  Dim b2 As Byte
 
  Dim b2 As Byte
 
    
 
    
   Read #varFile2, b2
+
   Read #fl2, b2
  <Font Color=#006400>' ''...il solito filtro:</font>
+
  <Font Color=gray>' ''...il solito filtro:</font>
   If b2 > 239 Then Return
+
   If b2 < 240 Print b2
<Font Color=#006400>' ''Per fini didattici ci limitiamo qui solo a scrivere il dato in console per vederlo:</font>
 
  Print b2
 
 
   
 
   
  '''End'''
+
  End
  
  
Riga 459: Riga 530:
 
Dovremo tenere conto che sarà ovviamente necessario integrare la parte ''extra ALSA'' (come l'abbiamo vista nei due precedenti paragrafi) di ricezione dei dati con la parte di utilizzo delle funzioni delle API di ALSA per inviargli i dati necessari dell'evento NoteON, così da ottenere il suono finale.
 
Dovremo tenere conto che sarà ovviamente necessario integrare la parte ''extra ALSA'' (come l'abbiamo vista nei due precedenti paragrafi) di ricezione dei dati con la parte di utilizzo delle funzioni delle API di ALSA per inviargli i dati necessari dell'evento NoteON, così da ottenere il suono finale.
  
<P>Pertanto porremo nella classe principale una routine di avvio con le dichiarazioni delle variabili e delle costanti, con i primi riferimenti ad ALSA e con la riga per aprire il file-device (midiC2D0) che rappresenta la porta virtuale della tastiera esterna:</p>
+
Per questo argomento rinviamo alla seguente pagina della Wiki: [[Ricevere dati Midi senza funzioni di ALSA e inviarli mediante le funzioni di Alsa]]
 
 
 
 
''<Font Color= #006400>' Gambas-3 class file''</font>
 
 
 
''<Font Color= #006400>' impostare queste costanti per suonare''</font>
 
Const outdevice As Integer = 14      ''<Font Color= #006400>' 14=midi out - ALSA''</font>
 
Const outport As Integer = 0          ''<Font Color= #006400>' porta d'uscita, solitamente 0''</font>
 
 
 
Public alsa As CAlsa              ''<Font Color= #006400>' classe che incapsula le funzioni alsa''</font>
 
 
Public varFile As File
 
 
'''Private''' bb[4] As Byte
 
'''Private''' n As Integer
 
 
 
 
'''Public''' Sub Form_Open()
 
 
  ''<Font Color= #006400>' creare ("istanziare") la classe per poterla usare''</font>
 
  alsa = New CAlsa As "alsa"
 
  ''<Font Color= #006400>' aprire alsa e assegnare un nome''</font>
 
  alsa.alsa_open("Gambas drum machine")
 
  ''<Font Color= #006400>' scegliere la periferica su cui suonare''</font>
 
  alsa.setdevice(outdevice, outport)
 
 
''''''''''
 
 
 
  varFile = Open "/dev/snd/midiC2D0" For Read Watch
 
 
 
End
 
 
 
<BR>Quindi inseriamo la parte relativa all'invio dei dati Midi alle funzioni della classe CAlsa.class, preposte alla costruzione dell'evento Midi, per il suo successivo invio ad ALSA.
 
<BR>A fini esemplificativi invieremo semplicemente i dati utili per un evento NoteON.
 
 
 
'''Public''' Sub File_Read()
 
 
 
  Dim by As Byte
 
 
 
  Read #varFile, by
 
 
 
  If by > 253 Then Return  ''<Font Color= #006400>' In questo modo eliminiamo il byte 254 (Active Sensing) costantemente inviato dalla tastiera esterna.''</font>
 
 
 
  n += 1
 
 
  bb[n] = by
 
 
 
 
''<Font Color= #006400>' Quando saranno giunti i tre valori costitutivi del NoteON,''</font>
 
''<Font Color= #006400>' si passano alla funzione seguente per la costruzione dell'evento Midi.''</font>
 
  If n = 3 Then
 
 
''<Font Color= #006400>' Questa operazione estrae il valore del canale dal messaggio di Stato inviato dalla tastiera.''</font>
 
  bb[1] = bb[1] AND 15           
 
 
 
''<Font Color= #006400>' La funzione che segue invia i dati alla funzione alsa.noteon.''</font>
 
    alsa.noteon(bb[1], bb[2], bb[3])
 
    n = 0
 
  Endif
 
 
 
'''End'''
 
 
 
 
 
 
 
=Note=
 
[1] Se non si vuole usare la variabile dichiarata come ''File'', o nell'altro esempio la variabile dichiarata come ''Process'', in alternativa è possibile usare '''#Last''', il quale, come è noto, restituisce un riferimento all'ultimo oggetto chiamato da un evento:
 
Read #Last, dato
 
 
 
[2] Se si scrive ''Sub Pipe_Read()'', come suggerisce la guida Gambas per intercettare l'evento, sembra non funzionare.
 

Versione attuale delle 05:38, 13 gen 2024

Introduzione

Presentiamo qui una soluzione alternativa per ricevere da un dispositivo esterno Midi (ad esempio una tastiera Midi) i messaggi MIDI senza l'utilizzo delle funzioni esterne di ALSA, anche se, però, si andrà a leggere il flusso di dati grezzi Midi direttamente da un file-device specifico comunque generato da Alsa.

Questa modalità di gestione, ancorché senza l'uso delle funzioni dell'API di Alsa, è per taluni aspetti analogo a quell del subsistema RawMidi di Alsa.


Ricezione mediante la lettura del file-device con Open

Dopo aver collegato la tastiera Midi al computer, intendiamo operare sul file-device provvisoriamente creato dal sistema e corrispondente alla predetta tastiera. Con la parola chiave Watch, dopo aver aperto con la funzione OPEN il file-device, faremo si che il programma resti in attesa di un messaggio Midi in arrivo dalla tastiera medesima.

Il file-device temporaneo corrisponde al file-device dei dispositivi rawmidi, che controllano flussi di eventi Midi 'nudi' e privi di temporizzazione. Il modello di file-device di questo tipo è:

midiCcDd

laddove le lettere:

  • "C" sta per Card, ossia la scheda audio;
  • "c" è il numero identificativo (0 - 3) assegnato alla scheda audio;
  • "D" sta per Device, il dispositivo audio;
  • "d" è il numero identificativo assegnato al dispositivo (0 - 16);

esempio: midiC2D0

e si trova nel seguente percorso: /dev/snd/midiCcDd.

V'è da dire che con alcuni kernel, l'inserimento di un dispositivo Midi esterno genera contestualmente - oltre al file-device sopra descritto (/dev/snd/midiCcDd) - anche i file-device "/dev/midi2", "/dev/dmidi2" o "/dev/dmmidi2". Detti ultimi tre file-device possono essere utilizzati e gestiti con le medesime modalità previste, e che saranno di seguito descritte, per il file-device /dev/snd/midiCcDd.


Lettura del file-device rawmidi

Per leggere il flusso di dati Midi grezzi che passa attraverso il predetto file-device, apriremo il file-device il lettura aggiungendo la parola "Watch". Se, dunque, potrà essere letto almeno un byte dal file-device, allora verrà chiamato l'evento File_Read():

Private mid As File


Public Sub Main()

 mid = Open "/dev/snd/midiC2D0" For Read Watch

End


Public Sub File_Read()
 
 Dim dato As Byte

' Legge dal file-device e scriviamo il valore ottenuto nella variabile "dato":
 Read #mid, dato

' Per fini didattici ci limitiamo qui solo a scrivere il dato in console per vederlo:
 Print "byte inviato dalla tastiera = "; dato

End


Public Sub Application_Read()   ' Questo Evento è sollevato premendo il tasto "Enter" o "Invio" della tastiera.
 
' Chiude il file-device:
 mid.Close
 
' Chiude il programma:
 Quit

End

Come si può notare, la ricezione dei dati dalla tastiera Midi esterna è avvenuta senza l'utilizzo di alcuna funzione ALSA, ma direttamente andando a "raccoglierli" dal file-device (comunque creato da Alsa) della porta virtuale della tastiera.


Mostriamo ora un esempio più complesso, nel quale verranno eliminati mediante un apposito filtro i dati aventi uno status byte identificativo superiore al valore 239 (in particolare per elimnare il continuo invio del messaggio Midi Active Sensing). Inoltre, si convertirà lo status byte dei Messaggi Midi autorizzati, affinché sia evidente il proprio numero di canale, e verranno mostrate altre informazioni relative ai dati ricevuti:

Private fl As File
 

Public Sub Main()

 Dim disp As String = "/dev/snd"

 If Dir(disp, "midi*", gb.Device).Count = 0 Then Error.Raise("Nessun dispositivo Midi esterno trovato !")

 disp &/= Dir(disp, "midi*", gb.Device)[0]

 fl = Open disp For Read Watch

End


Public Sub File_Read()
 
 Dim b As Byte
 
 Read #fl, b
 
 Select Case b
   Case 128 To 143
     b = b And 15
     Print "\nCanale", "Nota", "Velocità", " = NoteOFF"
   Case 144 To 159
     b = b And 15
     Print "\nCanale", "Nota", "Velocità", " = NoteON"
   Case 160 To 175
     b = b And 15
     Print "\nCanale", "Nota", "Velocità", " = Aftertouch Polyphonic"
   Case 176 To 191
     b = b And 15
     Print "\nCanale", "Parametro", "Valore", " = Control Change"
   Case 192 To 207
     b = b And 15
     Print "\nCanale", "parametro", Null, " = Program Change"
   Case 208 To 223
     b = b And 15
     Print "\nCanale", "Parametro", Null, " = Channel Aftertouch"
   Case 224 To 239
     b = b And 15
     Print "\nCanale", "Parametro", "Valore", " = Pitch Wheel"
   Case 240 To 255
     Return
 End Select
 
 Print b,
 
End


Public Sub Application_Read()   ' Questo Evento è sollevato premendo il tasto "Enter" o "Invio" della tastiera.

' Chiude il file-device:
 mid.Close
 
' Chiude il programma:
 Quit

End

Si fa presente, ad ogni modo, che la soluzione, presentata in questa pagina, seppur efficace, non è proprio ottimale per almeno le tre seguenti ragioni:

  • si va in ambiente extra ALSA, laddove invece si intendeva programmare utilizzando le API di ALSA;
  • ALSA non riesce ad intercettare più i dati provenienti dalla tastiera esterna Midi, essendo stati intercettati da noi con la presente modalità extra ALSA;
  • la presente soluzione extra ALSA potrebbe interferire o comunque creare problemi alle funzionalità del sistema ALSA.


Possiamo riportare, però, il caso di contemporaneo tentativo di ricezione dei dati da un medesimo dispositivo hardware Midi esterno con funzioni ALSA e con il presente metodo extra ALSA. Ebbene è stato accertato che in tale caso soltanto una delle procedure intercetterà i dati provenienti dal dispositivo Midi esterno. Se, ad esempio, verrà avviato prima il metodo extra ALSA e successivamente quello mediante le funzioni ALSA di ricezione (magari anche con due applicativi distinti), facendoli comunque funzionare contemporaneamente, l'applicativo che utilizza le funzioni di ALSA per ricevere i dati Midi, segnalerà in console il seguente errore:
"Subscribe inport: err=-11 (Resource temporarily unavailable)".


Ricezione dati da più dispositivi Midi esterni

Sino ad ora abbiamo considerato l'invio di messaggi Midi da un solo dispositivo esterno, per esempio una sola tastiera Midi. Come fare, però, se volessimo, con la modalità che stiamo ora trattando, ricevere e gestire i dati da più tastiere Midi esterne ?
Dobbiamo premettere che se colleghiamo al computer via USB un secondo dispositivo Midi esterno, possiamo notare che in /dev/snd comparirà un altro file-device simile a quello che creato dal sistema quando avevamo collegato la prima tastiera Midi esterna. Questa volta il nuovo file-device, che si riferisce alla seconda tastiera, avrà come nome: midiC3D0. Qualora collegassimo un terzo dispositivo Midi estreno, avremmo un ulteriore file-device "midiC4D0", e così via.

A questo punto, però, se proviamo ad inserire due o più OPEN.... Watch, ciascuno per ogni file-device creato e relativo ad un dispositivo Midi esterno, non potremmo intercettare i dati provenienti dalla seconda (o eventualmente anche da una terza tastiera) collegata; sia perché saremmo costretti a prevedere una sola subroutine File_Read(), sia perché - pur inserendo in detta sub-routine un Read per lo stream proveniente da ciascun file-device, riusciremmo ad intercettare soltanto i dati del primo file-device.

Dobbiamo allora trovare un'altra strategia. Vi possono essere diverse modalità che vedremo nel corso della lettura della corrente pagina. Per ora ne cominciamo a vedere un paio.

Utilizzando una Classe specifica aggiuntiva

E' possibile utilizzare una Classe specifica agggiuntiva, da noi appositamente creata, nella quale saranno poste le istruzioni di apertura e di "osservazione" del file successivo, o di quelli successivi (se più di due), al primo. Nella Classe, ovvero nel Modulo principale si porrà sotto osservazione il primo file-device Midi ed avremo questo codice essenziale:

Private fl As File
Public cl As New Class1


Public Sub Main()

' Invochiamo la sub-procedura della Classe secondaria per porre sotto osservazione il secondo file-device Midi:
 cl.OsservaMidi()

' Poniamo sotto osservazione il primo file-device Midi:
 fl = Open "/dev/snd/midiC2D0" For Read Watch

End


Public Sub File_Read()

 Dim b As Byte

 Read #fl, b
 If b > 239 then Return
 Print b
 
End


Public Sub Application_Read()   ' Questo Evento è sollevato premendo il tasto "Enter" o "Invio" della tastiera.

' Chiude il file-device:
 fl.Close
 
' Invoca la funzione della Classe secondaria, per chiudere il file-device Midi da essa aperto:
 Chiude2()

' Chiude il programma:
 Quit

End

Nella Classe secondaria specifica porremo sotto osservazione il secondo file-device Midi ed avremo questo codice essenziale:

Private fl As File


Public Procedure OsservaMidi()

 fl = Open "/dev/snd/midiC3D0" For Read Watch

End


Public Sub File_Read()

 Dim b As Byte

 Read #fl, b
 If b > 239 then Return
 Print b
 
End

 
Public Procedure Chiude2()

' Chiude il file-device:
 fl.Close

End


Usare la funzione Exec per lanciare due sotto-programmi Gambas autonomi

Si potrà, dunque, usare la funzione Exec per lanciare due sotto-programmi Gambas autonomi, resi demoni (quindi invisibili), con i quali gestire i dati, intercettati da ciascun file-device (opportunamente aperto in Watch) delle tastiere collegate, inviando i medesimi allo standard output del sistema. Il programma principale si preoccuperà, pertanto, solo di intercettare dallo standard output del sistema i dati provenienti da ciascun distinto sotto-programma

Scriveremo allora i due piccoli sotto-programmi in questo modo:

' Primo sotto-programma

Private primo As File
Private out As Stream


Public Sub Main()

' Rende "demone" il sotto-programma:
 Application.Daemon = True

' Apre lo standard output del sistema per la scrittura.
 out = Open "/dev/stdout" For Write

' Apre lo il file-device della prima tastiera esterna collegata e resta in attesa.
 primo = Open "/dev/snd/midiC2D0" For Watch

End


Public Sub File_read() ' Se potrà essere letto almeno un byte dal file-device, verrà scatenata questa subroutine.

 Dim by As Byte
 
' Legge i dati intercettati dal file-device che rappresenta la porta virtuale della prima tastiera esterna collegata.
 Read #primo, by

' Filtra i byte "Sensing Active" e degli altri eventi qui non necessari.
' Scrive quindi quei dati nello standard output del sistema:
 If by < 240 Then Write #out, by As Byte

End

Salvato il codice sopra descritto come primo sotto-programma a se stante (lo chiameremo per esempio: "primo_sotto"), scriviamo ora il codice del secondo sotto-programma per la ricezione dei dati provenienti dal secondo dispositivo Midi esterno:

' Secondo sotto-programma

Private secondo As File
Private out As Stream


Public Sub Main()
 
' Rende "demone" il sotto-programma:
 Application.Daemon = True

' Apre lo standard output del sistema per la scrittura.
 out = Open "/dev/stdout" For Write

' Apre lo il file-device della seconda tastiera esterna collegata e resta in attesa.
 secondo = Open "/dev/snd/midiC3D0" For Watch

End


Public Sub File_read() ' Se potrà essere letto almeno un byte dal file-device, verrà scatenata questa subroutine.

 Dim by As Byte
 
' Legge i dati intercettati dal file-device che rappresenta la porta virtuale della seconda tastiera esterna collegata.
 Read #secondo, by

' Filtra i byte "Sensing Active" e degli altri eventi qui non necessari.
' Scrive quei dati nello standard output del sistema:
 If by < 240 Then Write #out, by As Byte

End

Proceduto a salvare con il nome "secondo_sotto" anche questo secondo sotto-programma autonomo, scriviamo il codice del programma principale, che avrà il compito, come già detto, di intercettare a sua volta i dati inviati dai due sotto-programmi allo standar output del sistema:</p>

' Programma principale

Private prm as process
Private scnd as process


Public Sub Main()

' Avvia come processo il primo ed il secondo sotto-programma salvato, per esempio, nella Home.
 prm = Exec [User.Home &/ "primo_sotto.gambas"] For Read As "primo"

 scnd = Exec [User.Home &/ "secondo_sotto.gambas"] For Read As "secondo"

End


Public Sub primo_Read() ' Se potrà essere almeno un byte dallo standard output del processo "primo", verrà sollevata questa subroutine.

 Dim by As Byte
 
' Legge i dati scritti dal programma primo_sotto.
 Read #prm, by

' Qui per soli fini didattici semplicemente scriviamo in console i dati intercettati.
 Print by

End


Public Sub secondo_Read() ' Se potrà essere almeno un byte dallo standard output del processo "secondo", verrà sollevata questa subroutine.

 Dim by As Byte
 
' Legge i dati scritti dal programma secondo_sotto.
 Read #scnd, by

' Qui per soli fini didattici semplicemente scriviamo in console i dati intercettati.
 Print by
  
End


Public Sub Button1_Click() ' Per chiudere regolarmente i sotto-programmi ed il programma principale

 prm.kill()
 scnd.kill()
 Me.close()

End




Ricezione mediante Process

E' possibile in alternativa ricevere i dati dal file-device utilizzando non Open, ma Process.

Private pro As Process


Public Sub ToggleButton1_Click()

 If Last.value Then
   varProc = Exec ["cat", "/dev/snd/midiC2D0"] For Read As "Processo"
' oppure:
' pro = Shell "cat /dev/snd/midiC2D0" For Read As "Processo"
 Else
' cliccando di nuovo sul tasto, il flusso è bloccato, ma - cliccandoci ancora - il processo sarà riattivato:
   pro.Kill
 Endif

End


Public Sub Processo_Read()
 
 Dim dato As Byte

 While Not Eof(varProc)
' Leggiamo il file-device e scriviamo il valore ottenuto nella variabile "dato":
   Read #pro, dato
' Poiché la tastiera Midi invia in continuazione il messaggio Midi 254 (Active Sensing) ed altri messaggi che a noi qui non interessano, li escludiamo:
   If dato < 240 Then Print "byte inviato dalla tastiera = "; dato
 Wend
 
End

Se non si vuole usare la variabile dichiarata come File, o nell'altro esempio la variabile dichiarata come Process, in alternativa è possibile usare #Last, il quale, come è noto, restituisce un riferimento all'ultimo oggetto chiamato da un evento:

Read #Last, dato




Ricezione mediante Object.Attach

Potremo anche usare il metodo Object.Attach per gestire l'evento della ricezione dei dati Midi, associando l'oggetto file alla definizione dell'evento.

Private fl As File


Public Sub Main()

 fl = Open "/dev/snd/midiC2D0" For Read Watch

 Object.Attach(varFile, FMain, "EventoMidi")

End


Public Sub EventoMidi_Read()
 
 Dim b As Byte
 
 Read #fl, b
' ...il solito filtro:
 If b < 240 Then Print b

End


Ricezione dei dati da più dispositivi Midi esterni

Con il metodo Object.Attach potremo agevolmente ricevere e gestire i dati Midi, provenienti da due o più tastiere esterne.

Private fl1 As File
Private fl2 As File


Public Sub Main()

 fl1 = Open "/dev/snd/midiC2D0" For Read Watch
' Viene associato l'oggetto File alla denominazione del proprio evento:
 Object.Attach(fl1, FMain, "EventoMidi1")

 fl2 = Open "/dev/snd/midiC3D0" For Read Watch
' Viene associato l'oggetto File alla denominazione del proprio evento:
 Object.Attach(fl2, FMain, "EventoMidi2")
 
End


' Vengono intercettati i dati provenienti dalla prima tastiera esterna: 
Public Sub eventoMidi1_Read()
 
 Dim b1 As Byte
 
 Read #fl1, b1
' ...il solito filtro:
 If b1 < 240 Then Print b1

End


' Vengono intercettati i dati provenienti dalla seconda tastiera esterna:
Public Sub EventoMidi2_Read()
 
Dim b2 As Byte
 
 Read #fl2, b2
' ...il solito filtro:
 If b2 < 240 Then Print b2

End




Ricezione mediante Observer

Potremo anche usare il metodo Observer per gestire l'evento della ricezione dei dati Midi.

Private varFile As File


Public Sub Main()

 Dim oss As Observer

 fl = Open "/dev/snd/midiC2D0" For Read Watch

 oss = New Observer(fl) As "EventoMidi"

End


Public Sub eventoMidi_Read()
 
 Dim b As Byte
 
 Read #fl, b
' ...il solito filtro:
 If b < 240 Print b

End


Ricezione dei dati da più dispositivi Midi esterni

Ugualmente, con Observer potremo ricevere e gestire i dati Midi, provenienti da due o più tastiere esterne.

Private fl1 As File
Private fl2 As FileForm_Open


Public Sub Form_Open()

 Dim obs1 As Observer
 Dim obs2 As Observer

 fl1 = Open "/dev/snd/midiC2D0" For Read Watch

' Viene "osservato" l'Oggetto File per l'evento:
 obs1 = New Observer(varFile1) As "EventoMidi1"
 
 fl2 = Open "/dev/snd/midiC3D0" For Read Watch

' Viene "osservato" l'oggetto File per l'evento:
 obs2 = New Observer(fl2) As "EventoMidi2"
 
End


' Vengono intercettati i dati provenienti dalla prima tastiera esterna: 
Public Sub EventoMidi1_Read()
 
 Dim b1 As Byte

 Read #fl1, b1
' ...il solito filtro:
 If b1 < 240 Print b1

End


' Vengono intercettati i dati provenienti dalla seconda tastiera esterna:
Public Sub EventoMidi2_Read()
 
Dim b2 As Byte
 
 Read #fl2, b2
' ...il solito filtro:
 If b2 < 240 Print b2

End




Ricostruzione ed invio ad ALSA di eventi ricevuti extra ALSA da una tastiera Midi esterna

Anche per questa particolare modalità di ricezione extra ALSA dei messaggi Midi da una tastiera esterna considereremo il procedimento necessario, una volta ottenuti i dati, per costruire l'evento Midi in modo adatto ad essere inviato ad ALSA.

Dovremo tenere conto che sarà ovviamente necessario integrare la parte extra ALSA (come l'abbiamo vista nei due precedenti paragrafi) di ricezione dei dati con la parte di utilizzo delle funzioni delle API di ALSA per inviargli i dati necessari dell'evento NoteON, così da ottenere il suono finale.

Per questo argomento rinviamo alla seguente pagina della Wiki: Ricevere dati Midi senza funzioni di ALSA e inviarli mediante le funzioni di Alsa