Audio ed Alsa: apertura del subsistema PCM

Da Gambas-it.org - Wikipedia.

Dichiarazione della Libreria di Alsa

Poiché bisognerà utilizzare delle funzioni esterne a Gambas, appartenenti alle API di ALSA, bisognerà innanzitutto individuare la specifica libreria condivisa .so ove tali funzioni sono contenute.

Si dichiarera all'inizio della Classe specifica la libreria di ALSA:

Library "libasound:2.0.0"


Apertura del subsistema PCM

L'apertura del sub-sistema PCM nella modalità di Riproduzione audio avviene mediante la specifica delle API di ALSA, così espressa in C:

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

laddove:

  • snd_pcm_t **pcm è un puntatore ad un puntatore che rappresenta, quest'ultimo, l'handle necessario per l'apertura del dispositivo PCM. Per ottenere una variabile di tipo Puntatore di un Puntatore si utilizzerà in Gambas la funzione VarPtr( ).

In Gambas detto handle sarà dichiarato come una variabile di tipo Puntatore:

Private handle As Pointer
  • const char * name è una stringa che rappresenta il nome identificativo dell'handle del dispositivo audio PCM, come risulta dai file presenti nel percorso "/proc/asound ", indicando numero della scheda audio, numero del dispositivo audio e numero del sub-disposiivo audio (ad esempio: "hw:1,0,0").

Per avere un automatico riconoscimento ed uso del dispositivo audio fornito dal sistema si potrà adoperare il nome identificativo la stringa "default ":

Private device As String = "default"

Il nome è legato e condiziona il tipo di interfaccia al sub-sistema PCM di ALSA, che potrà essere individuato tra quelli compresi nella enumerazione _snd_pcm_type. [nota 1]

  • snd_pcm_stream_t stream è un intero che specifica la modalità scelta della direzione del flusso dei dati, in tal caso sarà in modalità Riproduzione audio, ossia valore pari a zero:
Private Const SND_PCM_STREAM_PLAYBACK As Byte = 0
  • int mode è la modalità di apertura del sub-sitema PCM. L'impostazione standard del valore mode è zero. Altre modalità sono Non blocking e Async notification.


La funzione restituisce un intero che, se è uguale, a zero significa che l'operazione della funzione ha avuto successo.


Dichiarazione della funzione esterna snd_pcm_open

In Gambas la funzione snd_pcm_open per l'apertura del sub-sistema PCM dovrà essere innanzitutto dichiarata preventivamente con Extern:

Private Extern snd_pcm_open(handleP As Pointer, nome As String, flusso As Integer, mode As Integer) As Integer

e potremo, quindi, in subroutine richiamarla per l'uso (i nomi dei parametri sono quelli indicati nel paragrafo precedente):

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


Riassunto esemplificativo

Il codice sin qui esposto sarà il seguente:

Library "libasound:2.0.0"

Private handle As Pointer
Private device As String = "default"
Private Const SND_PCM_STREAM_PLAYBACK As Byte = 0

Private Extern snd_pcm_open(handleP As Pointer, nome As String, flusso As Integer, mode As Integer) As Integer


Public apre_alsa()

 Dim err As Integer

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

End


Apertura del dispositivo PCM di ALSA ed impostazione dei parametri

Mostreremo un esempio che apre il dispositivo predefinito PCM, imposta alcuni parametri, e quindi visualizza il valore della maggior parte dei parametri dell'hardware audio. I risultati visualizzati variano leggermente a seconda dell'hardware audio.

Dopo aver eseguito il programma sul proprio sistema, sperimentare e apportare alcune modifiche. Cambiare il nome del dispositivo dal default a hw:0,0 o plughw : e vedere se i risultati cambiano. Modificare i valori dei parametri hardware e osservare come i risultati visualizzati cambiano.

Library "libasound:2.0.0"

