Differenze tra le versioni di "Alsa e Gambas: Invio dei dati senza connessione della porta del programma con il Softsynth"

Da Gambas-it.org - Wikipedia.
Riga 427: Riga 427:
 
   
 
   
 
  Private Const SND_SEQ_OPEN_OUTPUT As Integer = 1
 
  Private Const SND_SEQ_OPEN_OUTPUT As Integer = 1
Private Const SND_SEQ_EVENT_NOTEON As Byte = 6
 
Private Const SND_SEQ_EVENT_NOTEOFF As Byte = 7
 
 
  Private Const SND_SEQ_EVENT_PGMCHANGE As Byte = 11
 
  Private Const SND_SEQ_EVENT_PGMCHANGE As Byte = 11
 
  Private Const SND_SEQ_QUEUE_DIRECT As Byte = 253
 
  Private Const SND_SEQ_QUEUE_DIRECT As Byte = 253
 +
Private Enum SND_SEQ_EVENT_NOTEON = 6, SND_SEQ_EVENT_NOTEOFF
 
   
 
   
 
  <FONT Color=gray>' ''int snd_seq_open (snd_seq_t **handle, const char * name, int streams, int mode)''
 
  <FONT Color=gray>' ''int snd_seq_open (snd_seq_t **handle, const char * name, int streams, int mode)''

Versione delle 04:13, 2 dic 2023

Per inviare utilmente dati Midi ad altro Client di ALSA, non è necessario connettere i due Client (l'applicativo Gambas ed il Softsynth) mediante la funzione esterna di ALSA "snd_seq_connect_to()", purché si presti attenzione ad alcuni elementi che integrano due casi.


1° caso

Nel primo caso il codice dovrà prevedere che al membro dest_client e a quello dest_port della Struttura relativa agli eventi Midi dovranno essere rispettivamente assegnati i valori dell'Id e della porta del Client (Softsynth), al quale l'applicativo Gambas dovrà inviare i dati Midi. Il dispositivo destinatario degli Eventi Midi ALSA sarà individuato, dunque, esclusivamente attraverso il suo numero identificativo e il numero della sua porta quale Client di ALSA.

Mostriamo un esempio astratto facendo uso di una Struttura dichiarata ad immagine della citata Struttura di ALSA snd_seq_event_t:

Private Const CLIENT_DESTINATARIO As Byte = 128   ' Solitamente l'identificativo è 128
Private Const PORTA_DESTINATARIO As Byte = 0


Library "libasound:2.0.0"

Public Struct snd_seq_event_t
  type As Byte
  flags As Byte
  tag As Byte
  queue As Byte
  tick_o_tv_sec As Integer
  tv_nsec As Integer
  source_client As Byte
  source_port As Byte
  dest_client As Byte
  dest_port As Byte
  channel As Byte
  note As Byte
  velocity As Byte
  off_velocity As Byte
  param As Integer
  value As Integer
End Struct

Private Const SND_SEQ_OPEN_OUTPUT As Integer = 1
Private Const SND_SEQ_PORT_CAP_READ As Integer = 1
Private Const SND_SEQ_PORT_TYPE_MIDI_GENERIC As Integer = 2
Private Const SND_SEQ_PORT_TYPE_APPLICATION As Integer = 1048576
Private Enum SND_SEQ_EVENT_NOTEON = 6, SND_SEQ_EVENT_NOTEOFF, SND_SEQ_EVENT_KEYPRESS, SND_SEQ_EVENT_CONTROLLER = 10, SND_SEQ_EVENT_PGMCHANGE
 
' int snd_seq_open (snd_seq_t **handle, const char * name, int streams, int mode)
' Open the ALSA sequencer.
Private Extern snd_seq_open(handle As Pointer, name As String, streams As Integer, mode As Integer) As Integer

' const char * snd_strerror (int errnum)
' Returns the message for an error code.
Private Extern snd_strerror(err As Integer) As Pointer

' int snd_seq_client_id (snd_seq_t * seq)
' Get the client id.
Private Extern snd_seq_client_id(seq As Pointer) As Integer

' int snd_seq_create_simple_port (snd_seq_t *seq, const char *name, unsigned int caps, unsigned int type)
' Create a port - simple version.
Private Extern snd_seq_create_simple_port(seq As Pointer, name As String, caps As Integer, type As Integer) As Integer
 
' int snd_seq_alloc_queue (snd_seq_t *handle)
' Allocate a queue.
Private Extern snd_seq_alloc_queue(handle As Pointer) As Integer


Public Sub Main()

 Dim handle As Pointer
 Dim rit As Integer
 Dim id, porta, que, b As Byte
 Dim ev As New Snd_seq_event_t

 rit = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_OUTPUT, 0)
 If rit < 0 Then Error.Raise("Impossibile aprire il subsistema 'seq' di ALSA: " & String@(snd_strerror(rit)))

 id = snd_seq_client_id(handle)
 If id < 0 Then Error.Raise("Errore: " & String@(snd_strerror(rit)))

 porta = snd_seq_create_simple_port(handle, "porta_", SND_SEQ_PORT_CAP_READ, SND_SEQ_PORT_TYPE_MIDI_GENERIC Or SND_SEQ_PORT_TYPE_APPLICATION)
 If porta < 0 Then Error.Raise("Impossibile creare la porta di uscita dati ALSA !")

 que = snd_seq_alloc_queue(handle)

 With ev
   .queue = que
   .source_client = id
   .source_port = porta
   .dest_client = CLIENT_DESTINATARIO
   .dest_port = PORTA_DESTINATARIO
   .channel = 0
 End With
     
 ...etc...
 
