Eseguire un file audio WAV con le sole funzioni esterne di C e di ALSA

Da Gambas-it.org - Wikipedia.

Di seguito mostreremo un interessante esempio, con il quale sarà possibile eseguire un file audio WAV con le sole funzioni esterne di C e di ALSA, senza dunque utilizzare le funzioni native di Gambas.

Private Const BUFFER As Long = 512 


Library "libc:6"

Private Const SEEK_SET As Integer = 0
Private Const SEEK_END As Integer = 2
Private Const SIGKILL As Integer = 9

' FILE *fopen (const char *__restrict __filename, const char *__restrict __modes)
' Open a file and create a new stream for it.
Private Extern fopen(__filename As String, __modes As String) As Pointer

' int printf (const char *__restrict __format, ...)
' Write formatted output to stdout.
Private Extern printf(__format As String) As Integer
Private Extern printf_2(__format As String, param1 As Integer, param2 As Integer, param3 As Float) As Integer Exec "printf"

' int kill (__pid_t __pid, int __sig)
' Send signal SIG to process number PID.
Private Extern kill_c(__pid As Integer, __sig As Integer) As Integer Exec "kill"

' int fseek (FILE *__stream, long int __off, int __whence)
' Seek to a certain position on STREAM.
Private Extern fseek(__stream As Pointer, __off As Long, __whence As Integer) As Integer

' long int ftell (FILE *__stream)
' Return the current position of STREAM.
Private Extern ftell(__stream As Pointer) As Long

' void *malloc (size_t __size)
' Allocate SIZE bytes of memory.
Private Extern malloc(__size As Long) As Pointer

' size_t fread (void *__restrict __ptr, size_t __size, size_t __n, FILE *__restrict __stream)
' Read chunks of generic data from STREAM.
Private Extern fread(__ptr As Pointer, __size As Long, __n As Long, __stream As Pointer) As Long

' int sprintf (char *__restrict __s, const char *__restrict __format, ...)
' Write formatted output to S.
Private Extern sprintf(__str As Pointer, __format As String, param1 As Pointer) As Integer

' int strncmp (const char *__s1, const char *__s2, size_t __n)
' Compare N characters of S1 and S2.
Private Extern strncmp(__s1 As Pointer, __s2 As String, __n As Long) As Integer

' size_t fwrite (const void *__restrict __ptr, size_t __size, size_t __n, FILE *__restrict __s)
' Write chunks of generic data to STREAM.
Private Extern fwrite(__ptr As Pointer, __size As Long, __n As Long, __stream As Pointer) As Long

' int fclose (FILE *__stream)
' Close STREAM.
Private Extern fclose(__stream As Pointer) As Integer

' void free (void *__ptr)
' Free a block allocated by `malloc'.
Private Extern free_c(__ptr As Pointer) Exec "free"

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

Library "libasound:2.0.0"

Private Const SND_PCM_STREAM_PLAYBACK As Integer = 0
Private Const SND_PCM_FORMAT_S16_LE As Integer = 2
Private Const SND_PCM_ACCESS_RW_INTERLEAVED As Integer = 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, formatI As Integer, accessI As Integer, 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 As String
 Dim ent, ptr, usc, pcm As Pointer
 Dim lun, sp, off As Long
 Dim ii As New Integer[1]
 Dim err, tot As Integer
 Dim cc As New Short[1]
 Dim h, m As Short
 Dim sec As Float
 
 filewav = "/percorso/del/file.wav"
 
' Apre il file "wav" in sola "lettura" binaria:
 ent = fopen(filewav, "r")
 If ent == 0 Then
   printf("ERRORE in lettura del file wav!")
   kill_c(0, SIGKILL)
 Endif
 
' Posiziona il puntatore nell'area di memoria, puntata dalla variabile "ent", alla fine del flusso:
 fseek(ent, 0, SEEK_END)
 
