Differenze tra le versioni di "Apertura del subsistema PCM in modalità Registrazione"

Da Gambas-it.org - Wikipedia.
 
(22 versioni intermedie di uno stesso utente non sono mostrate)
Riga 6: Riga 6:
  
 
Si dichiarerà all'inizio la libreria specifica di ALSA:
 
Si dichiarerà all'inizio la libreria specifica di ALSA:
  Library "libasound:2"
+
  Library "libasound:2.0.0"
 
+
Bisognerà dichiarare e instanziare alcuni valori e tipi di dati come:
Bisognerà dichiarare ed instanziare alcuni valori e tipi di dati come i seguenti:
+
* l'identificativo del dispositivo audio del sistema ALSA, che sarà la seguente stringa:
Private handle As Pointer
+
  "''default''"
  Private device As String = "default"
+
* il valore che identifica la capacità del dispositivo di essere aperto per la intercettazione dei dati audio. Useremo in tal caso una Costante:
 
  Private Const SND_PCM_STREAM_CAPTURE As Byte = 1
 
  Private Const SND_PCM_STREAM_CAPTURE As Byte = 1
 
ai quali vanno aggiunti il tipo di formato di campionamento ed il tipo di accesso.
 
ai quali vanno aggiunti il tipo di formato di campionamento ed il tipo di accesso.
  
Si dichiarerà con ''Extern'' la funzione specifica per l'apertura del sub-sistema PCM di ALSA:
+
La funzione specifica per l'apertura del sub-sistema PCM di ALSA:
  Private Extern snd_pcm_open(handleP As Pointer, nome As String, flusso As Integer, mode As Integer) As Integer
+
int snd_pcm_open(snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode)
 +
dovrà essere dichiata con ''Extern'':  
 +
  Private Extern snd_pcm_open(pcm As Pointer, name As String, _stream As Integer, mode As Integer) As Integer
 
e potremo, quindi, in subroutine richiamare detta funzione per l'uso:
 
e potremo, quindi, in subroutine richiamare detta funzione per l'uso:
 
  err = snd_pcm_open(VarPtr(handle), device, SND_PCM_STREAM_CAPTURE, 0)
 
  err = snd_pcm_open(VarPtr(handle), device, SND_PCM_STREAM_CAPTURE, 0)
Riga 25: Riga 27:
  
 
==Funzione per la registrazione dei dati==
 
==Funzione per la registrazione dei dati==
Useremo la funzione ''snd_pcm_readi'' per un accesso di tipo "''interleaved''". Tale funzione legge dall'<I>handle</i> del dispositivo PCM di ALSA un numero di dati, indicato dal suo terzo argomento, e li scrive nella variabile ''buffer'' che rappresenta il suo secondo argomento.
+
Useremo la funzione ''snd_pcm_readi'' per un accesso di tipo "''interleaved''" <SUP>&#091;[[#Note|Nota 1]]&#093;</sup>. Tale funzione legge dall'<I>handle</i> del dispositivo PCM di ALSA un numero di dati, indicato dal suo terzo argomento, e li scrive nella variabile ''buffer'' che rappresenta il suo secondo argomento.
 
<BR>Ovviamente tale funzione andrà preventivamente dichiarata con la funzione ''Extern'', e potrà essere così espressa in routine:
 
<BR>Ovviamente tale funzione andrà preventivamente dichiarata con la funzione ''Extern'', e potrà essere così espressa in routine:
 
  snd_pcm_readi(handle, buffer, numframes)
 
  snd_pcm_readi(handle, buffer, numframes)
Riga 33: Riga 35:
  
 
===Esempi pratici di registrazione audio===
 
===Esempi pratici di registrazione audio===
Vediamo di seguito un esempio completo per la registrazione di 13 secondi di dati audio con frequenza di campionamento a 44100 hertz, risoluzione a 16 bit e 2 canali:
+
Vediamo di seguito un esempio completo per la registrazione di <U>13 secondi</u> di dati audio con frequenza di campionamento a 44100 hertz, risoluzione a 16 bit e 2 canali:
  Library "libasound:2"
+
Private Const DEVICE As String = "default"
 +
Private Const CANALI As Integer = 2
 +
Private Const FREQUENZA As Integer = 44100
 +
 +
 +
  Library "libasound:2.0.0"
 
   
 
   
 
  Private Const SND_PCM_STREAM_CAPTURE As Byte = 1
 
  Private Const SND_PCM_STREAM_CAPTURE As Byte = 1
Riga 86: Riga 93:
 
  <FONT color=gray>' ''snd_pcm_sframes_t snd_pcm_readi (snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)''
 
  <FONT color=gray>' ''snd_pcm_sframes_t snd_pcm_readi (snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)''
 
  ' ''Read interleaved frames from a PCM.''</font>
 
  ' ''Read interleaved frames from a PCM.''</font>
  Private Extern snd_pcm_readi(pcm As Pointer, buffS As Short[], sInt As Integer) As Long
+
  Private Extern snd_pcm_readi(pcm As Pointer, buffer As Pointer, size As Long) As Long
 
   
 
   
 
  <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_recover (snd_pcm_t *pcm, int err, int silent)''
 +
' ''Recover the stream state from an error or suspend.''</font>
 +
Private Extern snd_pcm_recover(pcm As Pointer, err As Integer, silent As Integer) As Integer
 
      
 
      
 
  <FONT color=gray>' ''snd_pcm_close(snd_pcm_t *pcm)''
 
  <FONT color=gray>' ''snd_pcm_close(snd_pcm_t *pcm)''
Riga 97: Riga 108:
 
   
 
   
 
   
 
   
  '''Public''' Sub Main()
+
  Public Sub Main()
 
   
 
   
   Dim handle As Pointer
+
   Dim handle, params As Pointer
 
   Dim percorsoTmp As String
 
   Dim percorsoTmp As String
  Dim device As String = "default"
 
 
   Dim err, fc, dr, cicli As Integer
 
   Dim err, fc, dr, cicli As Integer
   Dim params As Pointer
+
   Dim <FONT Color=darkorange>buffer</font> As New Short[128]
  Dim buffer As New Short[128]
+
   Dim dim_buffer, frames As Long
   Dim frames, b As Byte
 
 
   Dim datigrezzi As File
 
   Dim datigrezzi As File
 
+
 
 
 
  <FONT color=gray>' ''Creiamo il file che ospiterà i dati grezzi audio registrati dall'applicativo:''</font>
 
  <FONT color=gray>' ''Creiamo il file che ospiterà i dati grezzi audio registrati dall'applicativo:''</font>
  percorsoTmp = Temp("filegrezzo")
+
  percorsoTmp = Temp("filegrezzo")
  datigrezzi = Open percorsoTmp For Create
+
  File.Save(percorsoTmp, Null)
  datigrezzi.Close
+
    
+
   datigrezzi = Open percorsoTmp For Write Append
  datigrezzi = Open percorsoTmp For Write Append
+
 
 
 
  <FONT color=gray>'''''''''''''''''''''''''''''''''''''''''''''</font>
 
  <FONT color=gray>'''''''''''''''''''''''''''''''''''''''''''''</font>
 
   
 
   
 
  <FONT color=gray>' ''Apre il sub-sistema PCM di ALSA per la registrazione:''</font>
 
  <FONT color=gray>' ''Apre il sub-sistema PCM di ALSA per la registrazione:''</font>
  err = snd_pcm_open(VarPtr(handle), device, SND_PCM_STREAM_CAPTURE, 0)
+
  err = snd_pcm_open(VarPtr(handle), DEVICE, SND_PCM_STREAM_CAPTURE, 0)
  If err < 0 Then Error.Raise("Errore nell'apertura del sub-sistema PCM: " & snd_strerror(err))
+
  If err < 0 Then Error.Raise("Errore nell'apertura del sub-sistema PCM: " & snd_strerror(err))
   
+
 
  <FONT color=gray>' ''Alloca un oggetto di parametri hardware:''</font>
 
  <FONT color=gray>' ''Alloca un oggetto di parametri hardware:''</font>
    snd_pcm_hw_params_malloc(VarPtr(params))
