Differenze tra le versioni di "Alsa e Gambas: Creazione del Client e delle sue porte"

Da Gambas-it.org - Wikipedia.
Riga 1: Riga 1:
==Funzione per la connessione del programma Midi con ALSA==
+
#REDIRECT [[Subsistema_Seq:_Creazione_del_Client_e_delle_sue_porte]]
Per creare, ossia per rendere ''client'' il nostro programma, è necessario utilizzare la funzione all'uopo dedicata di ALSA:
 
''int snd_seq_open (snd_seq_t **seqp, const char *name, int streams, int mode)'' |[[#Note|1]]|
 
Questa, come abbiamo già detto, fornisce un ''handle'' (la variabile di tipo Puntatore di Puntatore ''seqp'') che consente al programma di relazionarsi con ALSA. Nello specifico ''apre'' il sequencer di ALSA, creando un nuovo ''handle''. Essa apre cioè una connessione all'interfaccia del kernel del sequencer ALSA. Crea dunque un ''client'', e - per quanto ci riguarda - possiamo dire che ''rende client'' il nostro progetto.
 
 
 
L'impostazione dei parametri di detta funzione è la seguente:
 
* '''seqp''' : un puntatore [ [[#Note|2]] ] al puntatore ''snd_seq_t'' (per questo ''**seqp'' è appunto un puntatore ad un puntatore e presenta due asterischi);
 
* '''name''' : il nome del nostro sequencer. E' un nome stringa che possiede uno speciale significato in ALSA, e solitamente è necessario usare il termine "''default''". Solo successivamente si potrà porre un nome stringa di nostra scelta.
 
* '''streams''' : la modalità in lettura e/o scrittura del nostro client. Questa modalità può essere:
 
<p>- SND_SEQ_OPEN_OUTPUT: apre il sequencer solo per l'output. Tale modalità è identificata con un valore costante di tipo integer = 1;</p>
 
<p>- SND_SEQ_OPEN_INPUT: apre il sequencer solo per l'input. Tale modalità è identificata con un valore costante di tipo integer = 2;</p>
 
<p>- SND_SEQ_OPEN_DUPLEX: apre il sequencer sia per l'output che per l'input. Tale modalità è identificata con un valore costante di tipo integer = 3.</p>
 
*'''mode''' : può assumere il valore di 0, oppure SND_SEQ_NONBLOCK. Esso rappresenta l'apertura del sequencer ALSA in modalità ''non-blocking'', cioè ''non-bloccante'': i dati non bloccheranno in entrata l'esecuzione del programma.
 
 
 
 
 
Nella scrittura del nostro codice dobbiamo, come abbiamo imparato, richiamare tale funzione con ''Extern'':
 
<Font Color= #B22222>''Private '''EXTERN''' snd_seq_open(seq AS Pointer, name as String, streams as Integer, mode as Integer) as Integer''</Font>
 
La chiamata ci restituirà un integer, poiché - come abbiamo già detto - <span style= "text-decoration:underline">ogni funzione esterna di ALSA ritorna un codice di errore</span> (anche quando non si deve necessariamente creare un oggetto/handle). Questa funzione sarà riportata in gambas così:
 
<Font Color= #B22222>''err = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_OUTPUT, 0)''</font color>
 
la porta (con riferimento al capitolo che stiamo trattando) ha caratteristica ''OUTPUT''.
 
 
 
In <span style= "text-decoration:underline">Gambas 3</span> utilizziamo ''VarPtr(Pointer as Pointer) AS Pointer'', il quale passa l'indirizzo del puntatore alla variabile "''handle''"; esso ritorna cioè un puntatore alla variabile "''handle''".
 
 
 
 
 
 
 
==Attribuire al "Client" un nome==
 
Potremo poi, volendo, attribuire al "Client" un nome da noi scelto (che avremo scelto già nella chiamata della funzione di apertura di ALSA effettuata nella classe principale). Per fare questo è necessario richiamare una funzione esterna di ALSA - che in C è:
 
''int snd_seq_set_client_name(snd_seq_t *seq, const char *name)''
 
con il solito Extern:
 
<Font Color= #B22222>''Private '''EXTERN''' snd_seq_client_name(seq AS Pointer, name AS String) AS Integer''</Font Color>
 
Questa funzione sarà espressa in Gambas come segue:
 
<Font Color= #B22222> ''err = snd_seq_client_name(handle, myname)</font color>
 
 
 
 
 
==Ottenere il numero identificativo del Client==
 
Possiamo inoltre ottenere il valore numerico identificativo del nostro Client. Questo identificativo numerico risulta indispensabile per conoscere od impostare le informazioni sul client. Solitamente ad un client-utente viene attribuito un identificativo tra 128 e 191. Richiamiamo ancora una funzione esterna di ALSA - che in C è:
 
''int snd_seq_client_id(snd_seq_t *seq)''
 
ovviamente dichiarata in Gambas così:
 
<Font Color= #B22222>''Private '''EXTERN''' snd_seq_client_id(seq As Pointer) As Integer''</Font Color>
 
Questa funzione sarà espressa in Gambas come segue:
 
<Font Color= #B22222> ''id = snd_seq_client_id(handle)</font color>
 
 
 
 
 
==Creazione della Porta e sua ''capacità''==
 
Dopo aver dato il nome al Client, creiamo effettivamente la sua porta conferendogli la capacità. Per creare una porta di normale uso si richiamerà la funzione esterna di ALSA - che in C è:
 
''int snd_seq_create_simple_port(snd_seq_t *seq, const char *name, unsigned int caps, unsigned int type)''
 
in Gambas tale dichiarazione sarà:
 
<Font Color= #B22222>''Private '''EXTERN'''  snd_seq_create_simple_port(seq As Pointer, name As String, caps As Integer, type As Integer) As Integer''</Font Color>.</p>
 
 
 
Questa funzione sarà espressa in Gambas come segue:
 
<Font Color= #B22222>'' err = snd_seq_create_simple_port(handle, "Seq-Out", SND_SEQ_PORT_CAP_READ, SND_SEQ_PORT_TYPE_MIDI_GENERIC + SND_SEQ_PORT_TYPE_APPLICATION)''</font color>
 
 
 
Creiamo la porta del Client con capacità generica di interpretare i dati Midi, ed in più la dichiariamo appartenente alla nostra applicazione, cioè il nostro sequencer-client.
 
 
 
Laddove le costanti:
 
* SND_SEQ_PORT_CAP_READ = 1;
 
* SND_SEQ_PORT_TYPE_MIDI_GENERIC = 2;
 
* SND_SEQ_PORT_TYPE_APPLICATION = 1048576  (1<<20).
 
 
 
 
 
 
 
==Sguardo alle Routine necessarie per la ''gestione dell'errore''==
 
Alle funzioni che ritornano un valore di errore, aggiungeremo la gestione dell'errore.
 
 
 
L'intera funzione per gestire gli errori potrà essere ad esempio così concepita:
 
 
 
1. Come sappiamo, Alsa restituisce codici di errore numerici;
 
 
 
2. Otteniamo quindi il codice di errore, e lo passiamo ad Alsa usando la funzione esterna:
 
const char * snd_strerror (int errnum)
 
che per essere utilizzata ovviamente sarà debitamente richiamata con il solito ''Extern'':
 
<Font Color= #B22222>''Private Extern snd_strerror(err As Integer) As Pointer''</font>
 
Si passerà alla funzione <Font Color= #B22222>''snd_strerror()''</font> un valore integer. La funzione in base al valore passato ritornerà un messaggio di spiegazione;
 
 
 
3. Alsa ritorna una stringa del "C", che Gambas non può utilizzare. Più precisamente ALSA passa un ''Pointer'' che deve essere dereferenziato. La funzione '''String@''' serve per dereferenziare quel ''Pointer'' passato da ALSA, consentendo così di leggere la stringa.
 
<BR>La sintassi della funzione String@ è:
 
String@(Pointer as Pointer) as String
 
 
 
La routine potrà essere come questa descritta di seguito:
 
<FONT Color=gray>' ''const char * snd_strerror (int errnum)''
 
' ''Returns the message for an error code.''</font>
 
Private Extern snd_strerror(err As Integer) As Pointer
 
 
 
'''Private''' Sub scriviErrore(operation As String, err As Integer)
 
 
  If err < 0 Then Print operation; ": err="; err; " ("; errmsg(err); ")"
 
 
 
'''End'''
 
 
 
'''Private''' Sub errmsg(err As Integer) As String
 
 
  Return String@(snd_strerror(err))
 
 
 
'''End'''
 
 
 
 
 
 
 
==Esempio pratico==
 
Mostriamo di seguito, a compendio di quanto sopra descritto, un codice di applicazione a ''riga di comando'', nel quale verrà inizializzata la libreria ALSA ed il suo subsistema ''Seq'' e soltanto creata la porta in lettura della nostra applicazione. Con il seguente codice dunque la nostra applicazione diventerà semplicemente così un ''Client'' di ALSA.
 
Library "libasound:2"
 
 
Private SND_SEQ_OPEN_OUTPUT As Integer = 1
 
Private SND_SEQ_PORT_CAP_READ As Integer = 1
 
Private SND_SEQ_PORT_TYPE_MIDI_GENERIC As Integer = 2
 
Private SND_SEQ_PORT_TYPE_APPLICATION As Integer = 1 * CInt(2 ^ 20)
 
 
<FONT Color=gray>' ''int snd_seq_open (snd_seq_t **handle, const char * name, int streams, int mode)''
 
' ''Open the ALSA sequencer.''</font>
 
Private Extern snd_seq_open(handle As Pointer, name As String, streams As Integer, mode As Integer) As Integer
 
 
<FONT Color=gray>' ''int snd_seq_set_client_name (snd_seq_t *seq, const char *name)''
 
' ''Set client name.''</font>
 
Private Extern snd_seq_set_client_name(seq As Pointer, name As String) As Integer
 
 
<FONT Color=gray>' ''int snd_seq_client_id (snd_seq_t *handle)''
 
' ''Get the client id.''</font>
 
Private Extern snd_seq_client_id(handle As Pointer) As Integer
 
 
<FONT Color=gray>' ''int snd_seq_create_simple_port (snd_seq_t *seq, const char *name, unsigned int caps, unsigned int type)''
 
' ''Create a port - simple version.''</font>
 
Private Extern snd_seq_create_simple_port(seq As Pointer, name As String, caps As Integer, type As Integer) As Integer
 
 
 
'''Public''' Sub Main()
 
 
  Dim sndseq As Pointer
 
  Dim rit, id, porta As Integer
 
 
 
  rit = snd_seq_open(VarPtr(sndseq), "default", SND_SEQ_OPEN_OUTPUT, 0)
 
  If rit < 0 Then Error.Raise("Impossibile aprire il subsistema 'seq' di ALSA !")
 
   
 
  snd_seq_set_client_name(sndseq, "nome del nostro Client")
 
   
 
  id = snd_seq_client_id(sndseq)
 
  Print "Identificativo del Client: "; id
 
   
 
  porta = snd_seq_create_simple_port(sndseq, "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 !")
 
  Print "Porta n. "; porta
 
   
 
  While True
 
    Wait 0.01
 
  Wend
 
   
 
'''End'''
 
Alcune informazioni sul nostro applicativo ''Client'' di Alsa sono visibili, fintanto che esso è in funzione, nel file "''/proc/asound/seq/clients''", ove sono elencati appunto i ''Client'' attivi di Alsa.
 
 
 
Va detto che, ovviamente, tutta la parte strettamente afferente alle funzioni esterne di ALSA potrebbe essere raccolta in un Modulo a se stante.
 
 
 
 
 
 
 
 
 
=Note=
 
[1] Vedi D. Blengino: [[Traduzione_della_comunità_di_Gambas-it#Iniziare_un_dialogo_con_ALSA|Iniziare un dialogo con ALSA]].
 
 
 
[2] I Puntatori (Pointer) sono variabili sulle quali si leggono e si scrivono indirizzi (i pointer contengono indirizzi). Ottenere un valore tramite un pointer si dice "''dereferenziare il pointer''", nel senso che il pointer viene interrogato, ed esso dice dove andare a cercare. Così viene dereferenziato (cioé, si scarta il pointer e si usa invece il suo risultato). Il pointer ad un determinato indirizzo si chiama, poniamo, ''var_point_p (nome a caso)'', e leggendolo dentro vi si trova scritto il valore. Solitamente ai pointer è possibile assegnare il valore NULL, che significa "''non esiste alcun indirizzo''". Se i pointer sono NULL, non devono essere dereferenziati, pena (di solito) un errore del sistema operativo.
 

Versione delle 04:57, 9 set 2020