Private Const SND_PCM_STREAM_PLAYBACK As Integer = 0
Private Const SND_PCM_ACCESS_RW_INTERLEAVED As Integer = 3
Private Const SND_PCM_FORMAT_S16_LE As Integer = 2


' 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

' 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(pcm As Pointer, snd_pcm_hw_params_P 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(pcm As Pointer, snd_pcm_hw_params_P As Pointer, snd_pcm_access_t 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 valI)
' Restrict a configuration space to contain only one format. 
Private Extern snd_pcm_hw_params_set_format(pcm As Pointer, snd_pcm_hw_params_P As Pointer, snd_pcm_format_t As Integer) As Integer

' int snd_pcm_hw_params_set_channels (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int valI)
' Restrict a configuration space to contain only one channels count.
Private Extern snd_pcm_hw_params_set_channels(pcm As Pointer, snd_pcm_hw_params_P As Pointer, snd_pcm_format_t As Integer) As Integer

' int snd_pcm_hw_params_set_rate_near (snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *valI, int *dirI)
' Restrict a configuration space to have rate nearest to a target.
Private Extern snd_pcm_hw_params_set_rate_near(pcm As Pointer, snd_pcm_hw_params_P As Pointer, valP As Pointer, irP 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(pcm As Pointer, snd_pcm_hw_params_P As Pointer) As Integer

' const char * snd_pcm_name (snd_pcm_t *pcm)
' Get identifier of PCM handle.
Private Extern snd_pcm_name(pcm As Pointer) As String

' snd_pcm_state_t snd_pcm_state (snd_pcm_t *pcm)
' Return PCM state.
Private Extern snd_pcm_state(pcm As Pointer) As Integer

' const char * snd_pcm_state_name (const snd_pcm_state_t state)
' Get name of PCM state.
Private Extern snd_pcm_state_name(snd_pcm_state_t As Integer) As String

' int snd_pcm_hw_params_get_access (const snd_pcm_hw_params_t *params, snd_pcm_access_t *_access)
' Extract access type from a configuration space.
Private Extern snd_pcm_hw_params_get_access(snd_pcm_hw_params_P As Pointer, snd_pcm_access_P As Pointer) As Integer

' const char * snd_pcm_access_name (const snd_pcm_access_t _access)
' Get name of PCM access type.
Private Extern snd_pcm_access_name(snd_pcm_access_t As Integer) As String

' int snd_pcm_hw_params_get_format (const snd_pcm_hw_params_t *params, snd_pcm_format_t *val)
' Extract format from a configuration space.
Private Extern snd_pcm_hw_params_get_format(snd_pcm_hw_params_P As Pointer, snd_pcm_format_P As Pointer) As Integer

' const char * snd_pcm_format_name (const snd_pcm_format_t format)
' Get name of PCM sample format.
Private Extern snd_pcm_format_name(snd_pcm_format_t As Integer) As String

' const char * snd_pcm_format_description (const snd_pcm_format_t format)
' Get description of PCM sample format.
Private Extern snd_pcm_format_description(snd_pcm_format_t As Integer) As String

' int snd_pcm_hw_params_get_subformat (const snd_pcm_hw_params_t *params, snd_pcm_subformat_t *subformat)
' Extract subformat from a configuration space.
Private Extern snd_pcm_hw_params_get_subformat(snd_pcm_hw_params_P As Pointer, snd_pcm_subformat_P As Pointer) As Integer

' const char * snd_pcm_subformat_name (const snd_pcm_subformat_t subformat)
' Get name of PCM sample subformat.
Private Extern snd_pcm_subformat_name(snd_pcm_subformat_t As Integer) As String

' const char * snd_pcm_subformat_description (const snd_pcm_subformat_t subformat)
' Get description of PCM sample subformat.
Private Extern snd_pcm_subformat_description(snd_pcm_subformat_t As Integer) As String

' int snd_pcm_hw_params_get_channels (const snd_pcm_hw_params_t *params, unsigned int *val)
' Extract channels from a configuration space.
Private Extern snd_pcm_hw_params_get_channels(snd_pcm_hw_params_P As Pointer, valP As Pointer) As Integer

' int snd_pcm_hw_params_get_rate (const snd_pcm_hw_params_t *params, unsigned int *val, int *dir)
' Extract rate from a configuration space.
Private Extern snd_pcm_hw_params_get_rate(snd_pcm_hw_params_P As Pointer, valP As Pointer, irP As Pointer) As Integer

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

' int snd_pcm_hw_params_get_period_size (const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *frames, int *dir)
' Extract period size from a configuration space.
Private Extern snd_pcm_hw_params_get_period_size(snd_pcm_hw_params_P As Pointer, snd_pcm_uframes_P As Pointer, irP As Pointer) As Integer

' int snd_pcm_hw_params_get_buffer_time (const snd_pcm_hw_params_t *params, unsigned int *val, int *dir)
' Extract buffer time from a configuration space.
Private Extern snd_pcm_hw_params_get_buffer_time(snd_pcm_hw_params_P As Pointer, valP As Pointer, irP As Pointer) As Integer

' int snd_pcm_hw_params_get_buffer_size (const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val)
' Extract buffer size from a configuration space.
Private Extern snd_pcm_hw_params_get_buffer_size(snd_pcm_hw_params_P As Pointer, snd_pcm_uframes_P As Pointer) As Integer

' int snd_pcm_hw_params_get_periods (const snd_pcm_hw_params_t *params, unsigned int *val, int *dir)
' Extract periods from a configuration space.
Private Extern snd_pcm_hw_params_get_periods(snd_pcm_hw_params_P As Pointer, valP As Pointer, irP As Pointer) As Integer

' int snd_pcm_hw_params_get_rate_numden (const snd_pcm_hw_params_t *params, unsigned int *rate_num, unsigned int *rate_den)
' Get rate exact info from a configuration space.
Private Extern snd_pcm_hw_params_get_rate_numden(snd_pcm_hw_params_P As Pointer, rate_num_P As Pointer, rate_den_P As Pointer) As Integer

' int snd_pcm_hw_params_get_sbits (const snd_pcm_hw_params_t *params)
' Get sample resolution info from a configuration space.
Private Extern snd_pcm_hw_params_get_sbits(snd_pcm_hw_params_P As Pointer) As Integer

' int snd_pcm_hw_params_is_batch (const snd_pcm_hw_params_t *params)
' Check if hardware does double buffering for data transfers for given configuration.
Private Extern snd_pcm_hw_params_is_batch(snd_pcm_hw_params_P As Pointer) As Integer

' int snd_pcm_hw_params_is_block_transfer (const snd_pcm_hw_params_t *params)
' Check if hardware does block transfers for samples for given configuration.
Private Extern snd_pcm_hw_params_is_block_transfer(snd_pcm_hw_params_P As Pointer) As Integer

' int snd_pcm_hw_params_is_double (const snd_pcm_hw_params_t *params)
' Check if hardware does double buffering for start/stop for given configuration.
Private Extern snd_pcm_hw_params_is_double(snd_pcm_hw_params_P As Pointer) As Integer

' int snd_pcm_hw_params_is_half_duplex (const snd_pcm_hw_params_t *params)
' Check if hardware does half-duplex only.
Private Extern snd_pcm_hw_params_is_half_duplex(snd_pcm_hw_params_P As Pointer) As Integer

' int snd_pcm_hw_params_is_joint_duplex (const snd_pcm_hw_params_t *params)
' Check if hardware does joint-duplex (playback and capture are somewhat correlated)
Private Extern snd_pcm_hw_params_is_joint_duplex(snd_pcm_hw_params_P As Pointer) As Integer

' int snd_pcm_hw_params_can_overrange (const snd_pcm_hw_params_t *params)
' Check if hardware supports overrange detection.
Private Extern snd_pcm_hw_params_can_overrange(snd_pcm_hw_params_P As Pointer) As Integer

' int snd_pcm_hw_params_can_mmap_sample_resolution (const snd_pcm_hw_params_t *params)
' Check if hardware supports sample-resolution mmap for given configuration. 
Private Extern snd_pcm_hw_params_can_mmap_sample_resolution(snd_pcm_hw_params_P As Pointer) As Integer

' int snd_pcm_hw_params_can_pause (const snd_pcm_hw_params_t *params)
' Check if hardware supports pause.
Private Extern snd_pcm_hw_params_can_pause(snd_pcm_hw_params_P As Pointer) As Integer

' int snd_pcm_hw_params_can_resume (const snd_pcm_hw_params_t *params)
' Check if hardware supports resume.
Private Extern snd_pcm_hw_params_can_resume(snd_pcm_hw_params_P As Pointer) As Integer 

' int snd_pcm_hw_params_can_sync_start (const snd_pcm_hw_params_t *params)
' Check if hardware supports synchronized start with sample resolution.
Private Extern snd_pcm_hw_params_can_sync_start(snd_pcm_hw_params_P As Pointer) 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_close(snd_pcm_t *pcm)
' Close PCM handle.
Private Extern snd_pcm_close(pcm As Pointer) As Integer


Public Sub Main()

 Dim handle, params As Pointer
 Dim err, v, v2, ir, frames, freq As Integer
 
' Apre il dispositivo PCM in riproduzione audio:
 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))
   