+
  snd_pcm_hw_params_malloc(VarPtr(params))
   
+
 
  <FONT color=gray>' ''Imposta valori predefiniti:''</font>
 
  <FONT color=gray>' ''Imposta valori predefiniti:''</font>
  snd_pcm_hw_params_any(handle, params)
+
  snd_pcm_hw_params_any(handle, params)
   
+
   
 
 
  <FONT color=#2F4F4F>' ''== Imposta i parametri prescelti ==''</font>
 
  <FONT color=#2F4F4F>' ''== Imposta i parametri prescelti ==''</font>
 
   
 
   
 
  <FONT color=gray>' ''Imposta la modalità "Interleaved":''</font>
 
  <FONT color=gray>' ''Imposta la modalità "Interleaved":''</font>
  snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED)
+
  snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED)
   
+
 
  <FONT color=gray>' ''Imposta il formato di campionamento a 16-bit little-endian:''</font>
 
  <FONT color=gray>' ''Imposta il formato di campionamento a 16-bit little-endian:''</font>
  snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE)
+
  snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE)
 
+
 
  <FONT color=gray>' ''Imposta il numero di canali a 2 (stereo):''</font>
 
  <FONT color=gray>' ''Imposta il numero di canali a 2 (stereo):''</font>
  snd_pcm_hw_params_set_channels(handle, params, 2)
+
  snd_pcm_hw_params_set_channels(handle, params, CANALI)
 
+
 
  <FONT color=gray>' ''Imposta la frequenza di campionamento a 44100 hertz:''</font>
 
  <FONT color=gray>' ''Imposta la frequenza di campionamento a 44100 hertz:''</font>
  fc = 44100
+
  fc = FREQUENZA
  snd_pcm_hw_params_set_rate_near(handle, params, VarPtr(fc), VarPtr(dr))
+
  snd_pcm_hw_params_set_rate_near(handle, params, VarPtr(fc), VarPtr(dr))
 
+
 
  <FONT color=gray>' ''Imposta la dimensione del periodo a 32 frame:''</font>
 
  <FONT color=gray>' ''Imposta la dimensione del periodo a 32 frame:''</font>
  frames = 32
+
  frames = 32
  snd_pcm_hw_params_set_period_size_near(handle, params, VarPtr(frames), VarPtr(dr))
+
  snd_pcm_hw_params_set_period_size_near(handle, params, VarPtr(frames), VarPtr(dr))
 
   
 
   
 
  <FONT color=gray>' ''Scrive i parametri nel dispositivo PCM:''</font>
 
  <FONT color=gray>' ''Scrive i parametri nel dispositivo PCM:''</font>
  err = snd_pcm_hw_params(handle, params)
+
  err = snd_pcm_hw_params(handle, params)
  If err < 0 Then Error.Raise("unable to set hw parameters: " & snd_strerror(err))
+
  If err < 0 Then Error.Raise("unable to set hw parameters: " & snd_strerror(err))
   
+
 
  <FONT color=gray>' ''Usa un buffer di dimensioni sufficenti per contenere un periodo:''</font>
 
  <FONT color=gray>' ''Usa un buffer di dimensioni sufficenti per contenere un periodo:''</font>
  snd_pcm_hw_params_get_period_size(params, VarPtr(frames), VarPtr(dr))
+
  snd_pcm_hw_params_get_period_size(params, VarPtr(frames), VarPtr(dr))
   
+
 
  <FONT color=gray>' ''Imposta il ciclo per tot secondi:''</font>
 
  <FONT color=gray>' ''Imposta il ciclo per tot secondi:''</font>
  snd_pcm_hw_params_get_period_time(params, VarPtr(frames), VarPtr(dr))
+
  snd_pcm_hw_params_get_period_time(params, VarPtr(frames), VarPtr(dr))
 
 
  cicli = 400000000 / fc
 
 
   
 
   
  While cicli > 0
+
  cicli = 400000000 / FREQUENZA
     
+
  dim_buffer = <FONT Color=darkorange>buffer</font>.Count / SizeOf(gb.Short)
    Dec cicli
+
 +
  Write "\r\e[5mAttendere......"
 +
  Flush
 +
 +
  While cicli > 0
 
  <FONT color=gray>' ''Registra i dati nel buffer:''</font>
 
  <FONT color=gray>' ''Registra i dati nel buffer:''</font>
    err = snd_pcm_readi(handle, buffer, 128 / SizeOf(gb.Short))
+
    frames = snd_pcm_readi(handle, <FONT Color=darkorange>buffer</font>.Data, dim_buffer)
    If err < 0 Then Error.Raise("Errore nella registrazione dei dati audio: " & snd_strerror(err))
+
    If frames < 0 Then
     
+
      frames = snd_pcm_recover(handle, frames, 0)
 +
      snd_pcm_close(handle)
 +
      Error.Raise("Errore nella registrazione dei dati audio: " & snd_strerror(frames))
 +
    Endif
 +
    If (frames > 0) And (frames < dim_buffer) Then
 +
      snd_pcm_close(handle)
 +
      Error.Raise("Lettura ridotta (atteso: " & CStr(dim_buffer) & ", letto: " & CStr(frames) & ")")
 +
    Endif
 
  <FONT color=gray>' ''Scrive il file contenente i dati grezzi wav:''</font>
 
  <FONT color=gray>' ''Scrive il file contenente i dati grezzi wav:''</font>
    Write #datigrezzi, buffer.data, buffer.Count * SizeOf(gb.Short)
+
    Write #datigrezzi, <FONT Color=darkorange>buffer</font>.Data, <FONT Color=darkorange>buffer</font>.Count * SizeOf(gb.Short)
     
+
    Dec cicli
  Wend
+
  Wend
 
   
 
   
 
  <FONT color=gray>' ''Genera il file WAV finale:''</font>
 
  <FONT color=gray>' ''Genera il file WAV finale:''</font>
  crea_file(percorsoTmp)
+
  crea_file(percorsoTmp)
 +
 +
  Write "\r\e[0mRegistrazione terminata !"
 
   
 
   
 
 
 
  <FONT color=gray>' ''Va in chiusura:''</font>
 
  <FONT color=gray>' ''Va in chiusura:''</font>
  datigrezzi.Close
+
  datigrezzi.Close
  snd_pcm_close(handle)
+
  snd_pcm_close(handle)
 
+
  '''End'''
+
  End
 +
 +
 +
Private Procedure crea_file(dg As String)
 
   
 
   
 +
  Dim st As Stream
 +
<FONT color=gray>' ''Si impostano i dati generali del blocco d'intestazione del futuro file WAV.
 +
' ''= Blocco d'intestazione del file wav futuro: 2 canali, 16 bit, hz 44100 =''</font>
 +
  Dim bh As Byte[] = [&52, &49, &46, &46, &00, &00, &00, &00, &57, &41, &56, &45, &66, &6D, &74, &20, &10, &00, &00, &00, &01, &00, &02, &00,
 +
                      &44, &AC, &00, &00, &10, &B1, &02, &00, &04, &00, &10, &00, &64, &61, &74, &61, &00, &00, &00, &00]
 +
 +
<FONT color=gray>' ''Imposta i dati di tipo "Integer" previsti al 5° e al 40° byte del blocco d'intestazione del file WAV:''</font>
 +
  st = Memory bh.Data For Write
 +
  Seek #st, 4
 +
  Write #st, Len(File.Load(dg)) + 36 As Integer
 +
  Seek #st, 40
 +
  Write #st, Len(File.Load(dg)) As Integer
 +
  st.Close
 
   
 
   
'''Private''' Procedure crea_file(tmp As String)
 
 
 
  Dim dati As String
 
  Dim i As Integer
 
 
 