End


2° caso

Nel secondo caso si dovrà tenere conto di quanto segue:

  • non viene utilizzata la funzione esterna (usata invece nel caso precedente) specifica di ALSA per la creazione della porta del Client applicativo;
  • al membro queue della Struttura relativa agli eventi Midi dovrà essere assegnato il valore della Costante ALSA "SND_SEQ_QUEUE_DIRECT" (253);
  • al membro dest_id e a quello dest_port della Struttura relativa agli eventi Midi dovranno essere rispettivamente assegnati i valori dell'Id e della porta del Client (Softsynth), al quale l'applicativo Gambas dovrà inviare i dati Midi.

Mostriamo un esempio astratto:

Private Const CLIENT_DESTINATARIO As Byte = 128   ' Solitamente l'identificativo è 128
Private Const PORTA_DESTINATARIO As Byte = 0


Library "libasound:2.0.0"

Public Struct snd_seq_event_t
  type As Byte
  flags As Byte
  tag As Byte
  queue As Byte
  tick_o_tv_sec As Integer
  tv_nsec As Integer
  source_client As Byte
  source_port As Byte
  dest_client As Byte
  dest_port As Byte
  channel As Byte
  note As Byte
  velocity As Byte
  off_velocity As Byte
  param As Integer
  value As Integer
End Struct

Private Const SND_SEQ_OPEN_OUTPUT As Integer = 1
Private Const SND_SEQ_PORT_CAP_READ As Integer = 1
Private Const SND_SEQ_PORT_TYPE_MIDI_GENERIC As Integer = 2
Private Const SND_SEQ_QUEUE_DIRECT As Byte = 253
Private Const SND_SEQ_PORT_TYPE_APPLICATION As Integer = 1048576
Private Enum SND_SEQ_EVENT_NOTEON = 6, SND_SEQ_EVENT_NOTEOFF, SND_SEQ_EVENT_KEYPRESS, SND_SEQ_EVENT_CONTROLLER = 10, SND_SEQ_EVENT_PGMCHANGE
 
' int snd_seq_open (snd_seq_t **handle, const char * name, int streams, int mode)
' Open the ALSA sequencer.
Private Extern snd_seq_open(handle As Pointer, name As String, streams As Integer, mode As Integer) As Integer

' const char * snd_strerror (int errnum)
' Returns the message for an error code.
Private Extern snd_strerror(err As Integer) As Pointer

' int snd_seq_client_id (snd_seq_t * seq)
' Get the client id.
Private Extern snd_seq_client_id(seq As Pointer) As Integer

