Alsa e Gambas: Creazione del Client e delle sue porte

Da Gambas-it.org - Wikipedia.
Versione del 23 ago 2011 alle 12:11 di Vuott (Discussione | contributi) (Nuova pagina: ====Preambolo==== Nella sezione precedente abbiamo detto che il dispositivo ALSA svolge sostanzialmente due compiti: * ''scheduling'', pianificare l'esecuzione di processi, ossia pian...)

(diff) ← Versione meno recente | Versione attuale (diff) | Versione più recente → (diff)

Preambolo

Nella sezione precedente abbiamo detto che il dispositivo ALSA svolge sostanzialmente due compiti:

  • scheduling, pianificare l'esecuzione di processi, ossia pianificare secondo una precisa sequenza, come in un elenco, l'invio degli eventi;
  • dispatching, ossia di indirizzarli all'esatta destinazione nel momento giusto. La fase della gestione reale degli eventi è lasciata ai programmi applicativi-utente (come i sequencer), i quali, in qualità di Client, comunicano tali dati con il dispositivo ALSA (più precisamente con uno dei suoi sub-sistemi; per il Midi: seq). Abbiamo anche detto che, pertanto, il nostro applicativo deve possedere le funzioni di un Client. Una parte di esso svolgerà le funzioni di Client. Per ottenere ciò, è necessario creare appunto un Client, capace di comunicare con gli altri dispositivi esterni, e certamente con il dispositivo ALSA. Per creare il nostro Client dobbiamo utilizzare particolari funzioni di ALSA, che dovranno essere preventivamente "richiamate" attraverso, come abbiamo visto, con una particolare dichiarazione contenente il termine "Extern".

Il nostro progetto seguirà più o meno la struttura base di ogni applicazione in ALSA:

1) aprire il dispositivo ALSA mediante specifica funzione;

2) impostare i parametri di detta funzione;

3) ricevere e/o inviare dati Midi al dispositivo;

4) chiudere il dispostivo.

Richiamo ed utilizzo delle funzioni di ALSA per creare il Client e le sue porte

Per creare, ossia per rendere client il nostro programma sperimentale, è necessario utilizzare la funzione all'uopo dedicata di ALSA: snd_seq_open. Questa, come abbiamo già detto, è un handle 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.

La documentazione ufficiale di ALSA ci dice che la dichiarazione di questa funzione in C è: int snd_seq_open (snd_seq_t **seqp, const char *name, int streams, int mode) [ 7 ].

L'impostazione dei parametri di detta funzione è la seguente:

  • seqp : un puntatore [ 8 ] 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:

- SND_SEQ_OPEN_OUTPUT: apre il sequencer solo per l'output. Tale modalità è identificata con un valore costante di tipo integer = 1;

- SND_SEQ_OPEN_INPUT: apre il sequencer solo per l'input. Tale modalità è identificata con un valore costante di tipo integer = 2;

- 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.

  • 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:

PRIVATE EXTERN snd_seq_open(seq AS Pointer, name as String, streams as Integer, mode as Integer) as Integer

La chiamata ci restituirà un integer, poiché - come abbiamo già detto - ogni funzione esterna di ALSA ritorna un codice di errore (anche quando non si deve necessariamente creare un oggetto/handle). Questa funzione sarà riportata in gambas così:

err = snd_seq_open(VarPtr(handle), "default", SND_SEQ_OPEN_DUPLEX, 0); la porta ha carateristica DUPLEX. In Gambas 3 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".


Quindi aggiungeremo la gestione dell'errore.</p>

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:

PRIVATE EXTERN snd_seq_client_name(seq AS Pointer, name AS String) AS Integer. Questa funzione sarà espressa in Gambas come segue: snd_seq_client_name(handle, myname)

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) - :

PRIVATE EXTERN snd_seq_client_id(seq As Pointer) As Integer. Questa funzione sarà espressa in Gambas come segue: id = snd_seq_client_id(handle).

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) - :

PRIVATE EXTERN snd_seq_create_simple_port(seq As Pointer, name As String, caps As Integer, type As Integer) As Integer.

Questa funzione sarà espressa in Gambas come segue: err = snd_seq_create_simple_port(handle, "Seq-Out", 0, SND_SEQ_PORT_TYPE_MIDI_GENERIC + SND_SEQ_PORT_TYPE_APPLICATION). 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.
Quindi aggiungeremo la consueta gestione dell'errore.


Procediamo in conclusione alla connessione delle porte del client con quella del subsistema seq di ALSA, ponendo nella classe principale la chiamata alla funzione di ALSA, trasferendole i valori dell'identificativo del dispositivo-client e della sua porta, la quale in C é: int snd_seq_connect_to(snd_seq_t *seq, int myport, int src_client, int src_port).

Nel nostro codice la richiameremo con la consueta funzione Extern: PRIVATE EXTERN snd_seq_connect_to(seq As Pointer, myport As Integer, src_client As Integer, src_port As Integer) As Integer.

Questa funzione sarà espressa in Gambas come segue: err = snd_seq_connect_to(handle, outport, dclient, dport)


A questo punto, lanciando da terminale il comando: cat /proc/asound/seq/clients, possiamo verificare:

  • il numero identificativo e il nome del nostro Client;
  • il numero ed il nome della porta del nostro client;
  • il numero identificativo del device (come QSynth) e della sua porta (con relativa indicazione delle capacità), al quale il client si è connesso.

Cercando il nostro client avremo infatti informazioni simili alle seguenti:

Client 130 : "Progetto sequencer in Gambas-3" [User]

Port 0 : "Seq-Out" (--e-)

Connecting To: 128:0


Sguardo alle Routine necessarie per la gestione dell'errore.

L'intera funzione per gestire gli errori sarà così concepita:

1. Come sappiamo, Alsa restituisce codici di errore numerici;

2. Otteniamo quindi il codice di errore, e lo passiamo ad Alsa usando snd_strerror(), che per essere utilizzata ovviamente sarà debitamente richiamata con il solito Extern. La funzione snd_strerror(), quindi, ritorna un messaggio di spiegazione.

3. Alsa ritorna una stringa del "C", che Gambas non può utilizzare; lo String@ serve a convertire tale stringa. La sintassi della funzione String@ è: String@(Pointer as Pointer) as String

Vedere il codice

Per vedere in ordine il nostro codice sin qui descritto, cliccare sul collegamento alla sua pagina: 1° CODICE.