' Ritorna la quantità di dati contenuti nell'area di memoria, puntata dalla variabile "ent":
 lun = ftell(ent)
 
' Alloca un'area di memoria di lunghezza pari al valore contenuto dalla varibile "lun":
 ptr = malloc(lun)
 
' Riposiziona il Puntatore all'inizio del file, per poter leggere tutti i suoi dati:
 fseek(ent, 0, SEEK_SET)
' Legge l'intera quantità di dati del file wav:
 fread(ptr, 1, lun, ent)
 
' Assegna un numero di byte, presenti nell'area puntata dal Puntatore "ptr", pari alla quantità di byte occupata da un "Intero Corto".
' L'assegnazione comincia dal byte di indice 22 per estrarre il numero dei canali del file wav.
 sprintf(cc.Data, "%s", ptr + 22)
 
' Assegna un numero di byte, presenti nell'area puntata dal Puntatore "ptr", pari alla quantità di byte occupata da un "Intero".
' L'assegnazione comincia dal byte di indice 24 per estrarre il valore della frequenza di campionamento del file wav.
 sprintf(ii.Data, "%s", ptr + 24)
 
 sp = 35
 
 Repeat
' Legge il file a cominciare dal 37° byte (indice 36), sino a quando non trova i caratteri "data":
   Inc sp
 Until strncmp(ptr + sp, "data", 4) == 0
 
' Incrementa di otto unità la variabile "sp", per raggiungere l'indice del byte di inizio dei dati audio grezzi:
 sp += 8
 
' Apre il file dei dati grezzi wav in sola "scrittura" binaria:
 usc = fopen("/tmp/datigrezzi", "w")
  If ent == 0 Then
   printf("ERRORE in scrittura del file wav!")
   kill_c(0, SIGKILL)
 Endif

' Scrive i dati, precedentemente letti, in un nuovo file contenente i soli dati audio wav:
 fwrite(ptr + sp, 1, lun - sp, usc)
' Chiude il flusso di dati in uscita per consentire la scrittura tottale dei dati grezzi nel file:
 fclose(usc)


' ALSA '

' Apre il sub-sistema "PCM" di ALSA:
 err = snd_pcm_open(VarPtr(pcm), "default", SND_PCM_STREAM_PLAYBACK, 0)
 If err < 0 Then
   printf("Errore nell'apertura del subsistema PCM: " & snd_strerror(err))
   kill_c(0, SIGKILL)
 Endif

' Imposta gli opportuni parametri:
 err = snd_pcm_set_params(pcm, SND_PCM_FORMAT_S16_LE, SND_PCM_ACCESS_RW_INTERLEAVED, cc[0], ii[0], 1, 500000)
 If err < 0 Then
   printf("Errore nell'impostazione dei parametri audio: " & snd_strerror(err))
   kill_c(0, SIGKILL)
 Endif

 Repeat
' Scrive i dati letti nel sub-sistema "PCM" di ALSA.
' Ad ogni giro il Puntatore avanza di BUFFER byte in avanti nell'area di memoria puntata.
   err = snd_pcm_writei(pcm, ptr + sp + off, BUFFER / 4)
   tot += err
   sec = tot \ ii[0]
   h = (sec / 3600)
   m = (sec - (3600 * h)) / 60
' Mostra il tempo trascorso.
' A destra del carattere di formato ".3lf" è posto uno spazio, per impedire che dopo il 59° secondo,
' ritornando la stampa dei secondi alle unità, resti visibile l'ultimo millesimo di secondo del precedente minuto.
   printf_2("\rTempo trascorso: %d:%d:%.3lf ", h, m, (sec - (3600 * h) - (m * 60)))
   off += BUFFER
 Until tot >= lun / 4

' Impedisce che avvenga un troncamento inaspettato del processo dei dati audio finali:
 snd_pcm_drain(pcm)

' Libera la memoria precedentemente allocata:
 snd_pcm_close(pcm)
 free_c(ptr)
 fclose(ent)

End


Riferimenti