<FONT color=gray>' ''Impostiamo il blocco iniziale generico del file WAV:''</font>
 
  Dim intesta1 As Byte[] = [82, 73, 70, 70]
 
  Dim intesta2 As Byte[] = [87, 65, 86, 69, 102, 109, 116, 32, 16, 0, 0, 0, 1, 0, 2, 0,
 
                          &44, &AC, 0, 0, &10, &B1, 2, 0, 4, 0, 16, 0, &64, &61, &74, &61]
 
 
 
  dati = File.Load(tmp)
 
  i = Len(dati) + 36
 
 
 
<FONT color=gray>' ''Imposta il valore dimensionale di 4 byte a partire dal 5° byte del futuro file:''</font>
 
  intesta1.Add(i And &FF)
 
  intesta1.Add(Shr(i And &FF00&, 8))
 
  intesta1.Add(Shr(i And &FF0000&, 16))
 
  intesta1.Add(Shr(i And &FF000000&, 24))
 
  intesta1.Insert(intesta2)
 
  i = Len(dati)
 
  intesta1.Add(i And &FF)
 
  intesta1.Add(Shr(i And &FF00&, 8))
 
  intesta1.Add(Shr(i And &FF0000&, 16))
 
  intesta1.Add(Shr(i And &FF000000&, 24))
 
 
 
 
  <FONT color=gray>' ''Per generare il file WAV finale, eseguibile, uniamo i dati del blocco iniziale ai dati grezzi registrati:''</font>
 
  <FONT color=gray>' ''Per generare il file WAV finale, eseguibile, uniamo i dati del blocco iniziale ai dati grezzi registrati:''</font>
  File.Save("/tmp/FileFINALE.wav", intesta1.ToString(0, intesta1.Count) & dati)
+
  File.Save("/tmp/FileFINALE.wav", bh.ToString(0, bh.Count) & File.Load(dg))
 
+
  '''End'''
+
  End
 
 
 
 
 
Un altro esempio più breve e semplice, come il precedente con frequenza di campionamento a 44100 hertz, risoluzione a 16 bit e 2 canali:
 
Un altro esempio più breve e semplice, come il precedente con frequenza di campionamento a 44100 hertz, risoluzione a 16 bit e 2 canali:
  Library "libasound:2"
+
Private Const DEVICE As String = "default"
 +
Private Const CANALI As Integer = 2
 +
Private Const FREQUENZA As Integer = 44100
 +
 +
 +
  Library "libasound:2.0.0"
 
   
 
   
 
  Private Const SND_PCM_STREAM_CAPTURE As Byte = 1
 
  Private Const SND_PCM_STREAM_CAPTURE As Byte = 1
Riga 231: Riga 241:
 
  <FONT color=gray>' ''snd_pcm_sframes_t snd_pcm_readi (snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)''
 
  <FONT color=gray>' ''snd_pcm_sframes_t snd_pcm_readi (snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)''
 
  ' ''Read interleaved frames from a PCM.''</font>
 
  ' ''Read interleaved frames from a PCM.''</font>
  Private Extern snd_pcm_readi(pcm As Pointer, buffer As Pointer, size As Integer) As Long
+
  Private Extern snd_pcm_readi(pcm As Pointer, buffer As Pointer, size As Long) As Long
 
   
 
   
 
  <FONT color=gray>' ''const char * snd_strerror (int errnum)''
 
  <FONT color=gray>' ''const char * snd_strerror (int errnum)''
Riga 246: Riga 256:
 
   
 
   
 
   
 
   
  '''Public''' Sub Main()
+
  Public Sub Main()
 
   
 
   
 
   Dim handle As Pointer
 
   Dim handle As Pointer
 
   Dim percorsoTmp As String
 
   Dim percorsoTmp As String
  Dim device As String = "default"
+
   Dim err, cicli As Integer
   Dim err, fc, cicli As Integer
+
   Dim <FONT Color=darkorange>buffer</font> As New Short[128]
   Dim buffer As New Short[128]
 
 
   Dim dim_buffer, frames As Long
 
   Dim dim_buffer, frames As Long
 
   Dim datigrezzi As File
 
   Dim datigrezzi As File
 
+
 
 
 
  <FONT color=gray>' ''Creiamo il file che ospiterà i dati grezzi audio registrati dall'applicativo:''</font>
 
  <FONT color=gray>' ''Creiamo il file che ospiterà i dati grezzi audio registrati dall'applicativo:''</font>
  percorsoTmp = Temp("filegrezzo")
+
  percorsoTmp = Temp("filegrezzo")
  datigrezzi = Open percorsoTmp For Create
+
  File.Save(percorsoTmp, Null)
  datigrezzi.Close
 
 
 
  datigrezzi = Open percorsoTmp For Write Append
 
 
   
 
   
 +
  datigrezzi = Open percorsoTmp For Write Append
 
   
 
   
<FONT color=gray>' ''Imposta la frequenza di campionamento a 44100 hertz:''</font>
 
  fc = 44100
 
 
 
 
  <FONT color=gray>' ''Apre il sub-sistema PCM di ALSA per la registrazione:''</font>
 
  <FONT color=gray>' ''Apre il sub-sistema PCM di ALSA per la registrazione:''</font>
  err = snd_pcm_open(VarPtr(handle), device, SND_PCM_STREAM_CAPTURE, 0)
+
  err = snd_pcm_open(VarPtr(handle), device, SND_PCM_STREAM_CAPTURE, 0)
  If err < 0 Then Error.Raise("Errore nell'apertura del sub-sistema PCM: " & snd_strerror(err))
+
  If err < 0 Then Error.Raise("Errore nell'apertura del sub-sistema PCM: " & snd_strerror(err))
 
   
 
   
 
  <FONT color=gray>' ''Imposta i parametri del sub-sistema PCM di ALSA per la registrazione:''</font>
 
  <FONT color=gray>' ''Imposta i parametri del sub-sistema PCM di ALSA per la registrazione:''</font>
  err = snd_pcm_set_params(handle, SND_PCM_FORMAT_S16_LE, SND_PCM_ACCESS_RW_INTERLEAVED, 2, fc, 1, 0)
+
  err = snd_pcm_set_params(handle, SND_PCM_FORMAT_S16_LE, SND_PCM_ACCESS_RW_INTERLEAVED, CANALI, FREQUENZA, 1, 0)
  If err < 0 Then Error.Raise("Errore nell'impostazione dei parametri del sub-sistema PCM: " & snd_strerror(err))
+
  If err < 0 Then Error.Raise("Errore nell'impostazione dei parametri del sub-sistema PCM: " & snd_strerror(err))
 
   
 
   
  cicli = 400000000 / fc
+
  cicli = 400000000 / FREQUENZA
  dim_buffer = 128 / SizeOf(gb.Short)
+
  dim_buffer = <FONT Color=darkorange>buffer</font>.Count / SizeOf(gb.Short)
 
   
 
   
  While cicli > 0
+
  Write "\r\e[5mAttendere......"
     
+
  Flush
    Dec cicli
+
 +
  While cicli > 0
 
  <FONT color=gray>' ''Registra i dati nel buffer:''</font>
 
  <FONT color=gray>' ''Registra i dati nel buffer:''</font>
    frames = snd_pcm_readi(handle, buffer.Data, dim_buffer)
+
    frames = snd_pcm_readi(handle, <FONT Color=darkorange>buffer</font>.Data, dim_buffer)
    If frames < 0 Then
+
    If frames < 0 Then
      frames = snd_pcm_recover(handle, frames, 0)
+
      frames = snd_pcm_recover(handle, frames, 0)
      snd_pcm_close(handle)
+
      snd_pcm_close(handle)
      Error.Raise("Errore nella registrazione dei dati audio: " & snd_strerror(frames))
+
      Error.Raise("Errore nella registrazione dei dati audio: " & snd_strerror(frames))
    Endif
+
    Endif
    If (frames > 0) And (frames < dim_buffer) Then
+
    If (frames > 0) And (frames < dim_buffer) Then
      snd_pcm_close(handle)
+
      snd_pcm_close(handle)
      Error.Raise("Lettura ridotta (atteso: " & CStr(dim_buffer) & ", letto: " & CStr(frames) & ")")
+
      Error.Raise("Lettura ridotta (atteso: " & CStr(dim_buffer) & ", letto: " & CStr(frames) & ")")
    Endif
+
    Endif
     
 
 
  <FONT color=gray>' ''Scrive il file contenente i dati grezzi wav:''</font>
 
  <FONT color=gray>' ''Scrive il file contenente i dati grezzi wav:''</font>
    Write #datigrezzi, buffer.data, buffer.Count * SizeOf(gb.Short)
+
    Write #datigrezzi, <FONT Color=darkorange>buffer</font>.Data, <FONT Color=darkorange>buffer</font>.Count * SizeOf(gb.Short)
     
+
    Dec cicli
  Wend
+
  Wend
 
+
 
   <FONT color=gray>' ''Genera il file WAV finale:''</font>
 
   <FONT color=gray>' ''Genera il file WAV finale:''</font>
  crea_file(percorsoTmp)
+
  crea_file(percorsoTmp)
 
+
    
+
   Write "\r\e[0mRegistrazione terminata !"
 +
 
  <FONT color=gray>' ''Va in chiusura:''</font>
 
  <FONT color=gray>' ''Va in chiusura:''</font>
  datigrezzi.Close
+
  datigrezzi.Close
  snd_pcm_close(handle)
+
  snd_pcm_close(handle)
 
 
'''End'''
 
 
   
 
   
 +
End
 +
 +
 +
Private Procedure crea_file(dg As String)
 +
 +
  Dim st As Stream
 +
<FONT color=gray>' ''Si impostano i dati generali del blocco d'intestazione del futuro file WAV.
 +
' ''= Blocco d'intestazione del file wav futuro: 2 canali, 16 bit, hz 44100 =''</font>
 +
  Dim bh As Byte[] = [&52, &49, &46, &46, &00, &00, &00, &00, &57, &41, &56, &45, &66, &6D, &74, &20, &10, &00, &00, &00, &01, &00, &02, &00,
 +
                      &44, &AC, &00, &00, &10, &B1, &02, &00, &04, &00, &10, &00, &64, &61, &74, &61, &00, &00, &00, &00]
 +
 +
<FONT color=gray>' ''Imposta i dati di tipo "Integer" previsti al 5° e al 40° byte del blocco d'intestazione del file WAV:''</font>
 +
  st = Memory bh.Data For Write
 +
  Seek #st, 4
 +
  Write #st, Len(File.Load(dg)) + 36 As Integer
 +
  Seek #st, 40
 +
  Write #st, Len(File.Load(dg)) As Integer
 +
  st.Close
 
   
 
   
'''Private''' Procedure crea_file(tmp As String)
 
 
 
  Dim dati As String
 
  Dim i As Integer
 
 
 
<FONT color=gray>' ''Impostiamo il blocco iniziale generico del file WAV:''</font>
 
  Dim intesta1 As Byte[] = [82, 73, 70, 70]
 
  Dim intesta2 As Byte[] = [87, 65, 86, 69, 102, 109, 116, 32, 16, 0, 0, 0, 1, 0, 2, 0,
 
                          &44, &AC, 0, 0, &10, &B1, 2, 0, 4, 0, 16, 0, &64, &61, &74, &61]
 
 
 
  dati = File.Load(tmp)
 
  i = Len(dati) + 36
 
 
 
<FONT color=gray>' ''Imposta il valore dimensionale di 4 byte a partire dal 5° byte del futuro file:''</font>
 
  intesta1.Add(i And &FF)
 
  intesta1.Add(Shr(i And &FF00&, 8))
 
  intesta1.Add(Shr(i And &FF0000&, 16))
 
  intesta1.Add(Shr(i And &FF000000&, 24))
 
  intesta1.Insert(intesta2)
 
  i = Len(dati)
 
  intesta1.Add(i And &FF)
 
  intesta1.Add(Shr(i And &FF00&, 8))
 
  intesta1.Add(Shr(i And &FF0000&, 16))
 
  intesta1.Add(Shr(i And &FF000000&, 24))
 
 
 
 
  <FONT color=gray>' ''Per generare il file WAV finale, eseguibile, uniamo i dati del blocco iniziale ai dati grezzi registrati:''</font>
 
  <FONT color=gray>' ''Per generare il file WAV finale, eseguibile, uniamo i dati del blocco iniziale ai dati grezzi registrati:''</font>
  File.Save("/tmp/FileFINALE.wav", intesta1.ToString(0, intesta1.Count) & dati)
+
  File.Save("/tmp/FileFINALE.wav", bh.ToString(0, bh.Count) & File.Load(dg))
 
+
  '''End'''
+
  End
 
 
 
 
 
 
 
In quest'altro esempio si avrà la possibilità, premendo su un ''Button'' posto sul ''Form'', di avviare la registrazione, e premendo su un altro ''Button'' di interromperla. Successivamente ripremendo sul primo ''Button'' di avviare una nuova registrazione e così via:
 
In quest'altro esempio si avrà la possibilità, premendo su un ''Button'' posto sul ''Form'', di avviare la registrazione, e premendo su un altro ''Button'' di interromperla. Successivamente ripremendo sul primo ''Button'' di avviare una nuova registrazione e così via:
 +
Private Const DEVICE As String = "default"
 +
Private Const CANALI As Integer = 2
 +
Private Const FREQUENZA As Integer = 44100
 
  Private cicli As Boolean
 
  Private cicli As Boolean
 
   
 
   
 
   
 
   
  Library "libasound:2"
+
  Library "libasound:2.0.0"
 
   
 
   
 
  Private Const SND_PCM_STREAM_CAPTURE As Byte = 1
 
  Private Const SND_PCM_STREAM_CAPTURE As Byte = 1
Riga 362: Riga 357:
 
  <FONT color=gray>' ''snd_pcm_sframes_t snd_pcm_readi (snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)''
 
  <FONT color=gray>' ''snd_pcm_sframes_t snd_pcm_readi (snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)''
 
  ' ''Read interleaved frames from a PCM.''</font>
 
  ' ''Read interleaved frames from a PCM.''</font>
  Private Extern snd_pcm_readi(pcm As Pointer, buffS As Short[], sInt As Integer) As Long
+
  Private Extern snd_pcm_readi(pcm As Pointer, buffer As Pointer, size As Long) As Long
 
   
 
   
 
  <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_recover (snd_pcm_t *pcm, int err, int silent)''
 +
' ''Recover the stream state from an error or suspend.''</font>
 +
Private Extern snd_pcm_recover(pcm As Pointer, err As Integer, silent As Integer) As Integer
 +
 
  <FONT color=gray>' ''snd_pcm_close(snd_pcm_t *pcm)''
 
  <FONT color=gray>' ''snd_pcm_close(snd_pcm_t *pcm)''
 
  ' ''Close PCM handle.''</font>
 
  ' ''Close PCM handle.''</font>
Riga 373: Riga 372:
 
   
 
   
 
   
 
   
  '''Public''' Sub Button1_Click()
+
  Public Sub Button1_Click()
 
   
 
   
 
   Dim handle As Pointer
 
   Dim handle As Pointer
 
   Dim percorsoTmp As String
 
   Dim percorsoTmp As String
   Dim device As String = "default"
+
   Dim err As Integer
   Dim err, fc As Integer
+
  Dim <FONT Color=darkorange>buffer</font> As New Short[128]
 +
   Dim dim_buffer, frames As Long
 
   Dim datigrezzi As File
 
   Dim datigrezzi As File
  Dim canali, b As Byte
 
  Dim buffer As New Short[128]
 
 
 
 
   
 
   
 
  <FONT color=gray>' ''Creiamo il file che ospiterà i dati grezzi audio registrati dall'applicativo:''</font>
 
  <FONT color=gray>' ''Creiamo il file che ospiterà i dati grezzi audio registrati dall'applicativo:''</font>
  percorsoTmp = Temp("filegrezzo")
+
  percorsoTmp = Temp("filegrezzo")
  datigrezzi = Open percorsoTmp For Create