' Alloca un oggetto relativo ai parametri hardware audio:
 err = snd_pcm_hw_params_malloc(VarPtr(params))
 If err < 0 Then Error.Raise("Errore nell'allocazione di oggetto relativo ai parametri hardware: " & snd_strerror(err))
   
' Lo riempie con valori predefiniti:
 snd_pcm_hw_params_any(handle, params)

   
' Impostazione dei parametri hardware prescelti
   
' Modalità "Interleaved" [Nota 2]:
 snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED)
   
' Formato Signed 16-bit little-endian:
 snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE)
   
' Due canali (stereo):
 snd_pcm_hw_params_set_channels(handle, params, 2)
   
' Frequenza di campionamento 44100 bits/secondo (qualità CD):
 freq = 44100
 snd_pcm_hw_params_set_rate_near(handle, params, VarPtr(freq), VarPtr(ir))
 
' Scrive i suddetti parametri nel driver:
 err = snd_pcm_hw_params(handle, params)
 If err < 0 Then Error.Raise("Impossibile impostare i parametri hardware: " & snd_strerror(err))
 

' Mostra le informazioni relative all'interfaccia PCM

 Print "=== Informazioni relative all'interfaccia PCM ===\n"
 Print "Nome dell'handle del subsistema PCM =  '"; snd_pcm_name(handle); "'"
 Print "Stato del subsistema PCM =  "; snd_pcm_state_name(snd_pcm_state(handle))
   
 snd_pcm_hw_params_get_access(params, VarPtr(v))
 Print "Tipo di accesso =  "; snd_pcm_access_name(v)
   
 snd_pcm_hw_params_get_format(params, VarPtr(v))
 Print "Formato =  '"; snd_pcm_format_name(v); "' ("; snd_pcm_format_description(v); ")"
   
 snd_pcm_hw_params_get_subformat(params, VarPtr(v))
 Print "Sottoformato =  '"; snd_pcm_subformat_name(v); "' ("; snd_pcm_subformat_description(v); ")"
   
 snd_pcm_hw_params_get_channels(params, VarPtr(v))
 Print "Numero canali =  "; v
   
 snd_pcm_hw_params_get_rate(params, VarPtr(v), VarPtr(ir))
 Print "Frequenza di campionamento =  Hz "; v
   
 snd_pcm_hw_params_get_period_time(params, VarPtr(v), VarPtr(ir))
 Print "Periodo =  "; v; " us"
   
 snd_pcm_hw_params_get_period_size(params, VarPtr(frames), VarPtr(ir))
 Print "Dimensione del periodo =  "; frames; " frames"
   
 snd_pcm_hw_params_get_buffer_time(params, VarPtr(v), VarPtr(ir))
 Print "buffer =  "; v; " us"
   
 snd_pcm_hw_params_get_buffer_size(params, VarPtr(v))
 Print "Dimensione del buffer =  "; v; " frames"
   
 snd_pcm_hw_params_get_periods(params, VarPtr(v), VarPtr(ir))
 Print "Periodi per buffer = "; v; " frames"
   
 snd_pcm_hw_params_get_rate_numden(params, VarPtr(v), VarPtr(v2))
 Print "Frequenza di campionamento esatta =  Hz "; v; Chr(92); v2
   
 v = snd_pcm_hw_params_get_sbits(params)
 Print "Bit significanti =  "; v

 v = snd_pcm_hw_params_is_batch(params)
 Print "Batch =  "; CBool(v)
   
 v = snd_pcm_hw_params_is_block_transfer(params)
 Print "Block transfer =  "; CBool(v)

 v = snd_pcm_hw_params_is_double(params)
 Print "Double =  "; CBool(v)

 v = snd_pcm_hw_params_is_half_duplex(params)
 Print "Half duplex =  "; CBool(v)

 v = snd_pcm_hw_params_is_joint_duplex(params)
 Print "Joint duplex =  "; CBool(v)

 v = snd_pcm_hw_params_can_overrange(params)
 Print "Overrange =  "; CBool(v)

 v = snd_pcm_hw_params_can_mmap_sample_resolution(params)
 Print "Mmap =  "; CBool(v)

 v = snd_pcm_hw_params_can_pause(params)
 Print "Pause =  "; CBool(v)

 v = snd_pcm_hw_params_can_resume(params)
 Print "Resume =  "; CBool(v)

 v = snd_pcm_hw_params_can_sync_start(params)
 Print "Sync start =  "; CBool(v)

' Va in chiusura:
 snd_pcm_close(handle)

End


Note

[1] ALSA fornisce una serie di nomi dei dispositivi PCM. Quando si specifica una sorgente o un dispositivo audio, ALSA consente di specificare i dispositivi alternativi, non solo le schede audio.
L'elenco essenziale dei nomi principali per i dispositivi è il seguente:

  • dispositivo standard: default

- è quello predefinito;
- corrisponde al dispositivo plughw predefinito;
- non prevede parametri.

  • dispositivo nullo: null

- è un dispositivo nullo (buco nero);
- in lettura è muto;
- in scrittura non scrive;
- non prevede parametri.

  • dispositivo hardware: hw

- consente l'accesso diretto ai driver della scheda installati sul computer;
- prevede i seguenti parametri:

  1. CARD: Tipo di scheda audio (numero o nome);
  2. DEV: Numero del dispostivo;
  3. SUBDEV: sub-dispositivo (numero di porta all'interno della scheda audio, ma solo per le schede con più porte di uscita).

Se si pone il valore -1 in DEV ed in SUBDEV, viene indicato rispettivamente qualunque dispositivo o porta immediatamente disponibile.
- la sintassi è:

hw: <scheda audio> <dispositivo> <sub-dispositivo>

esempio: hw:1,0,0

  • dispositivo hardware con conversioni automatiche: plughw.

- consente l'accesso diretto al controllo finale con un plugin di conversione automatica.

  • dispositivo di conversione automatica: plug

- passa i dati mediante un plugin di conversione prima di ridirigere il flusso a un plugin specificato come parametro.

  • dispositivo di memoria condivisa: shm

- si usa per connettersi con dispositivi remoti.

[2] 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: