Differenze tra le versioni di "Eseguire un file audio WAV con le sole funzioni esterne di Alsa"

Da Gambas-it.org - Wikipedia.
 
(45 versioni intermedie di uno stesso utente non sono mostrate)
Riga 2: Riga 2:
  
 
Si dovrà innanzitutto dichiarare la libreria di Alsa contenente le funzioni esterne che saranno utilizzate. Tale libreria è attualmente ''libasound.so.2.0.0'', e che sarà in Gambas così dichiarata:
 
Si dovrà innanzitutto dichiarare la libreria di Alsa contenente le funzioni esterne che saranno utilizzate. Tale libreria è attualmente ''libasound.so.2.0.0'', e che sarà in Gambas così dichiarata:
  Library "libasound:2"
+
  Library "libasound:2.0.0"
 
 
 
 
 
Si dovrà quindi aprire il subsistema ''PCM'' di Alsa e creare un ''handle'' per la sua gestione successiva. Questa operazione iniziale verrà compiuta con la funzione:
 
Si dovrà quindi aprire il subsistema ''PCM'' di Alsa e creare un ''handle'' per la sua gestione successiva. Questa operazione iniziale verrà compiuta con la funzione:
 
  ''int  snd_pcm_open(snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode)''
 
  ''int  snd_pcm_open(snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode)''
Riga 33: Riga 31:
 
  ......
 
  ......
 
  ......
 
  ......
  err = snd_pcm_set_params(handle, risolBit, SND_PCM_ACCESS_RW_INTERLEAVED, canali, frequenza, 1, 500000)
+
  err = snd_pcm_set_params(handle, rBit, SND_PCM_ACCESS_RW_INTERLEAVED, canali, frequenza, 1, 500000)
 
Il numero di canali e la frequenza del flusso dei dati audio verranno estrapolati dai dati stessi del file audio caricato. Nel caso di un file wav, ad esempio, tali dati sono contenuti nel primo blocco del file, ossia all'interno dei primi 44/46 dati.
 
Il numero di canali e la frequenza del flusso dei dati audio verranno estrapolati dai dati stessi del file audio caricato. Nel caso di un file wav, ad esempio, tali dati sono contenuti nel primo blocco del file, ossia all'interno dei primi 44/46 dati.
  
Riga 47: Riga 45:
 
* uframes: è un valore che rappresenta la quantità di dati, presenti nel buffer prima descritto, che devono essere inviati ad Alsa. Questo valore deve essere pari alla dimensione del buffer diviso la seguente operazione: ''risoluzione-bit / (8 / numero-canali)''.
 
* uframes: è un valore che rappresenta la quantità di dati, presenti nel buffer prima descritto, che devono essere inviati ad Alsa. Questo valore deve essere pari alla dimensione del buffer diviso la seguente operazione: ''risoluzione-bit / (8 / numero-canali)''.
 
La funzione sarà quindi richiamata in codice come segue:
 
La funzione sarà quindi richiamata in codice come segue:
  frames = snd_pcm_writei(handle, buffP, BUFFER / (bits / (8 / canali)))
+
  frames = snd_pcm_writei(handle, buffP, quant / (bits / (8 / canali)))
<BR>La variabile di tipo ''Intero'' "quant", che rappresenta la quantità massima di dati per ciascun ciclo da da inviare al sub-sistema PCM, verrà istanziata con un valore molto basso, se si intende all'interno del ciclo medesimo mostrare il tempo trascorso dall'inizio dell'esecuzione del file audio wav. Nel caso in cui non si intende mostrare il tempo trascorso, il valore della variabile "quant" può ovviamente assumere valori superiori.
+
La variabile di tipo ''Intero'' "quant" rappresenta la quantità massima di dati per ciascun ciclo da da inviare al sub-sistema PCM.
 +
 
 +
Prima della chiusura del codice viene posta la funzione ''snd_pcm_drain()'', la quale impedisce che al termine l'esecuzione venga un po' troncata inaspettatamente. Tale funzione, dunque, consente di eseguire la quantità totale dei dati audio grezzi.
 +
<BR>Questa funzione, volendo può essere sostituita dal seguente ciclo:
 +
<FONT color=gray>' ''Esegue il ciclo finché lo status del PCM non è nella condizione "Fermato":''</font>
 +
  Repeat
 +
<FONT color=gray>' ''Rileva lo status dell'interfaccia PCM:''</font>
 +
    snd_pcm_status(handle, VarPtr(stato))
 +
  Until stato = SND_PCM_STATE_XRUN
 +
In tal caso si dovrà avere ura di dichiarare opportunamente la funzione ''snd_pcm_status()'' e la costante ''SND_PCM_STATE_XRUN''.
 +
 
 +
Infine viene posta la funzione ''snd_pcm_close()'' che chiude il sub-sistema PCM.
  
  
====Esempio pratico====
+
===Fermare l'esecuzione dei dati audio===
Mostriamo di seguito un esempio di un semplice codice per eseguire un file WAV con un applicazione ''a riga di comando'':
+
Per arrestare l'esecuzione dei dati audio, si potrà utilizzare la seguente funzione di Alsa:
 +
''int snd_pcm_drop (snd_pcm_t * pcm)''
 +
<BR>In Gambas sarà così dichiarata:
 +
Private Extern snd_pcm_drop(pcmP As Pointer) As Integer
 +
ed utilizzata nella seguente maniera:
 +
snd_pcm_drop(handle)
 +
Se si chiama questa funzione ''snd_pcm_drop()'', da un lato l'altra funzione ''snd_pcm_writei()'' restituisce un valore d'errore uguale a -77 <SUP>&#091;[[#Note|Nota 1]]&#093;</sup>, dall'altro lo ''status'' dell'interfaccia PCM assume il valore 1 (identificatore: ''SND_PCM_STATE_SETUP'').
 +
<BR>Pertanto se si vorrà ad esempio utilizzare un ciclo - come si è visto nel paragrafo precedente - per impedire che l'esecuzione sia troncata alla fine, si potrà dichiarare il nuovo valore dello ''status'' come una costante:
 +
Private Const SND_PCM_STATE_SETUP As Byte = 1
 +
Di tali valori dovremo, dunque, tenere conto nel codice.
 +
 
 +
Se non si farà uso del ciclo finale, si potrà agevolmente utilizzare la funzione ''snd_pcm_drain()'' già vista in precedenza.
 +
 
 +
Se per arrestare l'esecuzione si utilizza in ambiente grafico, come prevedibile, un ''Button'', poiché l'esecuzione provoca l'impossibilità di agire su eventuali oggetti posti sul ''Form'', si dovrà impostare prima o dopo la funzione "''snd_pcm_writei()''" un ''Wait'' posto al valore:
 +
Wait 0.001
 +
 
 +
 
 +
===Esempio pratico con applicazione in ambiente grafico===
 +
Mostriamo, di seguito, un codice per l'esecuzione di un file WAV in ambiente grafico. Sono previsti sul ''Form'' dell'applicazione due ''Button'', uno per avviare l'esecuzione ed un altro per attivare la funzione di Alsa di arresto dell'esecuzione, nonché una ''TextLabel'':
 +
Private handle As Pointer
 +
 
 +
 +
Library "libasound:2.0.0"
 +
 
  Private Const SND_PCM_STREAM_PLAYBACK As Byte = 0
 
  Private Const SND_PCM_STREAM_PLAYBACK As Byte = 0
 
  Private Const SND_PCM_FORMAT_U8 As Byte = 1
 
  Private Const SND_PCM_FORMAT_U8 As Byte = 1
 
  Private Const SND_PCM_FORMAT_S16_LE As Byte = 2
 
  Private Const SND_PCM_FORMAT_S16_LE As Byte = 2
 
  Private Const SND_PCM_ACCESS_RW_INTERLEAVED As Byte = 3
 
  Private Const SND_PCM_ACCESS_RW_INTERLEAVED As Byte = 3
Private Const SND_PCM_STATE_XRUN As Byte = 4              <FONT color=gray>' ''Fermato''</font>
 
 
 
 
   
 
   
Library "libasound:2"
+
  <FONT color=gray>' ''int snd_pcm_open (snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode)''
 
  <FONT color=gray>' ''int snd_pcm_open(snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode)''
 
 
  ' ''Opens a PCM.''</font>
 
  ' ''Opens a PCM.''</font>
 
  Private Extern snd_pcm_open(pcm As Pointer, nome As String, stream As Integer, mode As Integer) As Integer
 
  Private Extern snd_pcm_open(pcm As Pointer, nome As String, stream As Integer, mode As Integer) As Integer
Riga 74: Riga 102:
 
  Private Extern snd_pcm_set_params(pcm As Pointer, formatB As Byte, accessB As Byte,  channels As Integer, rate As Integer, soft_resample As Integer, latency As Integer) As Integer
 
  Private Extern snd_pcm_set_params(pcm As Pointer, formatB As Byte, accessB As Byte,  channels As Integer, rate As Integer, soft_resample As Integer, latency As Integer) As Integer
 
   
 
   
  <FONT color=gray>' ''snd_pcm_sframes_t snd_pcm_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)''
+
  <FONT color=gray>' ''snd_pcm_sframes_t snd_pcm_writei (snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)''
 
  ' ''Write interleaved frames to a PCM.''</font>
 
  ' ''Write interleaved frames to a PCM.''</font>
 
  Private Extern snd_pcm_writei(pcm As Pointer, buffer As Pointer, uframes As Long) As Integer
 
  Private Extern snd_pcm_writei(pcm As Pointer, buffer As Pointer, uframes As Long) As Integer
 
   
 
   
  <FONT color=gray>' ''int snd_pcm_status (snd_pcm_t *pcm, snd_pcm_status_t *status)''
+
  <FONT color=gray>' ''int snd_pcm_drain (snd_pcm_t *pcm)''
  ' ''Obtain status (runtime) information for PCM handle.''</font>
+
  ' ''Stop a PCM preserving pending frames.''</font>
  Private Extern snd_pcm_status(pcm As Pointer, status As Pointer) As Integer
+
  Private Extern snd_pcm_drain(pcm As Pointer) As Integer
 +
 +
<FONT color=gray>' ''int snd_pcm_drop (snd_pcm_t *pcm)''
 +
' ''Stop a PCM dropping pending frames.''</font>
 +
Private Extern snd_pcm_drop(pcmP As Pointer) As Integer
 
   
 
   
  <FONT color=gray>' ''int snd_pcm_close(snd_pcm_t *pcm)''
+
  <FONT color=gray>' ''int snd_pcm_close (snd_pcm_t *pcm)''
 
  ' ''Close PCM handle.''</font>
 
  ' ''Close PCM handle.''</font>
 
  Private Extern snd_pcm_close(pcm As Pointer) As Integer
 
  Private Extern snd_pcm_close(pcm As Pointer) As Integer
 
   
 
   
 
   
 
   
  '''Public''' Sub Main()
+
  Public Sub Button1_Click()
 
   
 
   
 
   Dim fileWAV, s As String
 
   Dim fileWAV, s As String
 
   Dim fl As File
 
   Dim fl As File
 
   Dim buff As Byte[]
 
   Dim buff As Byte[]
  Dim handle As Pointer
 
 
   Dim err, rBit, frequenza, obr, dim_dati, frames, stato As Integer
 
   Dim err, rBit, frequenza, obr, dim_dati, frames, stato As Integer
   Dim quant As Integer = 32
+
   Dim quant As Integer = 1024
   Dim tipo, canali, bits, dati As Short
+
   Dim tipo, canali, bits, dati, c As Short
 
 
   
 
   
 
  <FONT color=gray>' ''Carica un file audio Wav:''</font>
 
  <FONT color=gray>' ''Carica un file audio Wav:''</font>
  fileWAV = "<FONT color=gray>''/percorso/del/file.wav''</font>"
+
  fileWAV = "<FONT color=darkgreen>''/percorso/del/file.wav''</font>"
  fl = Open fileWAV For Read  
+
  fl = Open fileWAV For Read  
 
      
 
      
 
  <FONT color=gray>' ''Inizia i controlli di verifica del file caricato:''</font>
 
  <FONT color=gray>' ''Inizia i controlli di verifica del file caricato:''</font>
  Seek #fl, 12
+
  Seek #fl, 12
  Read #fl, s, 3
+
  Read #fl, s, 3
  If s <> "fmt" Then Error.Raise("File errato !")
+
  If s <> "fmt" Then Error.Raise("File errato !")
 
    
 
    
 
  <FONT color=gray>' ''Legge il tipo di formato (legge 2 byte):''</font>
 
  <FONT color=gray>' ''Legge il tipo di formato (legge 2 byte):''</font>
  Seek #fl, 20
+
  Seek #fl, 20
  Read #fl, tipo
+
  Read #fl, tipo
  If tipo <> 1 Then Error.Raise("Non è un file PCM !")
+
  If tipo <> 1 Then Error.Raise("Non è un file PCM !")
 
    
 
    
 
  <FONT color=gray>' ''Rileva il numero di canali (legge 2 byte):''</font>
 
  <FONT color=gray>' ''Rileva il numero di canali (legge 2 byte):''</font>
  Read #fl, canali
+
  Read #fl, canali
 
    
 
    
 
  <FONT color=gray>' ''Rileva la frequenza di campionamento (legge 4 byte):''</font>
 
  <FONT color=gray>' ''Rileva la frequenza di campionamento (legge 4 byte):''</font>
  Read #fl, frequenza
+
  Read #fl, frequenza
 
    
 
    
 
  <FONT color=gray>' ''Rileva la risoluzione del campionamento (legge 2 byte):''</font>
 
  <FONT color=gray>' ''Rileva la risoluzione del campionamento (legge 2 byte):''</font>
  Seek #fl, 34
+
  Seek #fl, 34
  Read #fl, bits
+
  Read #fl, bits
 
      
 
      
 
  <FONT color=gray>' ''Verifica la risoluzione in bit dei campioni audio del file Wav:''</font>
 
  <FONT color=gray>' ''Verifica la risoluzione in bit dei campioni audio del file Wav:''</font>
  Select Case bits
+
  Select Case bits
    Case 8
+
    Case 8
      rBit = SND_PCM_FORMAT_U8             
+
      rBit = SND_PCM_FORMAT_U8             
    Case 16
+
    Case 16
      rBit = SND_PCM_FORMAT_S16_LE
+
      rBit = SND_PCM_FORMAT_S16_LE
    Case Else
+
    Case Else
      Error.Raise("Risoluzione " & bits & " non prevista dal presente applicativo !")
+
      Error.Raise("Risoluzione " & bits & " non prevista dal presente applicativo !")
  End Select
+
  End Select
 
    
 
    
  Print "Numero canali:  "; canali
+
  Print "Numero canali:  "; canali
  Print "Frequenza:      "; frequenza; " hertz"
+
  Print "Frequenza:      "; frequenza; " hertz"
  Print "Risoluzione:    "; bits; " bit"
+
  Print "Risoluzione:    "; bits; " bit"
  obr = frequenza * bits * canali
+
  obr = frequenza * bits * canali
  Print "Overall BitRate: "; obr; " bps"
+
  Print "Overall BitRate: "; obr; " bps"
 
    
 
    
 
  <FONT color=gray>' ''I successivi byte rappresentano il blocco dei dati audio grezzi del file:''</font>
 
  <FONT color=gray>' ''I successivi byte rappresentano il blocco dei dati audio grezzi del file:''</font>
  Seek #fl, 0
+
  Seek #fl, 0
  Read #fl, s, 256
+
  Read #fl, s, 256
  dati = InStr(s, "data")
+
  dati = InStr(s, "data")
  dim_dati = Lof(fl) - (dati + 7)
+
  dim_dati = Lof(fl) - (dati + 7)
  Print "Dati audio:      "; dim_dati; " byte"
+
  Print "Dati audio:      "; dim_dati; " byte"
 
   
 
   
 
  <FONT color=gray>' ''Avendo queste informazioni generali, posiamo ricavare la durata in secondi del brano audio:''</font>
 
  <FONT color=gray>' ''Avendo queste informazioni generali, posiamo ricavare la durata in secondi del brano audio:''</font>
  Print "Durata:          "; CStr(Date(0, 0, 0, 0, 0, 0, ((dim_dati * 8) / obr) * 1000))
+
  Print "Durata:          "; CStr(Time(0, 0, 0, ((dim_dati * 8) / obr) * 1000))
  Print
+
  Print
+
 
 
 
  <FONT color=gray>' ''Legge mediante "Seek" soltanto i dati audio grezzi da inviare successivamente all'interfaccia PCM di Alsa:''</font>
 
  <FONT color=gray>' ''Legge mediante "Seek" soltanto i dati audio grezzi da inviare successivamente all'interfaccia PCM di Alsa:''</font>
  <FONT color=#B22222>buff</font> = New Byte[quant]
+
  <FONT color=#B22222>buff</font> = New Byte[quant]
  Seek #fl, dati + 7
+
  Seek #fl, dati + 7
 
   
 
   
 
  <FONT color=gray>' ''Finalmente vengono utilizzate le funzioni esterne essenziali di Alsa:''</font>
 
  <FONT color=gray>' ''Finalmente vengono utilizzate le funzioni esterne essenziali di Alsa:''</font>
  err = snd_pcm_open(VarPtr(handle), "default", SND_PCM_STREAM_PLAYBACK, 0)
+
  err = snd_pcm_open(VarPtr(handle), "default", SND_PCM_STREAM_PLAYBACK, 0)
  If err < 0 Then Error.Raise("Errore nell'apertura del subsistema PCM: " & snd_strerror(err))
+
  If err < 0 Then Error.Raise("Errore nell'apertura del subsistema PCM: " & snd_strerror(err))
 
   
 
   
  err = snd_pcm_set_params(handle, rBit, SND_PCM_ACCESS_RW_INTERLEAVED, canali, frequenza, 1, 500000)
+
  err = snd_pcm_set_params(handle, rBit, SND_PCM_ACCESS_RW_INTERLEAVED, canali, frequenza, 1, 500000)
  If err < 0 Then Error.Raise("Errore nell'impostazione dei parametri audio: " & snd_strerror(err))
+
  If err < 0 Then Error.Raise("Errore nell'impostazione dei parametri audio: " & snd_strerror(err))
 +
 
 +
  c = bits / (8 / canali)
 
   
 
   
 
  <FONT color=gray>' ''Scrive i dati nell'interfaccia PCM.''</font>
 
  <FONT color=gray>' ''Scrive i dati nell'interfaccia PCM.''</font>
  While stato < dim_dati
+
  While stato < dim_dati
 
+
    buff.Read(fl, 0, quant)
    buff.Read(fl, 0, quant)
+
    wait 0.001
   
+
    frames = snd_pcm_writei(handle, buff.Data, quant / c)
    frames = snd_pcm_writei(handle, buff.Data, quant / (bits / (8 / canali)))
+
    If frames < 0 Then Break
    If frames < 0 Then Error.Raise("Errore nella funzione 'snd_pcm_writei()' !")
+
  <FONT color=gray>' ''Scrive il tempo trascorso ad esempio in una "TextLabel":''</font>
   
+
    TextLabel1.Text = "Tempo trascorso: " & Time(0, 0, 0, ((stato * 8) / obr) * 1000)
  <FONT color=gray>' ''La scrittura del tempo trascorso in console può determinare nei computer più lenti un errore nella funzione 'snd_pcm_writei()'.''
+
    stato += quant
' ''In tal caso sarà necessario eliminare la seguente linea di comando che scrive il tempo trascorso.''</font>
+
    If dim_dati - stato < 1024 Then quant = dim_dati - stato
    Write #File.Out, "\rTempo trascorso: " & Date(0, 0, 0, 0, 0, 0, ((stato * 8) / obr) * 1000)
+
  Wend
   
 
    stato += quant
 
   
 
    If dim_dati - stato < 32 Then quant = dim_dati - stato
 
   
 
  Wend
 
 
    
 
    
 +
  snd_pcm_drain(handle)
 
    
 
    
  <FONT color=gray>' ''Esegue il ciclo finché lo status del PCM non è nella condizione "Fermato":''</font>
+
  <FONT color=gray>' ''Alla fine dell'esecuzione del file audio chiude il subsistema PCM ed il file:''</font>
  Do
+
  err = snd_pcm_close(handle)
<FONT color=gray>' ''Rileva lo status dell'interfaccia PCM:''</font>
+
  If err = 0 Then Print "\nChiusura dell'interfaccia PCM: regolare."
    snd_pcm_status(handle, VarPtr(stato))
+
  fl.Close
  Loop Until stato = SND_PCM_STATE_XRUN
+
 +
End
 +
 
 
   
 
   
  <FONT color=gray>' ''Alla fine dell'esecuzione del file audio chiude il subsistema PCM ed il file:''</font>
+
  Public Sub Button2_Click()  <FONT color=gray>' ''Arresta l'esecuzione''</font>
  err = snd_pcm_close(handle)
 
  If err = 0 Then Print "Chiusura dell'interfaccia PCM: regolare."
 
  fl.Close
 
 
   
 
   
  '''End'''
+
  Dim err As Integer
 +
 
 +
  err = snd_pcm_drop(handle)
 +
  If err <> 0 Then Error.Raise("Impossibile fermare l'esecuzione dei dati audio !")
 +
 
 +
  Print "\nEsecuzione fermata !"
 +
 
 +
  End
  
  
==Fermare l'esecuzione dei dati audio==
+
===Esempi pratici con applicazione ''a riga di comando''===
Per arrestare l'esecuzione dei dati audio, si potrà utilizzare la seguente funzione di Alsa:
+
Mostriamo ora un codice simile al precedente, ma questa volta con un'applicazione ''a riga di comando''.
''int snd_pcm_drop (snd_pcm_t * pcm)''
+
  Library "libasound:2.0.0"
<BR>In Gambas sarà così dichiarata:
+
Private Extern snd_pcm_drop(pcmP As Pointer) As Integer
 
ed utilizzata nella seguente maniera:
 
snd_pcm_drop(handle)
 
Se si chiama questa funzione ''snd_pcm_drop()'', da un lato l'altra funzione ''snd_pcm_writei()'' restituisce un valore d'errore uguale a -77 |[[#Note|1]]|, dall'altro lo ''status'' dell'interfaccia PCM assume il valore 1 (identificatore: ''SND_PCM_STATE_SETUP''). Pertanto potremo dichiarare il nuovo valore dello ''status'' come una costante:
 
  Private Const SND_PCM_STATE_SETUP As Byte = 1
 
 
 
Di tali valori dovremo, dunque, tenere conto nel codice.
 
 
 
Se per arrestare l'esecuzione si utilizza, come prevedibile, un ''Button'', poiché l'esecuzione provoca l'impossibilità di agire su eventuali oggetti posti sul ''Form'', si dovrà impostare prima o dopo la funzione "''snd_pcm_writei()''" un ''Wait'' al minimo valore:
 
Wait 0.001
 
 
 
 
 
 
 
===Il codice con ''Arresto'' dell'esecuzione dei dati audio===
 
Mostriamo, di seguito, il precedente codice con l'aggiunta di un ''Button'' per attivare la funzione di Alsa di arresto dell'esecuzione dei dati audio:
 
 
  Private Const SND_PCM_STREAM_PLAYBACK As Byte = 0
 
  Private Const SND_PCM_STREAM_PLAYBACK As Byte = 0
 +
Private Const SND_PCM_FORMAT_U8 As Byte = 1
 +
Private Const SND_PCM_FORMAT_S16_LE As Byte = 2
 
  Private Const SND_PCM_ACCESS_RW_INTERLEAVED As Byte = 3
 
  Private Const SND_PCM_ACCESS_RW_INTERLEAVED As Byte = 3
Private Const SND_PCM_FORMAT_U8 As Byte = 0
 
Private Const SND_PCM_FORMAT_S16_LE As Byte = 2
 
Private Const SND_PCM_FORMAT_S24_LE As Byte = 7
 
Private Const SND_PCM_STATE_XRUN As Byte = 4              <FONT color=gray>' ''Fermato''</font>
 
Private Const SND_PCM_STATE_SETUP As Byte = 1
 
Private handle As Pointer
 
Private finis As Boolean
 
 
   
 
   
+
  <FONT color=gray>' ''int snd_pcm_open (snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode)''
Library "libasound:2"
+
  ' ''Opens a PCM.''</font>
+
  Private Extern snd_pcm_open(pcm As Pointer, nome As String, stream As Integer, mode As Integer) As Integer
  <FONT color=gray>' ''int snd_pcm_open(snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode)''
 
  ' 'Apre il sub-sistema PCM.''</font>
 
  Private Extern snd_pcm_open(pcmP As Pointer, nome As String, stream As Integer, mode As Integer) As Integer
 
 
   
 
   
 
  <FONT color=gray>' ''const char * snd_strerror (int errnum)''
 
  <FONT color=gray>' ''const char * snd_strerror (int errnum)''
  ' ''Returns the message For an Error code.''</font>
+
  ' ''Returns the message for an Error code.''</font>
 
  Private Extern snd_strerror(errnum As Integer) As String
 
  Private Extern snd_strerror(errnum As Integer) As String
 
   
 
   
 
  <FONT color=gray>' ''int snd_pcm_set_params (snd_pcm_t *pcm, snd_pcm_format_t format, snd_pcm_access_t access, unsigned int canali, unsigned int rate, int soft_resample, unsigned int latency)
 
  <FONT color=gray>' ''int snd_pcm_set_params (snd_pcm_t *pcm, snd_pcm_format_t format, snd_pcm_access_t access, unsigned int canali, unsigned int rate, int soft_resample, unsigned int latency)
 
  ' ''Set the hardware and software parameters in a simple way.''</font>
 
  ' ''Set the hardware and software parameters in a simple way.''</font>
  Private Extern snd_pcm_set_params(pcmP As Pointer, formatB As Byte, accessB As Byte, canali As Integer, rate As Integer, soft_resample As Integer, latency As Integer) As Integer
+
  Private Extern snd_pcm_set_params(pcm As Pointer, formatB As Byte, accessB As Byte, channels As Integer, rate As Integer, soft_resample As Integer, latency As Integer) As Integer
 
   
 
   
  <FONT color=gray>' ''snd_pcm_sframes_t snd_pcm_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)''
+
  <FONT color=gray>' ''snd_pcm_sframes_t snd_pcm_writei (snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)''
  ' ''Write interleaved frames to a PCM''
+
  ' ''Write interleaved frames to a PCM.''</font>
  ' ''In questo esempio dichiariamo il secondo argomento come un vettore di tipo Byte:''</font>
+
Private Extern snd_pcm_writei(pcm As Pointer, buffer As Pointer, uframes As Long) As Integer
  Private Extern snd_pcm_writei(pcmP As Pointer, buffA As Byte[], uframes As Long) As Integer
+
 
 +
  <FONT color=gray>' ''int snd_pcm_drain (snd_pcm_t *pcm)''
 +
' ''Stop a PCM preserving pending frames.''</font>
 +
  Private Extern snd_pcm_drain(pcm As Pointer) As Integer
 
   
 
   
  <FONT color=gray>' ''int snd_pcm_status (snd_pcm_t *pcm, snd_pcm_status_t *status)''
+
  <FONT color=gray>' ''int snd_pcm_close (snd_pcm_t *pcm)''
  ' ''Obtain status (runtime) information for PCM handle.''</font>
+
  ' ''Close PCM handle.''</font>
  Private Extern snd_pcm_status(pcmP As Pointer, statP As Pointer) As Integer
+
  Private Extern snd_pcm_close(pcm As Pointer) As Integer
 
   
 
   
<FONT color=gray>' ''int snd_pcm_drop (snd_pcm_t *pcm)''
 
' ''Stop a PCM dropping pending frames.''</font>
 
Private Extern snd_pcm_drop(pcmP As Pointer) As Integer
 
 
   
 
   
  <FONT color=gray>' ''int snd_pcm_close(snd_pcm_t *pcm)''
+
  Public Sub Main()
' ''Chiude il sub-sistema PCM''</font>
 
Private Extern snd_pcm_close(pcmP As Pointer) As Integer
 
 
 
'''Public''' Sub Button1_Click()
 
 
  Dim percorsoWAV, inte$ As String
 
  Dim fl, fl2 As File
 
  Dim buffA, buf As Byte[]
 
  Dim risolBit As Byte
 
  Dim err, frequenza, quantitas, frames, status, somma_frames As Integer
 
  Dim fmt, pcm, canali, bits, da As Short
 
  Dim buffer As Short = 512
 
 
   
 
   
 +
  Dim fileWAV, s As String
 +
  Dim fl As File
 +
  Dim handle As Pointer
 +
  Dim buff As Byte[]
 +
  Dim err, rBit, frequenza, obr As Integer
 +
  Dim stato, dim_dati, frames As Integer
 +
  Dim quant As Integer = 1024
 +
  Dim tipo, canali, bits, dati, c As Short
 
   
 
   
 
  <FONT color=gray>' ''Carica un file audio Wav:''</font>
 
  <FONT color=gray>' ''Carica un file audio Wav:''</font>
  percorsoWAV = "''/percorso/del/file.wav''"
+
  fileWAV = "<FONT color=darkgreen>''/percorso/del/file.wav''</font>"
  fl = Open percorsoWAV For Read  
+
  fl = Open fileWAV For Read  
 
      
 
      
  buf = New Byte[](1024)
 
  buf.Read(fl, 0, buf.Count)
 
  inte$ = buf.ToString(0, buf.Count)
 
 
 
  <FONT color=gray>' ''Inizia i controlli di verifica del file caricato:''</font>
 
  <FONT color=gray>' ''Inizia i controlli di verifica del file caricato:''</font>
  fmt = InStr(inte$, "fmt ")
+
  Seek #fl, 12
+
  Read #fl, s, 3
  If fmt = 0 Then
+
  If s <> "fmt" Then Error.Raise("File errato !")
+
    
    Error.Raise("File errato !")
 
 
   Else
 
 
  <FONT color=gray>' ''Legge il tipo di formato (legge 2 byte):''</font>
 
  <FONT color=gray>' ''Legge il tipo di formato (legge 2 byte):''</font>
    Seek #fl, fmt + 7
+
  Seek #fl, 20
    Read #fl, pcm
+
  Read #fl, tipo
    If pcm <> 1 Then Error.Raise("Non è un file PCM !")
+
  If tipo <> 1 Then Error.Raise("Non è un file PCM !")
+
 
 
  <FONT color=gray>' ''Rileva il numero di canali (legge 2 byte):''</font>
 
  <FONT color=gray>' ''Rileva il numero di canali (legge 2 byte):''</font>
    Read #fl, canali
+
  Read #fl, canali
+
 
 
  <FONT color=gray>' ''Rileva la frequenza di campionamento (legge 4 byte):''</font>
 
  <FONT color=gray>' ''Rileva la frequenza di campionamento (legge 4 byte):''</font>
    Read #fl, frequenza
+
  Read #fl, frequenza
+
 
 
  <FONT color=gray>' ''Rileva la risoluzione del campionamento (legge 2 byte):''</font>
 
  <FONT color=gray>' ''Rileva la risoluzione del campionamento (legge 2 byte):''</font>
    Seek #fl, fmt + 21
+
  Seek #fl, 34
    Read #fl, bits
+
  Read #fl, bits
 
      
 
      
  Endif
 
 
 
  <FONT color=gray>' ''Verifica la risoluzione in bit dei campioni audio del file Wav:''</font>
 
  <FONT color=gray>' ''Verifica la risoluzione in bit dei campioni audio del file Wav:''</font>
    Select Case bits
+
  Select Case bits
      Case 8
+
    Case 8
        risolBit = SND_PCM_FORMAT_U8
+
      rBit = SND_PCM_FORMAT_U8          
               
+
    Case 16
      Case 16
+
      rBit = SND_PCM_FORMAT_S16_LE
        risolBit = SND_PCM_FORMAT_S16_LE
+
    Case Else
       
+
      Error.Raise("Risoluzione " & bits & " non prevista dal presente applicativo !")
      Case Else
+
  End Select
        Error.Raise("Risoluzione " & bits & " non prevista dal presente applicativo !")
+
 
    End Select
+
  Print "File audio:      "; fileWAV
+
  Print "Numero canali:   "; canali
    Print "Canali di uscita: "; canali
+
  Print "Frequenza:       "; frequenza; " hertz"
    Print "Frequenza: Hz "; frequenza
+
  Print "Risoluzione:     "; bits; " bit"
    Print "Risoluzione: "; bits; " bit"
+
  obr = frequenza * bits * canali
+
  Print "Overall BitRate: "; obr; " bps"
+
 
  <FONT color=gray>' ''I successivi byte rappresentano il blocco dei dati audio del campione:''</font>
+
  <FONT color=gray>' ''I successivi byte rappresentano il blocco dei dati audio grezzi del file:''</font>
    da = InStr(inte$, "data")
+
  Seek #fl, 0
    quantitas = Stat(percorsoWAV).Size - (da + 7)
+
  Read #fl, s, 256
    Print "Quantità dei dati audio: "; quantitas; " byte"
+
  dati = InStr(s, "data")
 +
  dim_dati = Lof(fl) - (dati + 7)
 +
  Print "Dati audio:     "; dim_dati; " byte"
 
   
 
   
 
  <FONT color=gray>' ''Avendo queste informazioni generali, posiamo ricavare la durata in secondi del brano audio:''</font>
 
  <FONT color=gray>' ''Avendo queste informazioni generali, posiamo ricavare la durata in secondi del brano audio:''</font>
    Print "Durata del brano: "; CStr(Date(0, 0, 0, 0, 0, 0, (quantitas * 8) / (frequenza * bits * canali) * 1000))
+
  Print "Durata:         "; CStr(Time(0, 0, 0, ((dim_dati * 8) / obr) * 1000))
+
  Print
+
 
  <FONT color=gray>' ''Legge mediante "Seek" soltanto i dati audio da inviare successivamente all'interfaccia PCM di Alsa:''</font>
+
  <FONT color=gray>' ''Legge mediante "Seek" soltanto i dati audio grezzi da inviare successivamente all'interfaccia PCM di Alsa:''</font>
    <FONT color=#B22222>buffA</font> = New Byte[](buffer)
+
  <FONT color=#B22222>buff</font> = New Byte[quant]
    Seek #fl, da + 7
+
  Seek #fl, dati + 7
 
 
   
 
   
 
  <FONT color=gray>' ''Finalmente vengono utilizzate le funzioni esterne essenziali di Alsa:''</font>
 
  <FONT color=gray>' ''Finalmente vengono utilizzate le funzioni esterne essenziali di Alsa:''</font>
    err = snd_pcm_open(VarPtr(handle), "default", SND_PCM_STREAM_PLAYBACK, 0)
+
  err = snd_pcm_open(VarPtr(handle), "default", SND_PCM_STREAM_PLAYBACK, 0)
    If err < 0 Then Error.Raise("Errore nell'apertura del subsistema PCM: " & snd_strerror(err))
+
  If err < 0 Then Error.Raise("Errore nell'apertura del subsistema PCM: " & snd_strerror(err))
+
 
    err = snd_pcm_set_params(handle, risolBit, SND_PCM_ACCESS_RW_INTERLEAVED, canali, frequenza, 1, 500000)
+
  err = snd_pcm_set_params(handle, rBit, SND_PCM_ACCESS_RW_INTERLEAVED, canali, frequenza, 1, 500000)
    If err < 0 Then Error.Raise("Errore nell'impostazione dei parametri audio: " & snd_strerror(err))
+
  If err < 0 Then Error.Raise("Errore nell'impostazione dei parametri audio: " & snd_strerror(err))
 +
 
 +
  c = bits / (8 / canali)
 +
 
 +
<FONT color=gray>' ''Scrive i dati nell'interfaccia PCM.''</font>
 +
  While stato < dim_dati
 +
 
 +
    <FONT color=#B22222>buff</font>.Read(fl, 0, quant)
 +
   
 +
    frames = snd_pcm_writei(handle, <FONT color=#B22222>buff</font>.Data, quant / c)
 +
    If frames < 0 Then Break
 +
   
 +
<FONT color=gray>' ''Mostra in console e in rosso il tempo trascorso:''</font>
 +
    Write "\r\e[0mTempo trascorso: \e[31m" & Time(0, 0, 0, ((stato * 8) / obr) * 1000)
 +
         
 +
    stato += quant
 +
    If dim_dati - stato < 1024 Then quant = dim_dati - stato
 +
   
 +
  Wend
 
    
 
    
    fl2 = Open "/dev/stdout" For Write
 
 
    finis = False
 
 
    Do
 
 
<FONT color=gray>' ''Carichiamo nella variabile vettore i dati audio:''</font>
 
      If (Lof(fl) - (da + 7)) - Seek(fl) < 512 Then buffer = (Lof(fl) - (da + 7)) - Seek(fl)
 
 
      buffA.Read(fl, 0, buffer)
 
 
      Wait 0.001
 
 
    
 
    
  <FONT color=gray>' ''Scrive i dati nell'interfaccia PCM.''
+
  <FONT color=gray>' ''Va in chiusura:''</font>
' ''In questo esempio il buffer dei dati è rappresentato da un variabile array di tipo "Byte".''</font>
+
  fl.Close
      frames = snd_pcm_writei(handle, <FONT color=#B22222>buffA</font>, buffer / SizeOf(gb.Integer))
+
 
+
  snd_pcm_drain(handle)
      somma_frames += frames
 
 
<FONT color=gray>' ''Mostriamo il trascorrere dei secondi sulla base dei frames letti e sino al termine del brano audio:''</font>
 
      Write #fl2, "\r" & Date(0, 0, 0, 0, 0, 0, (somma_frames * 8 * SizeOf(gb.Integer)) / (frequenza * bits * canali) * 1000)
 
 
    Loop Until frames = 0 Or finis
 
 
    
 
    
 +
  Print "\nEsecuzione terminata !"
 
    
 
    
<FONT color=gray>' ''Esegue il ciclo finché lo status del PCM non è nella condizione "Fermato" o uguale a "-77":''</font>
+
  <FONT color=gray>' ''Alla fine dell'esecuzione del file audio chiude il subsistema PCM ed il file:''</font>
    Do
+
  err = snd_pcm_close(handle)
<FONT color=gray>' ''Rileva lo status dell'interfaccia PCM:''</font>
+
  If err = 0 Then Print "\nChiusura dell'interfaccia PCM: regolare."
      snd_pcm_status(handle, VarPtr(status))
+
    
+
  End
    Loop Until (status = SND_PCM_STATE_XRUN) Or (status = SND_PCM_STATE_SETUP)
 
 
 
 
  <FONT color=gray>' ''Alla fine dell'esecuzione del file audio chiude il file ed il subsistema PCM:''</font>
 
    fl2.Close
 
    fl.Close
 
    err = snd_pcm_close(handle)
 
    If err = 0 Then Print "\nChiusura dell'interfaccia PCM: regolare."
 
 
'''End'''
 
 
 
'''Public''' Sub Button2_Click()  <FONT color=gray>' ''Arresta l'esecuzione''</font>
 
 
  Dim err As Integer
 
 
    err = snd_pcm_drop(handle)
 
    If err = 0 Then
 
      Print "\nEsecuzione fermata !"
 
      finis = True
 
    Else
 
      Error.Raise("Impossibile fermare l'esecuzione dei dati audio !")
 
    Endif    
 
   
 
'''End'''
 
  
  
Riga 398: Riga 370:
 
=Note=
 
=Note=
 
[1] il valore 77 nel file ''errno.h'' corrisponde all'identificatore: ''EBADFD  /* File descriptor in bad state */''.
 
[1] il valore 77 nel file ''errno.h'' corrisponde all'identificatore: ''EBADFD  /* File descriptor in bad state */''.
 +
 +
[2] Riguardo all'istruzione ''escape'' "\e[7;0f" vedere la seguente pagina: [[Spostare da codice il cursore all'interno del Terminale]].

Versione attuale delle 11:35, 13 gen 2024

Per eseguire un file audio con le sole risorse dell'API di Alsa sono sufficienti sostanzialmente tre funzioni.

Si dovrà innanzitutto dichiarare la libreria di Alsa contenente le funzioni esterne che saranno utilizzate. Tale libreria è attualmente libasound.so.2.0.0, e che sarà in Gambas così dichiarata:

Library "libasound:2.0.0"

Si dovrà quindi aprire il subsistema PCM di Alsa e creare un handle per la sua gestione successiva. Questa operazione iniziale verrà compiuta con la funzione:

int   snd_pcm_open(snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode)

laddove i parametri rappresentano:

  • **pcm: un puntatore ad un puntatore per la creazione dell'handle dell'interfaccia PCM;
  • *name: una stringa che è il nome dell'handle, e che sarà inizialmente: "default";
  • stream: un numero intero che è il tipo di flusso dati (SND_PCM_STREAM_PLAYBACK oppure SND_PCM_STREAM_CAPTURE);
  • mode: un numero intero che è la modalità di apertura del subsistema PCM (SND_PCM_NONBLOCK oppure SND_PCM_ASYNC).

Questa funzione sarà così dichiarata in Gambas con la funzione Extern:

Private Extern snd_pcm_open(pcmP As Pointer, nome As String, stream As Integer, mode As Integer) As Integer

e nel codice sarà così invocata:

Private Const nomen As String = "default"
Private Const SND_PCM_STREAM_PLAYBACK As Byte = 0
Private Const SND_PCM_NONBLOCK As Byte = 0

......
......
err = snd_pcm_open(VarPtr(handle), nomen, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)


La seconda funzione essenziale è:

int snd_pcm_set_params (snd_pcm_t *pcm, snd_pcm_format_t format, snd_pcm_access_t access, unsigned int channels, unsigned int rate, int soft_resample, unsigned int latency)

con la quale vengono impostati i parametri audio hardware e software.
In Gambas verrà così dichiarata:

Private Extern snd_pcm_set_params(pcmP As Pointer, formatB As Byte, accessB As Byte, channels As Integer, rate As Integer, soft_resample As Integer, latency As Integer) As Integer

e verrà così richiamata per essere utilizzata:

Private Const SND_PCM_ACCESS_RW_INTERLEAVED As Byte = 3
......
......
err = snd_pcm_set_params(handle, rBit, SND_PCM_ACCESS_RW_INTERLEAVED, canali, frequenza, 1, 500000)

Il numero di canali e la frequenza del flusso dei dati audio verranno estrapolati dai dati stessi del file audio caricato. Nel caso di un file wav, ad esempio, tali dati sono contenuti nel primo blocco del file, ossia all'interno dei primi 44/46 dati.


La terza funzione esterna essenziale è:

snd_pcm_sframes_t snd_pcm_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)

che scrive i dati audio, precedentemente raccolti dal file audio caricato (nel caso di un file wav essi sono contenuti nel secondo blocco di del file), nel subsistema PCM, e quindi così li invia ad Alsa.
In Gambas sarà così dichiarata:

Private Extern snd_pcm_writei(pcmP As Pointer, buffP As Pointer, uframes As Long) As Integer

laddove:

  • pcmP: è l'handle per l'interfaccia PCM;
  • buffP: è una variabile di tipo puntatore che contiene i dati da scrivere/inviare ad Alsa tramite l'handle del subsistema PCM. Questa variabile è un'area di memoria allocata della dimensione almeno pari alla quantità complessiva dei dati audio da inviare ad Alsa. Da sottolineare che questa variabile potrà, volendo, essere anche dichiarata come vettore (array) di tipo Byte[ ] che sarà ovviamente riempito dei dati audio da inviare ad Alsa.
  • uframes: è un valore che rappresenta la quantità di dati, presenti nel buffer prima descritto, che devono essere inviati ad Alsa. Questo valore deve essere pari alla dimensione del buffer diviso la seguente operazione: risoluzione-bit / (8 / numero-canali).

La funzione sarà quindi richiamata in codice come segue:

frames = snd_pcm_writei(handle, buffP, quant / (bits / (8 / canali)))

La variabile di tipo Intero "quant" rappresenta la quantità massima di dati per ciascun ciclo da da inviare al sub-sistema PCM.

Prima della chiusura del codice viene posta la funzione snd_pcm_drain(), la quale impedisce che al termine l'esecuzione venga un po' troncata inaspettatamente. Tale funzione, dunque, consente di eseguire la quantità totale dei dati audio grezzi.
Questa funzione, volendo può essere sostituita dal seguente ciclo:

' Esegue il ciclo finché lo status del PCM non è nella condizione "Fermato":
 Repeat
' Rileva lo status dell'interfaccia PCM:
   snd_pcm_status(handle, VarPtr(stato))
 Until stato = SND_PCM_STATE_XRUN

In tal caso si dovrà avere ura di dichiarare opportunamente la funzione snd_pcm_status() e la costante SND_PCM_STATE_XRUN.

Infine viene posta la funzione snd_pcm_close() che chiude il sub-sistema PCM.


Fermare l'esecuzione dei dati audio

Per arrestare l'esecuzione dei dati audio, si potrà utilizzare la seguente funzione di Alsa:

int snd_pcm_drop (snd_pcm_t * pcm)


In Gambas sarà così dichiarata:

Private Extern snd_pcm_drop(pcmP As Pointer) As Integer

ed utilizzata nella seguente maniera:

snd_pcm_drop(handle)

Se si chiama questa funzione snd_pcm_drop(), da un lato l'altra funzione snd_pcm_writei() restituisce un valore d'errore uguale a -77 [Nota 1], dall'altro lo status dell'interfaccia PCM assume il valore 1 (identificatore: SND_PCM_STATE_SETUP).
Pertanto se si vorrà ad esempio utilizzare un ciclo - come si è visto nel paragrafo precedente - per impedire che l'esecuzione sia troncata alla fine, si potrà dichiarare il nuovo valore dello status come una costante:

Private Const SND_PCM_STATE_SETUP As Byte = 1

Di tali valori dovremo, dunque, tenere conto nel codice.

Se non si farà uso del ciclo finale, si potrà agevolmente utilizzare la funzione snd_pcm_drain() già vista in precedenza.

Se per arrestare l'esecuzione si utilizza in ambiente grafico, come prevedibile, un Button, poiché l'esecuzione provoca l'impossibilità di agire su eventuali oggetti posti sul Form, si dovrà impostare prima o dopo la funzione "snd_pcm_writei()" un Wait posto al valore:

Wait 0.001


Esempio pratico con applicazione in ambiente grafico

Mostriamo, di seguito, un codice per l'esecuzione di un file WAV in ambiente grafico. Sono previsti sul Form dell'applicazione due Button, uno per avviare l'esecuzione ed un altro per attivare la funzione di Alsa di arresto dell'esecuzione, nonché una TextLabel:

Private handle As Pointer
 

Library "libasound:2.0.0"

Private Const SND_PCM_STREAM_PLAYBACK As Byte = 0
Private Const SND_PCM_FORMAT_U8 As Byte = 1
Private Const SND_PCM_FORMAT_S16_LE As Byte = 2
Private Const SND_PCM_ACCESS_RW_INTERLEAVED As Byte = 3

' int snd_pcm_open (snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode)
' Opens a PCM.
Private Extern snd_pcm_open(pcm As Pointer, nome As String, stream As Integer, mode As Integer) As Integer

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

' int snd_pcm_set_params (snd_pcm_t *pcm, snd_pcm_format_t format, snd_pcm_access_t access, unsigned int canali, unsigned int rate, int soft_resample, unsigned int latency)
' Set the hardware and software parameters in a simple way.
Private Extern snd_pcm_set_params(pcm As Pointer, formatB As Byte, accessB As Byte,  channels As Integer, rate As Integer, soft_resample As Integer, latency As Integer) As Integer

' snd_pcm_sframes_t snd_pcm_writei (snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
' Write interleaved frames to a PCM.
Private Extern snd_pcm_writei(pcm As Pointer, buffer As Pointer, uframes As Long) As Integer

' int snd_pcm_drain (snd_pcm_t *pcm)
' Stop a PCM preserving pending frames.
Private Extern snd_pcm_drain(pcm As Pointer) As Integer

' int snd_pcm_drop (snd_pcm_t *pcm)
' Stop a PCM dropping pending frames.
Private Extern snd_pcm_drop(pcmP As Pointer) As Integer

' int snd_pcm_close (snd_pcm_t *pcm)
' Close PCM handle.
Private Extern snd_pcm_close(pcm As Pointer) As Integer


Public Sub Button1_Click()

 Dim fileWAV, s As String
 Dim fl As File
 Dim buff As Byte[]
 Dim err, rBit, frequenza, obr, dim_dati, frames, stato As Integer
 Dim quant As Integer = 1024
 Dim tipo, canali, bits, dati, c As Short

' Carica un file audio Wav:
 fileWAV = "/percorso/del/file.wav"
 fl = Open fileWAV For Read 
   
' Inizia i controlli di verifica del file caricato:
 Seek #fl, 12
 Read #fl, s, 3
 If s <> "fmt" Then Error.Raise("File errato !")
  
' Legge il tipo di formato (legge 2 byte):
 Seek #fl, 20
 Read #fl, tipo
 If tipo <> 1 Then Error.Raise("Non è un file PCM !")
  
' Rileva il numero di canali (legge 2 byte):
 Read #fl, canali
  
' Rileva la frequenza di campionamento (legge 4 byte):
 Read #fl, frequenza
  
' Rileva la risoluzione del campionamento (legge 2 byte):
 Seek #fl, 34
 Read #fl, bits
   
' Verifica la risoluzione in bit dei campioni audio del file Wav:
 Select Case bits
   Case 8
     rBit = SND_PCM_FORMAT_U8            
   Case 16
     rBit = SND_PCM_FORMAT_S16_LE
   Case Else
     Error.Raise("Risoluzione " & bits & " non prevista dal presente applicativo !")
 End Select
  
 Print "Numero canali:   "; canali
 Print "Frequenza:       "; frequenza; " hertz"
 Print "Risoluzione:     "; bits; " bit"
 obr = frequenza * bits * canali
 Print "Overall BitRate: "; obr; " bps"
 
' I successivi byte rappresentano il blocco dei dati audio grezzi del file:
 Seek #fl, 0
 Read #fl, s, 256
 dati = InStr(s, "data")
 dim_dati = Lof(fl) - (dati + 7)
 Print "Dati audio:      "; dim_dati; " byte"

' Avendo queste informazioni generali, posiamo ricavare la durata in secondi del brano audio:
 Print "Durata:          "; CStr(Time(0, 0, 0, ((dim_dati * 8) / obr) * 1000))
 Print
 
' Legge mediante "Seek" soltanto i dati audio grezzi da inviare successivamente all'interfaccia PCM di Alsa:
 buff = New Byte[quant]
 Seek #fl, dati + 7

' Finalmente vengono utilizzate le funzioni esterne essenziali di Alsa:
 err = snd_pcm_open(VarPtr(handle), "default", SND_PCM_STREAM_PLAYBACK, 0)
 If err < 0 Then Error.Raise("Errore nell'apertura del subsistema PCM: " & snd_strerror(err))

 err = snd_pcm_set_params(handle, rBit, SND_PCM_ACCESS_RW_INTERLEAVED, canali, frequenza, 1, 500000)
 If err < 0 Then Error.Raise("Errore nell'impostazione dei parametri audio: " & snd_strerror(err))
  
 c = bits / (8 / canali)

' Scrive i dati nell'interfaccia PCM.
 While stato < dim_dati
   buff.Read(fl, 0, quant)
   wait 0.001
   frames = snd_pcm_writei(handle, buff.Data, quant / c)
   If frames < 0 Then Break
' Scrive il tempo trascorso ad esempio in una "TextLabel":
   TextLabel1.Text = "Tempo trascorso: " & Time(0, 0, 0, ((stato * 8) / obr) * 1000)
   stato += quant
   If dim_dati - stato < 1024 Then quant = dim_dati - stato
 Wend
  
 snd_pcm_drain(handle)
  
' Alla fine dell'esecuzione del file audio chiude il subsistema PCM ed il file:
 err = snd_pcm_close(handle)
 If err = 0 Then Print "\nChiusura dell'interfaccia PCM: regolare."
 fl.Close

End
 

Public Sub Button2_Click()   ' Arresta l'esecuzione

 Dim err As Integer
 
 err = snd_pcm_drop(handle)
 If err <> 0 Then Error.Raise("Impossibile fermare l'esecuzione dei dati audio !")
  
 Print "\nEsecuzione fermata !"
  
End


Esempi pratici con applicazione a riga di comando

Mostriamo ora un codice simile al precedente, ma questa volta con un'applicazione a riga di comando.

Library "libasound:2.0.0"

Private Const SND_PCM_STREAM_PLAYBACK As Byte = 0
Private Const SND_PCM_FORMAT_U8 As Byte = 1
Private Const SND_PCM_FORMAT_S16_LE As Byte = 2
Private Const SND_PCM_ACCESS_RW_INTERLEAVED As Byte = 3

' int snd_pcm_open (snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode)
' Opens a PCM.
Private Extern snd_pcm_open(pcm As Pointer, nome As String, stream As Integer, mode As Integer) As Integer

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

' int snd_pcm_set_params (snd_pcm_t *pcm, snd_pcm_format_t format, snd_pcm_access_t access, unsigned int canali, unsigned int rate, int soft_resample, unsigned int latency)
' Set the hardware and software parameters in a simple way.
Private Extern snd_pcm_set_params(pcm As Pointer, formatB As Byte, accessB As Byte,  channels As Integer, rate As Integer, soft_resample As Integer, latency As Integer) As Integer

' snd_pcm_sframes_t snd_pcm_writei (snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
' Write interleaved frames to a PCM.
Private Extern snd_pcm_writei(pcm As Pointer, buffer As Pointer, uframes As Long) As Integer
 
' int snd_pcm_drain (snd_pcm_t *pcm)
' Stop a PCM preserving pending frames.
Private Extern snd_pcm_drain(pcm As Pointer) As Integer

' int snd_pcm_close (snd_pcm_t *pcm)
' Close PCM handle.
Private Extern snd_pcm_close(pcm As Pointer) As Integer


Public Sub Main()

 Dim fileWAV, s As String
 Dim fl As File
 Dim handle As Pointer
 Dim buff As Byte[]
 Dim err, rBit, frequenza, obr As Integer
 Dim stato, dim_dati, frames As Integer
 Dim quant As Integer = 1024
 Dim tipo, canali, bits, dati, c As Short

' Carica un file audio Wav:
 fileWAV = "/percorso/del/file.wav"
 fl = Open fileWAV For Read 
   
' Inizia i controlli di verifica del file caricato:
 Seek #fl, 12
 Read #fl, s, 3
 If s <> "fmt" Then Error.Raise("File errato !")
  
' Legge il tipo di formato (legge 2 byte):
 Seek #fl, 20
 Read #fl, tipo
 If tipo <> 1 Then Error.Raise("Non è un file PCM !")
  
' Rileva il numero di canali (legge 2 byte):
 Read #fl, canali
 
' Rileva la frequenza di campionamento (legge 4 byte):
 Read #fl, frequenza
  
' Rileva la risoluzione del campionamento (legge 2 byte):
 Seek #fl, 34
 Read #fl, bits
   
' Verifica la risoluzione in bit dei campioni audio del file Wav:
 Select Case bits
   Case 8
     rBit = SND_PCM_FORMAT_U8            
   Case 16
     rBit = SND_PCM_FORMAT_S16_LE
   Case Else
     Error.Raise("Risoluzione " & bits & " non prevista dal presente applicativo !")
 End Select
  
 Print "File audio:      "; fileWAV
 Print "Numero canali:   "; canali
 Print "Frequenza:       "; frequenza; " hertz"
 Print "Risoluzione:     "; bits; " bit"
 obr = frequenza * bits * canali
 Print "Overall BitRate: "; obr; " bps"
 
' I successivi byte rappresentano il blocco dei dati audio grezzi del file:
 Seek #fl, 0
 Read #fl, s, 256
 dati = InStr(s, "data")
 dim_dati = Lof(fl) - (dati + 7)
 Print "Dati audio:      "; dim_dati; " byte"

' Avendo queste informazioni generali, posiamo ricavare la durata in secondi del brano audio:
 Print "Durata:          "; CStr(Time(0, 0, 0, ((dim_dati * 8) / obr) * 1000))
 Print
 
' Legge mediante "Seek" soltanto i dati audio grezzi da inviare successivamente all'interfaccia PCM di Alsa:
 buff = New Byte[quant]
 Seek #fl, dati + 7

' Finalmente vengono utilizzate le funzioni esterne essenziali di Alsa:
 err = snd_pcm_open(VarPtr(handle), "default", SND_PCM_STREAM_PLAYBACK, 0)
 If err < 0 Then Error.Raise("Errore nell'apertura del subsistema PCM: " & snd_strerror(err))
 
 err = snd_pcm_set_params(handle, rBit, SND_PCM_ACCESS_RW_INTERLEAVED, canali, frequenza, 1, 500000)
 If err < 0 Then Error.Raise("Errore nell'impostazione dei parametri audio: " & snd_strerror(err))
  
 c = bits / (8 / canali)
 
' Scrive i dati nell'interfaccia PCM.
 While stato < dim_dati
  
   buff.Read(fl, 0, quant)
    
   frames = snd_pcm_writei(handle, buff.Data, quant / c)
   If frames < 0 Then Break
    
' Mostra in console e in rosso il tempo trascorso:
   Write "\r\e[0mTempo trascorso: \e[31m" & Time(0, 0, 0, ((stato * 8) / obr) * 1000)
         
   stato += quant 
   If dim_dati - stato < 1024 Then quant = dim_dati - stato
    
 Wend
 
 
' Va in chiusura:
 fl.Close
  
 snd_pcm_drain(handle)
  
 Print "\nEsecuzione terminata !"
  
' Alla fine dell'esecuzione del file audio chiude il subsistema PCM ed il file:
 err = snd_pcm_close(handle)
 If err = 0 Then Print "\nChiusura dell'interfaccia PCM: regolare."
  
End


Note

[1] il valore 77 nel file errno.h corrisponde all'identificatore: EBADFD /* File descriptor in bad state */.

[2] Riguardo all'istruzione escape "\e[7;0f" vedere la seguente pagina: Spostare da codice il cursore all'interno del Terminale.