+
  datigrezzi = Open percorsoTmp For Create
 
 
 
<FONT color=gray>' ''Imposta la frequenza di campionamento a 44100 hertz:''</font>
 
  fc = 44100
 
 
<FONT color=gray>' ''Imposta il numero di canali:''</font>
 
  canali = 2
 
 
   
 
   
 
  <FONT color=gray>' ''Apre il sub-sistema PCM di ALSA per la registrazione:''</font>
 
  <FONT color=gray>' ''Apre il sub-sistema PCM di ALSA per la registrazione:''</font>
  err = snd_pcm_open(VarPtr(handle), device, SND_PCM_STREAM_CAPTURE, 0)
+
  err = snd_pcm_open(VarPtr(handle), device, SND_PCM_STREAM_CAPTURE, 0)
  If err < 0 Then Error.Raise("Errore nell'apertura del sub-sistema PCM: " & snd_strerror(err))
+
  If err < 0 Then Error.Raise("Errore nell'apertura del sub-sistema PCM: " & snd_strerror(err))
 
   
 
   
 
  <FONT color=gray>' ''Imposta i parametri del sub-sistema PCM di ALSA per la registrazione:''</font>
 
  <FONT color=gray>' ''Imposta i parametri del sub-sistema PCM di ALSA per la registrazione:''</font>
  err = snd_pcm_set_params(handle, SND_PCM_FORMAT_S16_LE, SND_PCM_ACCESS_RW_INTERLEAVED, canali, fc, 1, 0)
+
  err = snd_pcm_set_params(handle, SND_PCM_FORMAT_S16_LE, SND_PCM_ACCESS_RW_INTERLEAVED, CANALI, FREQUENZA, 1, 0)
  If err < 0 Then Error.Raise("Errore nell'impostazione dei parametri del sub-sistema PCM: " & snd_strerror(err))
+
  If err < 0 Then Error.Raise("Errore nell'impostazione dei parametri del sub-sistema PCM: " & snd_strerror(err))
 
   
 
   
  cicli = True
+
  cicli = True
 +
  dim_buffer = <FONT Color=darkorange>buffer</font>.Count / SizeOf(gb.Short)
 
   
 
   
  While cicli  <FONT color=gray>' ''Avvia un ciclo infinito per la registrazione''</font>
+
  While cicli  <FONT color=gray>' ''Avvia un ciclo infinito per la registrazione''</font>
     
 
 
  <FONT color=gray>' ''Registra i dati nel buffer:''</font>
 
  <FONT color=gray>' ''Registra i dati nel buffer:''</font>
    err = snd_pcm_readi(handle, buffer, 128 / SizeOf(gb.Short))
+
    frames = snd_pcm_readi(handle, <FONT Color=darkorange>buffer</font>.Data, dim_buffer)
    If err < 0 Then Error.Raise("Errore nella registrazione dei dati audio: " & snd_strerror(err))
+
    If frames < 0 Then
 +
      frames = snd_pcm_recover(handle, frames, 0)
 +
      snd_pcm_close(handle)
 +
      Error.Raise("Errore nella registrazione dei dati audio: " & snd_strerror(frames))
 +
    Endif
 +
    If (frames > 0) And (frames < dim_buffer) Then
 +
      snd_pcm_close(handle)
 +
      Error.Raise("Lettura ridotta (atteso: " & CStr(dim_buffer) & ", letto: " & CStr(frames) & ")")
 +
    Endif
 +
<FONT color=gray>' ''Scrive il file contenente i dati grezzi wav:''</font>
 +
    Write #datigrezzi, <FONT Color=darkorange>buffer</font>.Data, <FONT Color=darkorange>buffer</font>.Count * SizeOf(gb.Short)
 +
<FONT color=gray>' ''Una brevissima pausa consente all'utente di operare su eventuali oggetti posti sul Form:''</font>
 +
    Wait 0.001
 +
  Wend
 
   
 
   
  <FONT color=gray>' ''Scrive il file contenente i dati grezzi wav:''</font>
 
    Write #datigrezzi, buffer.data, buffer.Count * SizeOf(gb.Short)
 
   
 
    Wait 0.001
 
   
 
  Wend
 
 
 
 
   <FONT color=gray>' ''Genera il file WAV finale:''</font>
 
   <FONT color=gray>' ''Genera il file WAV finale:''</font>
  crea_file(percorsoTmp)
+
  crea_file(percorsoTmp)
 
+
 
 
 
  <FONT color=gray>' ''Va in chiusura:''</font>
 
  <FONT color=gray>' ''Va in chiusura:''</font>
  datigrezzi.Close
+
  datigrezzi.Close
  snd_pcm_close(handle)
+
  snd_pcm_close(handle)
 
+
  Me.Close
  '''End'''
+
 +
  End
 +
 +
 +
Public Sub Button2_Click()
 
   
 
   
 +
  cicli = False
 
   
 
   
  '''Private''' Procedure crea_file(tmp As String)
+
  End
 
+
   
  Dim dati As String
+
   
  Dim i As Integer
+
  Private Procedure crea_file(dg As String)
 
 
  <FONT color=gray>' ''Impostiamo il blocco iniziale generico del file WAV:''</font>
 
  Dim intesta1 As Byte[] = [82, 73, 70, 70]
 
  Dim intesta2 As Byte[] = [87, 65, 86, 69, 102, 109, 116, 32, 16, 0, 0, 0, 1, 0, 2, 0,
 
                          &44, &AC, 0, 0, &10, &B1, 2, 0, 4, 0, 16, 0, &64, &61, &74, &61]
 
 
 
  dati = File.Load(tmp)
 
  i = Len(dati) + 36
 
 
 
  <FONT color=gray>' ''Imposta il valore dimensionale di 4 byte a partire dal 5° byte del futuro file:''</font>
 
  intesta1.Add(i And &FF)
 
  intesta1.Add(Shr(i And &FF00&, 8))
 
  intesta1.Add(Shr(i And &FF0000&, 16))
 
  intesta1.Add(Shr(i And &FF000000&, 24))
 
  intesta1.Insert(intesta2)
 
  i = Len(dati)
 
  intesta1.Add(i And &FF)
 
  intesta1.Add(Shr(i And &FF00&, 8))
 
  intesta1.Add(Shr(i And &FF0000&, 16))
 
  intesta1.Add(Shr(i And &FF000000&, 24))
 
 
 
  <FONT color=gray>' ''Per generare il file WAV finale, eseguibile, uniamo i dati del blocco iniziale ai dati grezzi registrati:''</font>
 
  File.Save("/tmp/FileFINALE.wav", intesta1.ToString(0, intesta1.Count) & dati)
 
 
 
'''End'''
 
 
   
 
   
 +
  Dim st As Stream
 +
<FONT color=gray>' ''Si impostano i dati generali del blocco d'intestazione del futuro file WAV.''
 +
' ''= Blocco d'intestazione del file wav futuro: 2 canali, 16 bit, hz 44100 =''</font>
 +
  Dim bh As Byte[] = [&52, &49, &46, &46, &00, &00, &00, &00, &57, &41, &56, &45, &66, &6D, &74, &20, &10, &00, &00, &00, &01, &00, &02, &00,
 +
                      &44, &AC, &00, &00, &10, &B1, &02, &00, &04, &00, &10, &00, &64, &61, &74, &61, &00, &00, &00, &00]
 
   
 
   
  '''Public''' Sub Button2_Click()
+
  <FONT color=gray>' ''Imposta i dati di tipo "Integer" previsti al 5° e al 40° byte del blocco d'intestazione del file WAV:''</font>
 +
  st = Memory bh.Data For Write
 +
  Seek #st, 4
 +
  Write #st, Len(File.Load(dg)) + 36 As Integer
 +
  Seek #st, 40
 +
  Write #st, Len(File.Load(dg)) As Integer
 +
  st.Close
 
   
 
   
  cicli = False
+
<FONT color=gray>' ''Per generare il file WAV finale, eseguibile, uniamo i dati del blocco iniziale ai dati grezzi registrati:''</font>
 +
File.Save("/tmp/FileFINALE.wav", bh.ToString(0, bh.Count) & File.Load(dg))
 
   
 
   
  '''End'''
+
  End
 +
 
 +
 
 +
 
 +
=Note=
 +
[1] La memorizzazione e la conseguente disposizione dei valori dei campioni in memoria è condizionata dalle opzioni: "''Interleaved''" e "''Non-interleaved''". Tale caratteristica viene specificamente e precisamente impostata nel 3° parametro della funzione esterna di ALSA "''snd_pcm_set_params( )''".
 +
<BR>In breve possiamo dire che l'impostazione di quel parametro, e quindi della modalità di registrazione audio, con la costante ''SND_PCM_ACCESS_RW_INTERLEAVED'' determina che nel caso di una registrazione "stereo" i dati afferenti ai campioni audio dei due canali saranno memorizzati in memoria in modo alternato uno dopo l'altro , come nel semplice esempio che segue:
 +
| <FONT Color=maroon>16-bit 1° canale</font> | <FONT Color=green>16-bit 2° canale</font> | <FONT Color=maroon>16-bit 1° canale</font> | <FONT Color=green>16-bit 2° canale</font> | e così via......
 +
<BR>Scegliendo invece la costante ''SND_PCM_ACCESS_RW_NONINTERLEAVED'', i dati dei campioni audio saranno registrati in blocchi distinti per canali; ossia ad esempio così:
 +
| <FONT Color=maroon>16-bit 1° canale</font> | <FONT Color=maroon>16-bit 1° canale</font> | <FONT Color=maroon>16-bit 1° canale</font> | ...... | <FONT Color=green>16-bit 2° canale</font> | <FONT Color=green>16-bit 2° canale</font> | <FONT Color=green>16-bit 2° canale</font> | .....
 +
 
 +
Vedere al riguardo anche:
 +
* https://www.alsa-project.org/main/index.php/PCM_Ring_Buffer
 +
* http://alsa.opensrc.org/HowTo_Asynchronous_Playback
 +
* http://nairobi-embedded.org/alsa_terminology.html
 +
* http://www.alsa-project.org/alsa-doc/alsa-lib/pcm.html

Versione attuale delle 07:48, 25 ott 2024

Per aprire il sub-sistema PCM di ALSA in modalità "Registrazione" bisognerà dotarsi di un handle specifico e diverso da quello relativo alla modalità in Riproduzione.

Si utilizzerà come parametro stream, che rappresenta la direzione del flusso dei dati audio, della funzione snd_pcm_open il valore espresso dalla costante di ALSA: SND_PCM_STREAM_CAPTURE.

Pertanto l'apertura del dispositivo PCM di ALSA avverrà in modo simile a quella effettuata la Riproduzione audio.

Si dichiarerà all'inizio la libreria specifica di ALSA:

Library "libasound:2.0.0"

Bisognerà dichiarare e instanziare alcuni valori e tipi di dati come:

  • l'identificativo del dispositivo audio del sistema ALSA, che sarà la seguente stringa:
"default"
  • il valore che identifica la capacità del dispositivo di essere aperto per la intercettazione dei dati audio. Useremo in tal caso una Costante:
Private Const SND_PCM_STREAM_CAPTURE As Byte = 1

ai quali vanno aggiunti il tipo di formato di campionamento ed il tipo di accesso.

La funzione specifica per l'apertura del sub-sistema PCM di ALSA:

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

dovrà essere dichiata con Extern:

Private Extern snd_pcm_open(pcm As Pointer, name As String, _stream As Integer, mode As Integer) As Integer

e potremo, quindi, in subroutine richiamare detta funzione per l'uso:

err = snd_pcm_open(VarPtr(handle), device, SND_PCM_STREAM_CAPTURE, 0)


Impostazioni generali

Le altre impostazioni sono simili alle impostazioni viste per la modalità in Riproduzione.


Funzione per la registrazione dei dati

Useremo la funzione snd_pcm_readi per un accesso di tipo "interleaved" [Nota 1]. Tale funzione legge dall'handle del dispositivo PCM di ALSA un numero di dati, indicato dal suo terzo argomento, e li scrive nella variabile buffer che rappresenta il suo secondo argomento.
Ovviamente tale funzione andrà preventivamente dichiarata con la funzione Extern, e potrà essere così espressa in routine:

snd_pcm_readi(handle, buffer, numframes)

Anche in questo caso bisognerà far sì che l'applicazione chiami la funzione di registrazione prima che il buffer di acquisizione della scheda audio sia completamente riempito. Altrimenti ci sarà un sovraccarico del buffer, e verrà sollevato un corrispondente errore di overrun.


Esempi pratici di registrazione audio

Vediamo di seguito un esempio completo per la registrazione di 13 secondi di dati audio con frequenza di campionamento a 44100 hertz, risoluzione a 16 bit e 2 canali:

Private Const DEVICE As String = "default"
Private Const CANALI As Integer = 2
Private Const FREQUENZA As Integer = 44100


Library "libasound:2.0.0"

Private Const SND_PCM_STREAM_CAPTURE 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(handleP As Pointer, nome As String, flusso As Integer, mode As Integer) As Integer

' int snd_pcm_hw_params_malloc(snd_pcm_hw_params_t **ptr)
' Allocate an invalid snd_pcm_hw_params_t using standard malloc.
Private Extern snd_pcm_hw_params_malloc(ptr As Pointer) As Integer

' int snd_pcm_hw_params_any(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
' Fill params with a full configuration space for a PCM.
Private Extern snd_pcm_hw_params_any(pcmP As Pointer, ptrP As Pointer) As Integer

' int snd_pcm_hw_params_set_access(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t _access)
' Restrict a configuration space to contain only one access type.
Private Extern snd_pcm_hw_params_set_access(pcmP As Pointer, ptrP As Pointer, accesso As Integer) As Integer

' int snd_pcm_hw_params_set_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t val)
' Restrict a configuration space to contain only one format.
Private Extern snd_pcm_hw_params_set_format(pcmP As Pointer, ptrP As Pointer, valformat As Integer) As Integer

' int snd_pcm_hw_params_set_channels_first(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val)
' Restrict a configuration space to contain only one channels count.
Private Extern snd_pcm_hw_params_set_channels(pcmP As Pointer, ptrP As Pointer, valP As Pointer) As Integer

' int snd_pcm_hw_params_set_rate_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir)
' Restrict a configuration space to have rate nearest to a target.
Private Extern snd_pcm_hw_params_set_rate_near(pcmP As Pointer, ptrP As Pointer, valP As Pointer, dirP As Pointer) As Integer

' int snd_pcm_hw_params_set_period_size_near(snd_pcm_t * pcm, snd_pcm_hw_params_t * params, snd_pcm_uframes_t * val, int * dir)
' Restrict a configuration space to have period size nearest to a target.
Private Extern snd_pcm_hw_params_set_period_size_near(pcmP As Pointer, ptrP As Pointer, valP As Pointer, dirP As Pointer) As Integer

' int snd_pcm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
' Install one PCM hardware configuration chosen from a configuration space and snd_pcm_prepare it.
Private Extern snd_pcm_hw_params(pcmP As Pointer, ptrP As Pointer) As Integer

' int snd_pcm_hw_params_get_period_size(const snd_pcm_hw_params_t * params, snd_pcm_uframes_t * val, int * dir)
' Extract period size from a configuration space.
Private Extern snd_pcm_hw_params_get_period_size(ptrP As Pointer, valP As Pointer, dirP As Pointer) As Integer

' int snd_pcm_hw_params_get_period_time(const snd_pcm_hw_params_t * params, snd_pcm_uframes_t * val, int * dir)
' Extract period time from a configuration space.
Private Extern snd_pcm_hw_params_get_period_time(ptrP As Pointer, valP As Pointer, dirP As Pointer) As Integer

' snd_pcm_sframes_t snd_pcm_readi (snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)
' Read interleaved frames from a PCM.
Private Extern snd_pcm_readi(pcm As Pointer, buffer As Pointer, size As Long) As Long

' 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_recover (snd_pcm_t *pcm, int err, int silent)
' Recover the stream state from an error or suspend.
Private Extern snd_pcm_recover(pcm As Pointer, err As Integer, silent As Integer) As Integer
    
' snd_pcm_close(snd_pcm_t *pcm)
' Close PCM handle.
Private Extern snd_pcm_close(pcm As Pointer)


Public Sub Main()

 Dim handle, params As Pointer
 Dim percorsoTmp As String
 Dim err, fc, dr, cicli As Integer
 Dim buffer As New Short[128]
 Dim dim_buffer, frames As Long
 Dim datigrezzi As File

' Creiamo il file che ospiterà i dati grezzi audio registrati dall'applicativo:
 percorsoTmp = Temp("filegrezzo")
 File.Save(percorsoTmp, Null)

 datigrezzi = Open percorsoTmp For Write Append

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

' Apre il sub-sistema PCM di ALSA per la registrazione:
 err = snd_pcm_open(VarPtr(handle), DEVICE, SND_PCM_STREAM_CAPTURE, 0)
 If err < 0 Then Error.Raise("Errore nell'apertura del sub-sistema PCM: " & snd_strerror(err))

' Alloca un oggetto di parametri hardware:
 snd_pcm_hw_params_malloc(VarPtr(params))

' Imposta valori predefiniti:
 snd_pcm_hw_params_any(handle, params)

' == Imposta i parametri prescelti ==

' Imposta la modalità "Interleaved":
 snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED)

' Imposta il formato di campionamento a 16-bit little-endian:
 snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE)

' Imposta il numero di canali a 2 (stereo):
 snd_pcm_hw_params_set_channels(handle, params, CANALI)

' Imposta la frequenza di campionamento a 44100 hertz:
 fc = FREQUENZA
 snd_pcm_hw_params_set_rate_near(handle, params, VarPtr(fc), VarPtr(dr))

' Imposta la dimensione del periodo a 32 frame:
 frames = 32
 snd_pcm_hw_params_set_period_size_near(handle, params, VarPtr(frames), VarPtr(dr))

' Scrive i parametri nel dispositivo PCM:
 err = snd_pcm_hw_params(handle, params)
 If err < 0 Then Error.Raise("unable to set hw parameters: " & snd_strerror(err))

' Usa un buffer di dimensioni sufficenti per contenere un periodo:
 snd_pcm_hw_params_get_period_size(params, VarPtr(frames), VarPtr(dr))

' Imposta il ciclo per tot secondi:
 snd_pcm_hw_params_get_period_time(params, VarPtr(frames), VarPtr(dr))

 cicli = 400000000 / FREQUENZA
 dim_buffer = buffer.Count / SizeOf(gb.Short)

 Write "\r\e[5mAttendere......"
 Flush

 While cicli > 0
' Registra i dati nel buffer:
   frames = snd_pcm_readi(handle, buffer.Data, dim_buffer)
   If frames < 0 Then
     frames = snd_pcm_recover(handle, frames, 0)
     snd_pcm_close(handle)
     Error.Raise("Errore nella registrazione dei dati audio: " & snd_strerror(frames))
   Endif
   If (frames > 0) And (frames < dim_buffer) Then
     snd_pcm_close(handle)
     Error.Raise("Lettura ridotta (atteso: " & CStr(dim_buffer) & ", letto: " & CStr(frames) & ")")
   Endif
' Scrive il file contenente i dati grezzi wav:
   Write #datigrezzi, buffer.Data, buffer.Count * SizeOf(gb.Short)
   Dec cicli
 Wend

' Genera il file WAV finale:
 crea_file(percorsoTmp)

 Write "\r\e[0mRegistrazione terminata !"

' Va in chiusura:
 datigrezzi.Close
 snd_pcm_close(handle)

End


Private Procedure crea_file(dg As String)

 Dim st As Stream
' Si impostano i dati generali del blocco d'intestazione del futuro file WAV.
' = Blocco d'intestazione del file wav futuro: 2 canali, 16 bit, hz 44100 =
 Dim bh As Byte[] = [&52, &49, &46, &46, &00, &00, &00, &00, &57, &41, &56, &45, &66, &6D, &74, &20, &10, &00, &00, &00, &01, &00, &02, &00,
                     &44, &AC, &00, &00, &10, &B1, &02, &00, &04, &00, &10, &00, &64, &61, &74, &61, &00, &00, &00, &00]

' Imposta i dati di tipo "Integer" previsti al 5° e al 40° byte del blocco d'intestazione del file WAV:
 st = Memory bh.Data For Write 
 Seek #st, 4
 Write #st, Len(File.Load(dg)) + 36 As Integer
 Seek #st, 40
 Write #st, Len(File.Load(dg)) As Integer
 st.Close

' Per generare il file WAV finale, eseguibile, uniamo i dati del blocco iniziale ai dati grezzi registrati:
 File.Save("/tmp/FileFINALE.wav", bh.ToString(0, bh.Count) & File.Load(dg))

End

Un altro esempio più breve e semplice, come il precedente con frequenza di campionamento a 44100 hertz, risoluzione a 16 bit e 2 canali:

Private Const DEVICE As String = "default"
Private Const CANALI As Integer = 2
Private Const FREQUENZA As Integer = 44100


Library "libasound:2.0.0"

Private Const SND_PCM_STREAM_CAPTURE 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(handleP As Pointer, nome As String, flusso As Integer, mode As Integer) As Integer

' 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)
' Set the hardware and software parameters in a simple way.
Private Extern snd_pcm_set_params(pcm As Pointer, formatInt As Integer, accesso As Integer, channels As Integer, rate As Integer, soft_resample As Integer, latency As Integer) As Integer

' snd_pcm_sframes_t snd_pcm_readi (snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)
' Read interleaved frames from a PCM.
Private Extern snd_pcm_readi(pcm As Pointer, buffer As Pointer, size As Long) As Long

' 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_recover (snd_pcm_t *pcm, int err, int silent)
' Recover the stream state from an error or suspend.
Private Extern snd_pcm_recover(pcm As Pointer, err As Integer, silent As Integer) As Integer
    
' snd_pcm_close(snd_pcm_t *pcm)
' Close PCM handle.
Private Extern snd_pcm_close(pcm As Pointer)


Public Sub Main()

 Dim handle As Pointer
 Dim percorsoTmp As String
 Dim err, cicli As Integer
 Dim buffer As New Short[128]
 Dim dim_buffer, frames As Long
 Dim datigrezzi As File

' Creiamo il file che ospiterà i dati grezzi audio registrati dall'applicativo:
 percorsoTmp = Temp("filegrezzo")
 File.Save(percorsoTmp, Null)

 datigrezzi = Open percorsoTmp For Write Append

' Apre il sub-sistema PCM di ALSA per la registrazione:
 err = snd_pcm_open(VarPtr(handle), device, SND_PCM_STREAM_CAPTURE, 0)
 If err < 0 Then Error.Raise("Errore nell'apertura del sub-sistema PCM: " & snd_strerror(err))

' Imposta i parametri del sub-sistema PCM di ALSA per la registrazione:
 err = snd_pcm_set_params(handle, SND_PCM_FORMAT_S16_LE, SND_PCM_ACCESS_RW_INTERLEAVED, CANALI, FREQUENZA, 1, 0)
 If err < 0 Then Error.Raise("Errore nell'impostazione dei parametri del sub-sistema PCM: " & snd_strerror(err))

 cicli = 400000000 / FREQUENZA
 dim_buffer = buffer.Count / SizeOf(gb.Short)

 Write "\r\e[5mAttendere......"
 Flush

 While cicli > 0
' Registra i dati nel buffer:
   frames = snd_pcm_readi(handle, buffer.Data, dim_buffer)
   If frames < 0 Then
     frames = snd_pcm_recover(handle, frames, 0)
     snd_pcm_close(handle)
     Error.Raise("Errore nella registrazione dei dati audio: " & snd_strerror(frames))
   Endif
   If (frames > 0) And (frames < dim_buffer) Then
     snd_pcm_close(handle)
     Error.Raise("Lettura ridotta (atteso: " & CStr(dim_buffer) & ", letto: " & CStr(frames) & ")")
   Endif
' Scrive il file contenente i dati grezzi wav:
   Write #datigrezzi, buffer.Data, buffer.Count * SizeOf(gb.Short)
   Dec cicli
 Wend

 ' Genera il file WAV finale:
 crea_file(percorsoTmp)

 Write "\r\e[0mRegistrazione terminata !"

' Va in chiusura:
 datigrezzi.Close
 snd_pcm_close(handle)

End


Private Procedure crea_file(dg As String)

 Dim st As Stream
' Si impostano i dati generali del blocco d'intestazione del futuro file WAV.
' = Blocco d'intestazione del file wav futuro: 2 canali, 16 bit, hz 44100 =
 Dim bh As Byte[] = [&52, &49, &46, &46, &00, &00, &00, &00, &57, &41, &56, &45, &66, &6D, &74, &20, &10, &00, &00, &00, &01, &00, &02, &00,
                     &44, &AC, &00, &00, &10, &B1, &02, &00, &04, &00, &10, &00, &64, &61, &74, &61, &00, &00, &00, &00]

' Imposta i dati di tipo "Integer" previsti al 5° e al 40° byte del blocco d'intestazione del file WAV:
 st = Memory bh.Data For Write 
 Seek #st, 4
 Write #st, Len(File.Load(dg)) + 36 As Integer
 Seek #st, 40
 Write #st, Len(File.Load(dg)) As Integer
 st.Close

' Per generare il file WAV finale, eseguibile, uniamo i dati del blocco iniziale ai dati grezzi registrati:
 File.Save("/tmp/FileFINALE.wav", bh.ToString(0, bh.Count) & File.Load(dg))

End

In quest'altro esempio si avrà la possibilità, premendo su un Button posto sul Form, di avviare la registrazione, e premendo su un altro Button di interromperla. Successivamente ripremendo sul primo Button di avviare una nuova registrazione e così via:

Private Const DEVICE As String = "default"
Private Const CANALI As Integer = 2
Private Const FREQUENZA As Integer = 44100
Private cicli As Boolean


Library "libasound:2.0.0"

Private Const SND_PCM_STREAM_CAPTURE 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(handleP As Pointer, nome As String, flusso As Integer, mode As Integer) As Integer

' 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)
' Set the hardware and software parameters in a simple way.
Private Extern snd_pcm_set_params(pcm As Pointer, formatInt As Integer, accesso As Integer, channels As Integer, rate As Integer, soft_resample As Integer, latency As Integer) As Integer

' snd_pcm_sframes_t snd_pcm_readi (snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)
' Read interleaved frames from a PCM.
Private Extern snd_pcm_readi(pcm As Pointer, buffer As Pointer, size As Long) As Long

' 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_recover (snd_pcm_t *pcm, int err, int silent)
' Recover the stream state from an error or suspend.
Private Extern snd_pcm_recover(pcm As Pointer, err As Integer, silent As Integer) As Integer

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


Public Sub Button1_Click()

 Dim handle As Pointer
 Dim percorsoTmp As String
 Dim err As Integer
 Dim buffer As New Short[128]
 Dim dim_buffer, frames As Long
 Dim datigrezzi As File

' Creiamo il file che ospiterà i dati grezzi audio registrati dall'applicativo:
 percorsoTmp = Temp("filegrezzo")
 datigrezzi = Open percorsoTmp For Create

' Apre il sub-sistema PCM di ALSA per la registrazione:
 err = snd_pcm_open(VarPtr(handle), device, SND_PCM_STREAM_CAPTURE, 0)
 If err < 0 Then Error.Raise("Errore nell'apertura del sub-sistema PCM: " & snd_strerror(err))

' Imposta i parametri del sub-sistema PCM di ALSA per la registrazione:
 err = snd_pcm_set_params(handle, SND_PCM_FORMAT_S16_LE, SND_PCM_ACCESS_RW_INTERLEAVED, CANALI, FREQUENZA, 1, 0)
 If err < 0 Then Error.Raise("Errore nell'impostazione dei parametri del sub-sistema PCM: " & snd_strerror(err))

 cicli = True
 dim_buffer = buffer.Count / SizeOf(gb.Short)

 While cicli   ' Avvia un ciclo infinito per la registrazione
' Registra i dati nel buffer:
   frames = snd_pcm_readi(handle, buffer.Data, dim_buffer)
   If frames < 0 Then
     frames = snd_pcm_recover(handle, frames, 0)
     snd_pcm_close(handle)
     Error.Raise("Errore nella registrazione dei dati audio: " & snd_strerror(frames))
   Endif
   If (frames > 0) And (frames < dim_buffer) Then
     snd_pcm_close(handle)
     Error.Raise("Lettura ridotta (atteso: " & CStr(dim_buffer) & ", letto: " & CStr(frames) & ")")
   Endif
' Scrive il file contenente i dati grezzi wav:
   Write #datigrezzi, buffer.Data, buffer.Count * SizeOf(gb.Short)
' Una brevissima pausa consente all'utente di operare su eventuali oggetti posti sul Form:
   Wait 0.001 
 Wend

 ' Genera il file WAV finale:
 crea_file(percorsoTmp)

' Va in chiusura:
 datigrezzi.Close
 snd_pcm_close(handle)
 Me.Close

End


Public Sub Button2_Click()

 cicli = False

End


Private Procedure crea_file(dg As String)

 Dim st As Stream
' Si impostano i dati generali del blocco d'intestazione del futuro file WAV.
' = Blocco d'intestazione del file wav futuro: 2 canali, 16 bit, hz 44100 =
 Dim bh As Byte[] = [&52, &49, &46, &46, &00, &00, &00, &00, &57, &41, &56, &45, &66, &6D, &74, &20, &10, &00, &00, &00, &01, &00, &02, &00,
                     &44, &AC, &00, &00, &10, &B1, &02, &00, &04, &00, &10, &00, &64, &61, &74, &61, &00, &00, &00, &00]

' Imposta i dati di tipo "Integer" previsti al 5° e al 40° byte del blocco d'intestazione del file WAV:
 st = Memory bh.Data For Write 
 Seek #st, 4
 Write #st, Len(File.Load(dg)) + 36 As Integer
 Seek #st, 40
 Write #st, Len(File.Load(dg)) As Integer
 st.Close

' Per generare il file WAV finale, eseguibile, uniamo i dati del blocco iniziale ai dati grezzi registrati:
File.Save("/tmp/FileFINALE.wav", bh.ToString(0, bh.Count) & File.Load(dg))

End


Note

[1] La memorizzazione e la conseguente disposizione dei valori dei campioni in memoria è condizionata dalle opzioni: "Interleaved" e "Non-interleaved". Tale caratteristica viene specificamente e precisamente impostata nel 3° parametro della funzione esterna di ALSA "snd_pcm_set_params( )".
In breve possiamo dire che l'impostazione di quel parametro, e quindi della modalità di registrazione audio, con la costante SND_PCM_ACCESS_RW_INTERLEAVED determina che nel caso di una registrazione "stereo" i dati afferenti ai campioni audio dei due canali saranno memorizzati in memoria in modo alternato uno dopo l'altro , come nel semplice esempio che segue:

| 16-bit 1° canale | 16-bit 2° canale | 16-bit 1° canale | 16-bit 2° canale | e così via......


Scegliendo invece la costante SND_PCM_ACCESS_RW_NONINTERLEAVED, i dati dei campioni audio saranno registrati in blocchi distinti per canali; ossia ad esempio così:

| 16-bit 1° canale | 16-bit 1° canale | 16-bit 1° canale | ...... | 16-bit 2° canale | 16-bit 2° canale | 16-bit 2° canale | .....

Vedere al riguardo anche: