Differenze tra le versioni di "Alsa e Gambas: Ricezione con un programma esterno di supporto"

Da Gambas-it.org - Wikipedia.
 
Riga 1: Riga 1:
Procederemo, ora, a mostrare un secondo metodo, che fa uso di una ''pipe'' da un programma esterno, scritto in linguaggio C, con il quale evitare ogni possibile evidente latenza. Con ALSA i programmi in C usano solitamente il meccanismo di controllare gli eventi provenienti dal ''device'' Midi nel ''Main Message Loop'', basato sulle funzioni ''poll()'' e ''select()''. La funzione ''poll()'', in breve, ci dice se c'è qualche dato da leggere. La procedura, dunque, prevede che dapprima vengono ottenuti da ALSA  i descrittori dei file; successivamente viene utilizzata la chiamata alla funzione ''poll()'' per vedere ''se'' c'è quealcosa da leggere. Più precisamente si chiede al sistema di controllare tutti i ''file descriptors'', e poi si va a leggere con la funzione di ALSA: ''snd_seq_event_input(snd_seqt * seq, snd_seq_event_t ** ev)''.
+
#REDIRECT [[Alsa_e_Gambas:_Ricezione_dei_dati_Midi_con_un_programma_esterno_di_supporto]]
<BR>I semplici programmi in C, come arecordmidi e aseqdump, per leggere gli eventi dalla predetta funzione di ALSA, eseguono un ciclo. Ciò ha un senso, poiché tali semplici programmi di ricezione svolgono soltanto una applicazione: quella di leggere dati. Ma se un programma GUI deve anche gestire la grafica, il ciclo non va bene. Noi, allora, abbiamo insomma bisogno di un modo efficace per essere avvertiti che c'è qualche dato da leggere. Cosa, questa, che potrebbe essere fatta anche con un ciclo, se in modo sicuro vi fossero dati da leggere. Se, però non c'è niente da eggere, non può essere usato un ciclo, poiché sarebbe una perdita di tempo, nonché un inutile uso della CPU e delle altre risorse di sistema.
 
<P>E', quindi, necessario un evento di Gambas autonomo e distinto dalla fase di ''ricezione'' dei dati. E' anzi necessario che Gambas ''non si occupi'' della procedura di ''ricezione'' dei dati.</p>
 
 
 
 
 
===La soluzione di un programma esterno di supporto===
 
 
 
Per risolvere il problema relativo all'input degli eventi da ALSA, è possibile pensare ad una soluzione esterna combinata: si tratterebbe di scrivere un breve programma C, cosa che quindi permetterebbe di avere accesso alle piene funzionalità del sistema. Poi da Gambas si farebbe un Exec a quel programma, per poterne leggere lo standard output con un ''watch''. Il programma di supporto in C normalmente dormirebbe; qualora, però, dovesse trovare eventi da ALSA, li manderebbe fuori. Il programma principale di Gambas potrebbe intercettare e leggere tali dati in output dal programma C con un evento del tipo process_Read.
 
 
 
<BR>Presentiamo, allora, di seguito soltanto le parti essenziali e ''fondamentali'' di codice sia del programma C di supporto che del programma in Gambas.
 
 
 
<BR>'''Programma in C di supporto''':
 
 
 
Come ''base'' abbiamo preso il codice del programma "''aseqdump''" e lo abbiamo opportunamente modificato per ottenere l'invio <span style= "text-decoration:underline">non ''verboso''</span> di singoli messaggi Midi all'output. Questo programma dovrà essere preventivamente caricato nella cartella "Dati" del programma di Gambas.
 
 
 
#include <stdio.h>
 
#include <stdlib.h>
 
#include <stdarg.h>
 
#include <string.h>
 
#include <signal.h>
 
#include <getopt.h>
 
#include <sys/poll.h>
 
#include <alsa/asoundlib.h>
 
 
<Font Color= #006400>/* Qui le varie funzioni per la Gestione degli Errori */</font>
 
......
 
 
<Font Color= #006400>/* Qui le varie funzioni per l'apertura di ALSA e per la creazione del Client e delle sue porte */</font>
 
......
 
 
<Font Color= #006400>/* Ora le funzioni per la distinzione dei vari possibili eventi Midi ricevuti, */</font>
 
<Font Color= #006400>/* e per l'invio dei singoli Messaggi Midi all'output. */</font>
 
<Font Color= #006400>/* Ora per fini didattici considereremo soltanto l'evento Note ON. */</font>
 
<Font Color= #006400>/* Useremo '''''write''''', anziché ''fprint'', per inviare i singoli messaggi Midi in formato ''binario''. */</font>
 
<Font Color= #006400>/* Useremo quindi un ''fflush'' per inviare un singolo Messaggio alla volta, per consentire a Gambas di leggere un solo dato alla volta */</font>
 
static void dump_event(const snd_seq_event_t *ev)
 
{
 
      switch (ev->type) {
 
      case SND_SEQ_EVENT_NOTEON:
 
                write(1, &ev->data.note.channel, sizeof(char));<Font Color= #006400> {[[#Note|1]]}</font>
 
                  fflush(stdout);
 
                write(1, &ev->data.note.note, sizeof(char));
 
                  fflush(stdout);
 
                write(1, &ev->data.note.velocity, sizeof(char));
 
                  fflush(stdout);
 
            break;
 
      }
 
}
 
 
<Font Color= #006400>/* Quindi la funzione principale e finale di ricezione dei dati da uno strumento Midi esterno */</font>
 
<Font Color= #006400>/* da parte del programma in C di supporto */</font>
 
<Font Color= #006400>/* (Ne riportiamo soltanto le funzioni essenziali per la ricezione dei dati dau dispositivo Midi esterno) */</font>
 
int main(int argc, char *argv[])
 
{
 
      struct pollfd *pfds;
 
      int npfds;
 
      int err;
 
 
      ......
 
      .........
 
 
<Font Color= #006400>/* Viene chiesto ad ALSA quanti sono i file descrittori per la lettura */</font>
 
      npfds = snd_seq_poll_descriptors_count(seq, POLLIN);
 
 
<Font Color= #006400>/* Viene riservata un'area di memoria per contenere quei descrittori. */</font>
 
      pfds = alloca(sizeof(*pfds) * npfds);
 
      for (;;) {
 
 
<Font Color= #006400>/* Si chiede ad ALSA di riempire la memoria puntata dal puntatore ''pfds'' con i descrittori rilevanti. */</font>
 
<Font Color= #006400>/* Si passa il puntatore all'area di memoria ed il numero di descrittori che possono essere scritti in quell'area riservata. */</font>
 
            snd_seq_poll_descriptors(seq, pfds, npfds, POLLIN);
 
 
<Font Color= #006400>/* Si chiede quindi al sistema di controllare tutti i descrittori: */</font>
 
<Font Color= #006400>/* la funzione poll() dirà, insomma, se ci sono eventi da leggere */</font>
 
            if (poll(pfds, npfds, -1) < 0)
 
                  break;
 
            do {
 
 
<Font Color= #006400>/* Questo ciclo legge tutti gli eventi pendenti. */</font>
 
                  snd_seq_event_t *event;
 
                  err = snd_seq_event_input(seq, &event);
 
                  if (err < 0)
 
                        break;
 
 
<Font Color= #006400>/* I dati ricevuti e ralativi agli eventi Midi vengono passati alla sub-routine ''dump_event(event)'' precedentemente descritta.  */</font>
 
                  if (event)
 
                        dump_event(event);
 
            } while (err > 0);
 
      ......
 
      .........
 
}
 
 
 
 
 
<BR>'''Programma principale in Gambas''':
 
 
 
<Font Color= #006400>'' ' Gambas class file</font>
 
 
 
'''Private''' d As Process
 
 
'''Public''' Sub Form_Open()
 
 
Dim rob As String
 
Dim robTre as Integer
 
Dim client_id As Integer
 
 
<Font Color= #006400>' ''Assicura comunque la chiusura del programma in C di supporto''</font>
 
  Shell "killall supporto" Wait
 
 
<Font Color= #006400>' ''Se non esiste già il file del programma di supporto in: /tmp'',</font>
 
<Font Color= #006400>' ''lo copia in quella directory.''</font>
 
  If Not Exist ("/tmp/supporto") Then
 
    Copy "supporto" To "/tmp/supporto"
 
  Endif
 
 
<Font Color= #006400>' ''A questo punto bisognerà rendere eseguibile il programma,''</font>
 
<Font Color= #006400>' ''effettuando manualmente le necessarie modifiche nelle proprietà del suo file.''</font>
 
 
<Font Color= #006400>' ''Poi, viene avviato il programma in C di supporto.''</font>
 
  d = Exec ["/tmp/supporto"] For Read As "prova"
 
 
<Font Color= #006400>' ''Attende che si sia certamente avviato il programma di supporto.''</font>
 
  Wait 0.5
 
 
<Font Color= #006400>' ''Verifica che il programma di supporto sia stato avviato e sia presente fra i client.''</font>
 
<Font Color= #006400>' ''Poi carica l'output nella variabile "rob".''</font>
 
  Shell "cat /proc/asound/seq/clients | grep supporto" To rob
 
 
<Font Color= #006400>' ''Estrapola l'ottavo, il nono ed il decimo carattere dalla stringa memorizzata nella variabile "rob".</font>
 
<Font Color= #006400>' ''Si tratta del numero identificativo del Client/programma di supporto.''</font>
 
  robTre = Mid$(rob, 8, 3)
 
 
<Font Color= #006400>' ''Converte la variabile "rob" in un numero intero.''</font>
 
  client_id = Val(robTre)
 
 
<Font Color= #006400>' ''Connette lo strumento Midi esterno al programma in C di supporto,''</font>
 
<Font Color= #006400>' ''individuandolo con il suo Id come client.''</font>
 
  Shell "aconnect 24:0 " & client_id & ":0"
 
 
'''End'''
 
 
 
<Font Color= #006400>' ''Se viene inviato dal programma di supporto almeno un dato al suo output,''</font>
 
<Font Color= #006400>' ''e quindi se c'è qualcosa da leggere, viene scatenata questa sub-routine.''</font>
 
'''Public''' Sub prova_Read()
 
 
  Dim vRacc As Byte
 
 
<Font Color= #006400>' ''Vengono letti i dati provenienti dallo standard output del programma di supporto.''</font>
 
  Read #Last, vRacc
 
 
<Font Color= #006400>' ''Per fini didattici qui viene soltanto stampato in console in contenuto della variabile vRacc.''</font>
 
  Print vRacc
 
   
 
'''End'''
 
 
 
'''Public''' Sub Button1_Click()
 
 
<Font Color= #006400>' ''Viene disconnesso il client di supporto dal dispositivo Midi esterno,''</font>
 
  Shell "aconnect -d 24:0 128:0" Wait
 
 
<Font Color= #006400>' ''Viene quindi chiuso il processo del client di supporto; ed in fine il programma stesso di Gambas.''</font>
 
  d.kill
 
 
  Me.Close()
 
 
'''End'''
 
 
 
=Note=
 
[1] '''write()''' vuole un file descriptor al quale mandare i dati. Lo standard output ha come file descriptor sempre 1. Poi vuole un indirizzo di memoria (un ''pointer'') dal quale prelevare i dati da inviare al file. Il carattere "&" in C fa quello che ''varptr()'' fa in Gambas, cioé ritorna un pointer al dato indicato. L'ultimo parametro indica la lunghezza dei dati: su un sistema a 32 bit i valori sarebbero 1 per il byte e 4 per l'integer; ma su un sistema a 64 bit i valori sarebbero 1 e 8. Infatti la lunghezza del dato da scrivere (per un int) non è 4 o 8, ma è "la lunghezza di un int". La funzione (che non è una vera funzione) ''sizeof()'' ritorna la lunghezza in byte di quanto specificato.
 
<P>La sintassi è per i dati con valore byte e quelli con valore integer:</p>
 
* ''write(1, &ev->note.xxx, sizeof(char));'' ------> nel caso di un byte
 
* ''write(1, &ev->note.xxx, sizeof(int));'' ------> nel caso di un integer
 

Versione attuale delle 17:45, 11 gen 2022