' int snd_seq_create_simple_port (snd_seq_t *seq, const char *name, unsigned int caps, unsigned int type)
' Create a port - simple version.
Private Extern snd_seq_create_simple_port(seq As Pointer, name As String, caps As Integer, type As Integer) As Integer


Public Sub Main()

 Dim handle As Pointer
 Dim rit As Integer
 Dim id, porta As Byte
 Dim ev As New Snd_seq_event_t

 rit = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_OUTPUT, 0)
 If rit < 0 Then Error.Raise("Impossibile aprire il subsistema 'seq' di ALSA: " & String@(snd_strerror(rit)))

 id = snd_seq_client_id(handle)
 If id < 0 Then Error.Raise("Errore: " & String@(snd_strerror(rit)))

 porta = snd_seq_create_simple_port(handle, "porta_", SND_SEQ_PORT_CAP_READ, SND_SEQ_PORT_TYPE_MIDI_GENERIC Or SND_SEQ_PORT_TYPE_APPLICATION)
 If porta < 0 Then Error.Raise("Impossibile creare la porta di uscita dati ALSA !")

 With ev
   .queue = SND_SEQ_QUEUE_DIRECT
   .source_client = id
   .source_port = porta
   .dest_client = CLIENT_DESTINATARIO
   .dest_port = PORTA_DESTINATARIO
   .channel = 0
 End With
     
 ...etc...
 
End


Esempio più essenziale per invio diretto degli Eventi Midi ALSA

Mostriamo ora la modalità più breve ed essenziale per inviare ad altro Client di Alsa alcuni eventi Midi, e nella quale non è prevista la creazione di porte del nostro Client applicativo.
Si utilizzerà in questo caso la funzione esterna di ALSA: "snd_seq_event_output_direct()" per invio diretto di ciaascun Evento Midi ALSA.

Private Const CLIENT_DESTINATARIO As Byte = 128   ' Solitamente l'identificativo è 128
Private Const PORTA_DESTINATARIO As Byte = 0

Library "libasound:2.0.0"

Public Struct snd_seq_event_t
  type As Byte
  flags As Byte
  tag As Byte
  queue As Byte
  tick_o_tv_sec As Integer
  tv_nsec As Integer
  source_client As Byte
  source_port As Byte
  dest_client As Byte
  dest_port As Byte
  channel As Byte
  note As Byte
  velocity As Byte
  off_velocity As Byte
  param As Integer
  value As Integer
End Struct

Private Const SND_SEQ_OPEN_OUTPUT As Integer = 1
Private Const SND_SEQ_QUEUE_DIRECT As Byte = 253
Private Enum SND_SEQ_EVENT_NOTEON = 6, SND_SEQ_EVENT_NOTEOFF

' int snd_seq_open (snd_seq_t **handle, const char * name, int streams, int mode)
' Open the ALSA sequencer.
Private Extern snd_seq_open(handle As Pointer, name As String, streams As Integer, mode As Integer) As Integer

' const char * snd_strerror (int errnum)
' Returns the message for an error code.
Private Extern snd_strerror(err As Integer) As Pointer

' int snd_seq_event_output_direct (snd_seq_t *handle, snd_seq_event_t *ev)
' Output an event directly to the sequencer NOT through output buffer.
Private Extern snd_seq_event_output_direct(handle As Pointer, ev As Snd_seq_event_t) As Integer

' int snd_seq_close (snd_seq_t *handle)
' Close the sequencer.
Private Extern snd_seq_close(handle As Pointer) As Integer


Public Sub Main()

 Dim seq As Pointer
 Dim evento As New Snd_seq_event_t
 Dim rit As Integer

 snd_seq_open(VarPtr(seq), "default", SND_SEQ_OPEN_OUTPUT, 0)
 If rit < 0 Then Error.Raise("Impossibile aprire il subsistema 'seq' di ALSA: " & String@(snd_strerror(rit)))

 With evento
   .type = SND_SEQ_EVENT_NOTEON
   .queue = SND_SEQ_QUEUE_DIRECT
   .dest_client = CLIENT_DESTINATARIO
   .dest_port = PORTA_DESTINATARIO
   .channel = 0
   .note = 64
   .velocity = 100
 End With

 snd_seq_event_output_direct(seq, evento)

' Imposta la durata dell'esecuzione della nota.
' Va precisato che l'uso della funzione "Wait" NON è un modo coerente con il protocollo di ALSA per la temporizzazione degli Eventi Midi: dato che ALSA usa a tale fine la cosiddetta "marcatura degli Eventi Midi" assegnando degli opportuni valori nel membro "flags" e in quelli del "Timestamp" della Struttura "snd_seq_event_t".
' Qui si fa uso della funzione "Wait" soltanto per meri motivi didattici ed esemplificativi del funzionamento degli altri aspetti trattati in questa pagina.
 Wait 1

 evento.type = SND_SEQ_EVENT_NOTEOFF
 evento.velocity = 0
 snd_seq_event_output_direct(seq, evento)

 snd_seq_close(seq)

End

In quest'altro esempio pratico viene rappresentata sul Form una tastierina Midi che può inviare dati Midi grezzi 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 Const ALTEZZA_TASTI_BIANCHI As Single = 0.35
Private bb As New Byte[3]
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.5
   .H = Screen.AvailableHeight * 0.2
   .Center
 End With
 CreaClient()
 With cb = New ComboBox(Me) As "Combo"
   .W = 160
   .H = 25
   .X = (Me.W * 0.94) - .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.88
   .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.013
         .H = ((Me.H * ALTEZZA_TASTI_BIANCHI) * 66.66) / 100
         Select Case t 
           Case 25 + (12 * n)
             .X = tasti[t - 1].X + (((Me.W * 0.025) / 2))
           Case 27 + (12 * n)
             .X = tasti[t - 1].X + (((Me.W * 0.025) / 2))
           Case 30 + (12 * n)
             .X = tasti[t - 1].X + (((Me.W * 0.025) / 2))
           Case 32 + (12 * n)
             .X = tasti[t - 1].X + (((Me.W * 0.025) / 2))
           Case 34 + (12 * n)
             .X = tasti[t - 1].X + (((Me.W * 0.025) / 2))
             Inc n
         End Select
         .Y = Me.H * 0.375
         .Background = Color.Black
         .Tag = t
       Else
' Imposta i tasti bianchi:
         .W = Me.W * 0.018
         .H = Me.H * ALTEZZA_TASTI_BIANCHI
         .X = (.W * c) + (Me.W / 16)
         .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(SND_SEQ_EVENT_NOTEON, 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(SND_SEQ_EVENT_NOTEOFF, bb)

 If Last.Background = Color.DarkGray Then Last.Background = Color.Black
 Me.Title = Null

End

'''''''''''''''''''''''''''''''''''''''''''''''

Private seq As Pointer


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_EVENT_PGMCHANGE As Byte = 11
Private Const SND_SEQ_QUEUE_DIRECT As Byte = 253
Private Enum SND_SEQ_EVENT_NOTEON = 6, SND_SEQ_EVENT_NOTEOFF

' int snd_seq_open (snd_seq_t **handle, const char * name, int streams, int mode)
' Open the ALSA sequencer.
Private Extern snd_seq_open(handle As Pointer, name As String, streams As Integer, mode As Integer) As Integer

 ' const char * snd_strerror (int errnum)
' Returns the message for an error code.
Private Extern snd_strerror(err As Integer) As 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(tipo A Byte, 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:
 Messaggio(evento, tipo, 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

Se nel proprio sistema è stato installato il softsynth Fluidsynth, allora il programma, qui sopra descritto, dovrebbe connettersi automaticamente a quel softsynth, consentendo così la riproduzione dei suoni. Qualora non avvenga, procedere come segue:
1) da Terminale lanciare questa riga: ":~$ fluidsynth reload 0";
2) senza chiudere il Terminale, verificare nell'utilità "Monitor di sistema" che Fluidsynth sia presente fra i processi attivi;
3) se Fluidsynth è presente fra i processi, verificare anche che esso sia presente al num. 128 nel file /proc/asound/seq/clients;
4) in caso affermativo - senza chiudere il Terminale - lanciare il codice sopra esposto.