Eseguire onde sonore con Alsa

Da Gambas-it.org - Wikipedia.

Mostriamo di seguito alcuni esempi di codice per generare ed eseguire onde di diverso tipo.


Codice base esemplificativo

Codice che genera ed esegue un rumore bianco

Di questo codice esporremo anche la descrizione della sua struttura:

' Individuiamo innanzitutto la libreria condivisa, nella quale sono contenute le funzioni esterne di ALSA:
Library "libasound:2.0.0"

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

' int snd_pcm_open(snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode)
Private Extern snd_pcm_open(pcmP 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)
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)
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)
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)
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_frequenza_near(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir)
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_channels_first(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val)
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_periodi(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val, int dir)
Private Extern snd_pcm_hw_params_set_periods(pcmP As Pointer, ptrP As Pointer, valP As Pointer, dirI As Integer) As Integer

' int snd_pcm_hw_params_set_buffer_size(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t val)
Private Extern snd_pcm_hw_params_set_buffer_size(pcmP As Pointer, ptrP As Pointer, uframes As Long) As Integer

' int snd_pcm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
Private Extern snd_pcm_hw_params(pcmP As Pointer, ptrP As Pointer) As Integer

' snd_pcm_sframes_t snd_pcm_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
Private Extern snd_pcm_writei(pcmP As Pointer, buffer As single[], uframes As Integer) As Integer

' int snd_pcm_prepare(snd_pcm_t * pcm)
Private Extern snd_pcm_prepare(pcmP As Pointer) As Integer

' int snd_pcm_close(snd_pcm_t * pcm)
Private Extern snd_pcm_close(pcmP As Pointer)


Public Sub Main()

 Dim handle As Pointer
 Dim dimen As Integer
 Dim j, num_secondi as Byte
 Dim frequenza As Integer = 44100   ' frequenza del campione audio
 Dim canali As Integer = 2          ' canali di uscita del campione audio
 Dim periodi As Integer = 2         ' Numero di periodi
 Dim dimenPeriodi As Integer = 8192
 Dim nomen As String = "plughw:0,0"
 Dim hwparams, frequenzaEsatta As Pointer
 Dim dati As Single[]

1 Viene innanzitutto allocata una struttura di tipo "hwparams", che consentirà di impostare la configurazione adatta per il dispositivo PCM.

 snd_pcm_hw_params_malloc(VarPtr(hwparams))
 

2 Quindi apriamo il dispositivo PCM di ALSA:

 If snd_pcm_open(VarPtr(handle), nomen, SND_PCM_STREAM_PLAYBACK, 0) < 0 then Message.Error("Errore nell'apertura del dispositivo PCM !")
 

3 Prima di poter scrivere dati dal PCM alla scheda audio, dobbiamo specificare il tipo di accesso, formato dei campioni, frequenza di campionamento, numero di canali, numero di periodi e dimensioni del periodo. In primo luogo, si inizializza la struttura "hwparams" con lo spazio di configurazione completa della scheda audio.

 If snd_pcm_hw_params_any(handle, hwparams) < 0 Then Message.Error("Non è possibile la configurazione del dispositivo PCM !")
 

4 Viene quindi impostato il tipo di accesso, il quale specifica il modo in cui i dati multicanale sono memorizzati nel buffer. Per l'accesso INTERLEAVED [Nota 1], ogni frame nel buffer contiene campioni di dati consecutivi per entrambi i canali. Per dati stereo a 16 bit il buffer contiene campioni di dati che si alternano per il canale sinistro e per quello destro. Per l'accesso NONINTERLEAVED [Nota 2], ogni periodo prima contiene tutti i campioni di dati per il primo canale seguito dai campioni di dati per il secondo canale e così via.

 If snd_pcm_hw_params_set_access(handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0 Then Message.Error("Errore nell'impostazione della modalità di accesso !")
 

5 Viene impostato il formato dei campioni:

 If snd_pcm_hw_params_set_format(handle, hwparams, SND_PCM_FORMAT_S16_LE) < 0 Then Message.Error("Errore nell'impostazione del formato campioni audio !")
 

6 Imposta la frequenza di campionamento. Se la frequenza esatta non è supportata dall'hardware, è opportuno utilizzare la frequenza più prossima possibile.

 frequenzaEsatta = VarPtr(frequenza)
 If snd_pcm_hw_params_set_rate_near(handle, hwparams, frequenzaEsatta, 0) < 0 Then Message.Error("Errore nell'impostazione della frequenza !")
 
 If frequenza <> Int@(frequenzaEsatta) Then Message.Error("La frequenza non è supportata dalla scheda audio !")
 

7 Imposta il numero dei canali:

 If snd_pcm_hw_params_set_channels(handle, hwparams, canali) < 0 Then Message.Error("Errore nell'impostazione dei canali !")
 

8 Imposta i numeri dei periodi:

 If snd_pcm_hw_params_set_periods(handle, hwparams, periodi, 0) < 0 Then Message.Error("Errore nell'impostazione dei periodi !")
 

9 L'unità della dimensione del buffer dipende dalla funzione. A volte viene fornito in byte, a volte il numero di frame deve essere specificata. Un frame [Nota 3] è il vettore dei dati di esempio per tutti i canali. Per 16 di dati bit stereo, un frame ha una lunghezza di quattro byte. Imposta in "frames" la dimensione del buffer. La latenza risultante è data da: latenza = dimenPeriodi * periodi / (frequenza * bytes_per_frame).

 dimen = Shr((dimenPeriodi * periodi), 2)

 If snd_pcm_hw_params_set_buffer_size(handle, hwparams, dimen) < 0 Then Message.Error("Errore nell'impostazione della dimensione del buffer !")
 

10 Si applica, dunque, la configurazione fin qui preparata al dispositivo PCM puntato dalla variabile handle di tipo Pointer.

 If snd_pcm_hw_params(handle, hwparams) < 0 Then Message.Error("Errore nell'impostazione dei parametri hardware !")
 

11 Dopo la configurazione del sub-sistema PCM, è possibile inviargli i dati. Con il tipo di accesso in scrittura "interleaved" si dovrà utilizzare la funzione "snd_pcm_writei", che invia i dati frames presenti nella variabile "dati" dal buffer dei dati al dispositivo PCM puntato dalla variabile handle di tipo Pointer. Bisogna assicurarsi che venga inviata al sub-sistema PCM una quantità adeguata di dati, altrimenti verrà sollevato un errore di Buffer Underrun [Nota 4]. In caso di tale errore è opportuno chiamare la funzione "snd_pcm_prepare".

 num_secondi = 1

 num_campioni = num_secondi * canali * frequenza

 dati = New Single[]

 While k <= num_campioni
   dati.Push(Rnd(0.1, 1))
   k += canali
 Wend

' Invia i valori dell'onda al dispositivo sonoro PCM di ALSA per 4 secondi:
 For j = 1 To 4
   frames = snd_pcm_writei(handle, dati, frequenza)
   snd_pcm_prepare(handle)
   If frames < 0 Then Error.Raise("Errore alla funzione 'snd_pcm_writei' !")
 Next

' Va in chiusura:
 snd_pcm_close(handle)
   
End


Uso di codice semplificato

L'API di ALSA consente anche di utilizzare una modalità semplificata per ottenenere onde sonore.

Esempio di codice per generare un'onda sinusoidale

L'esempio che segue mostra un codice che genera un'onda sinusoidale della frequenza di 440 hz:

Private Const CAMPIONAMENTO As Integer = 44100   ' Frequenza di campionamento dell'onda
Private Const FREQUENZA As Single = 440.0        ' Frequenza sonora dell'onda


Library "libasound:2.0.0"  
     
Private Const device As String = "default"  
Private Const SND_PCM_STREAM_PLAYBACK As Integer = 0  
Private Const SND_PCM_FORMAT_FLOAT As Integer = 14  
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, name As String, pcm_stream 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, formatI 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_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 Single[], size 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
     
' snd_pcm_close(snd_pcm_t *pcm)
' Close PCM handle.
Private Extern snd_pcm_close(pcm As Pointer)  
     
     
Public Sub Main()
     
  Dim err, j, k, frames As Integer
  Dim handle As Pointer
  Dim buffer As New Single[]
    
  err = snd_pcm_open(VarPtr(handle), device, SND_PCM_STREAM_PLAYBACK, 0)
  If err < 0 Then Error.Raise("Errore nell'apertura del sub-sistema PCM: " & snd_strerror(err))
     
  err = snd_pcm_set_params(handle, SND_PCM_FORMAT_FLOAT, SND_PCM_ACCESS_RW_INTERLEAVED, 1, CAMPIONAMENTO, 1, 500000)
  If err < 0 Then Error.Raise("Errore nell'impostazione dei parametri del sub-sistema PCM: " & snd_strerror(err))

' Genera i valori dell'onda sinusoidale:
  For k = 0 To CAMPIONAMENTO - 1
   buffer.Push(Sin(2 * Pi * FREQUENZA / CAMPIONAMENTO * k))
  Next  

' Invia i valori dell'onda sinusoidale al dispositivo sonoro PCM di ALSA per 4 secondi:
  For j = 1 To 4
    frames = snd_pcm_writei(handle, buffer, CAMPIONAMENTO)
    If frames < 0 Then Error.Raise("Errore alla funzione 'snd_pcm_writei' !")
  Next

' Va in chiusura:
  snd_pcm_close(handle)  
     
End


Generare un bip

Private Const CAMPIONAMENTO As Integer = 11025   ' Frequenza di campionamento dell'onda
Private Const FREQUENZA As Single = 880.0        ' Frequenza sonora dell'onda


Library "libasound:2.0.0"  
    
Private Const device As String = "default"  
Private Const SND_PCM_STREAM_PLAYBACK As Integer = 0  
Private Const SND_PCM_FORMAT_FLOAT As Integer = 14  
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, name As String, pcm_stream 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, formatI 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_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 Single[], size 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

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


Public Sub Main()

 Dim err, k, frames As Integer
 Dim handle As Pointer
 Dim buffer As New Single[]

 err = snd_pcm_open(VarPtr(handle), device, SND_PCM_STREAM_PLAYBACK, 0)
 If err < 0 Then Error.Raise("Errore nell'apertura del sub-sistema PCM: " & snd_strerror(err))

 err = snd_pcm_set_params(handle, SND_PCM_FORMAT_FLOAT, SND_PCM_ACCESS_RW_INTERLEAVED, 1, CAMPIONAMENTO, 1, 500000)
 If err < 0 Then Error.Raise("Errore nell'impostazione dei parametri del sub-sistema PCM: " & snd_strerror(err))

' Genera i valori dell'onda sinusoidale:
 For k = 0 To CAMPIONAMENTO - 1
  buffer.Push(Sin(2 * Pi * FREQUENZA / CAMPIONAMENTO * k))
 Next  

' Invia i valori dell'onda sinusoidale al dispositivo sonoro PCM di ALSA:
 For k = 1 To 2
   frames = snd_pcm_writei(handle, buffer, CAMPIONAMENTO / 3.5)  ' "CAMPIONAMENTO / 3.5" imposta la durata del bip
   If frames < 0 Then Error.Raise("Errore alla funzione 'snd_pcm_writei' !")
 Next

' Va in chiusura:
 snd_pcm_close(handle)  

End


Eseguire una scala cromatica di 10 ottave con un'onda sinusoidale

L'esempio in questione eseguirà con un suono di onda sinusoidale una scala cromatica di ben 10 ottave:

Private Const CAMPIONAMENTO As Integer = 44100   ' frequenza di campionamento


Library "libasound:2.0.0"  
     
Private Const device As String = "default"  
Private Const SND_PCM_STREAM_PLAYBACK As Integer = 0  
Private Const SND_PCM_FORMAT_FLOAT As Integer = 14  
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, name As String, pcm_stream 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, formatI 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_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 Single[], size 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
     
' snd_pcm_close(snd_pcm_t *pcm)
' Close PCM handle.
Private Extern snd_pcm_close(pcm As Pointer)
       
     
Public Sub Main()
     
  Dim err, k, frames As Integer  
  Dim handle As Pointer  
  Dim buffer As New Single[]
  Dim b, m, st As Byte

  err = snd_pcm_open(VarPtr(handle), device, SND_PCM_STREAM_PLAYBACK, 0)
  If err < 0 Then Error.Raise("Errore nell'apertura del sub-sistema PCM: " & snd_strerror(err))
     
  err = snd_pcm_set_params(handle, SND_PCM_FORMAT_FLOAT, SND_PCM_ACCESS_RW_INTERLEAVED, 1, CAMPIONAMENTO, 1, 500000)
  If err < 0 Then Error.Raise("Errore nell'impostazione dei parametri del sub-sistema PCM: " & snd_strerror(err))

  For b = 1 To 10   ' Conta il numero di ottave
    For st = 0 To 11   ' Aggiunge la quantità di semitoni esistenti fra la prima nota dell'ottava e la nota da eseguire
      m = b - 1
' Genera i valori dell'onda sinusoidale:
      For k = 0 To CAMPIONAMENTO - 1
' Calcola la frequenza della nota da eseguire:
        buffer.Push(Sin(2 * Pi * (Fix((2 ^ (((12 * m) + st) / 12)) * 16)) / CAMPIONAMENTO * k))   ' [Nota 5]
      Next
' Invia i valori dell'onda al dispositivo sonoro PCM di ALSA:
      frames = snd_pcm_writei(handle, buffer, CAMPIONAMENTO)
      If frames < 0 Then Error.Raise("Errore alla funzione 'snd_pcm_writei' !")
      buffer.Clear
    Next
  Next

' Va in chiusura:
  snd_pcm_close(handle)  

End


Esempio di codice per emettere un'onda "triangolare"

L'esempio che segue mostra un codice che genera un'onda a triangolare della frequenza di 440 hz:

Private Const CAMPIONAMENTO As Integer = 44100   ' frequenza di campionamento


Library "libasound:2.0.0"  
    
Private Const device As String = "default"  
Private Const SND_PCM_STREAM_PLAYBACK As Integer = 0  
Private Const SND_PCM_FORMAT_FLOAT As Integer = 14  
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, name As String, pcm_stream 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, formatI 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_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 Single[], size 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
     
' snd_pcm_close(snd_pcm_t *pcm)
' Close PCM handle.
Private Extern snd_pcm_close(pcm As Pointer)
     

Public Sub Main()
     
  Dim err, j, k, frames As Integer
  Dim handle As Pointer
  Dim buffer As New Single[]
  Dim canali As Integer
  Dim hz, periodo As Single

  hz = 440.0                 ' frequenza dell'onda
  canali = 1
  periodo = CAMPIONAMENTO / hz
 
  err = snd_pcm_open(VarPtr(handle), device, SND_PCM_STREAM_PLAYBACK, 0)
  If err < 0 Then Error.Raise("Errore nell'apertura del sub-sistema PCM: " & snd_strerror(err))
     
  err = snd_pcm_set_params(handle, SND_PCM_FORMAT_FLOAT, SND_PCM_ACCESS_RW_INTERLEAVED, canali, CAMPIONAMENTO, 1, 500000)
  If err < 0 Then Error.Raise("Errore nell'impostazione dei parametri del sub-sistema PCM: " & snd_strerror(err))


' Genera i valori dell'onda "triangolare":
  While k <= CAMPIONAMENTO
    buffer.Push(Abs(2 * (k / periodo - CInt((k / periodo) + 0.5))))
    k += canali
  Wend
   
' Invia i valori dell'onda al dispositivo sonoro PCM di ALSA:
  For j = 0 To 4
    frames = snd_pcm_writei(handle, buffer, CAMPIONAMENTO)
    If frames < 0 Then Error.Raise("Errore alla funzione 'snd_pcm_writei' !")
  Next

' Va in chiusura:
  snd_pcm_close(handle)  
     
End


Esempio di codice per emettere un'onda "quadra"

L'esempio che segue mostra un codice che genera un'onda quadra della frequenza di 440 hz:

Private Const CAMPIONAMENTO As Integer = 44100   ' frequenza di campionamento
Private Const CANALI As Integer = 1
Private Const NUM_SECONDI As Byte = 1


Library "libasound:2.0.0"  
    
Private Const device As String = "default"  
Private Const SND_PCM_STREAM_PLAYBACK As Integer = 0  
Private Const SND_PCM_FORMAT_FLOAT As Integer = 14  
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, name As String, pcm_stream 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, formatI 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_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 Single[], size 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
     
' snd_pcm_close(snd_pcm_t *pcm)
' Close PCM handle.
Private Extern snd_pcm_close(pcm As Pointer)
      

Public Sub Main()
     
 Dim err, j, k, num_campioni, frames As Integer
 Dim handle As Pointer
 Dim buffer As New Single[]
 Dim hz, periodo As Single

 hz = 440.0            ' frequenza dell'onda
 num_campioni = NUM_SECONDI * CANALI * CAMPIONAMENTO
 periodo = CAMPIONAMENTO / hz
      
 err = snd_pcm_open(VarPtr(handle), device, SND_PCM_STREAM_PLAYBACK, 0)
 If err < 0 Then Error.Raise("Errore nell'apertura del sub-sistema PCM: " & snd_strerror(err))
     
 err = snd_pcm_set_params(handle, SND_PCM_FORMAT_FLOAT, SND_PCM_ACCESS_RW_INTERLEAVED, CANALI, CAMPIONAMENTO, 1, 500000)
 If err < 0 Then Error.Raise("Errore nell'impostazione dei parametri del sub-sistema PCM: " & snd_strerror(err))

' Genera i valori dell'onda "quadra":
 While k <= num_campioni
   buffer.Push(Sgn(Sin(2 * Pi(1) * (k / periodo))))
   k += CANALI
 Wend
        
' Invia i valori dell'onda al dispositivo sonoro PCM di ALSA:
 For j = 0 To 4
   frames = snd_pcm_writei(handle, buffer, CAMPIONAMENTO)
   If frames < 0 Then Error.Raise("Errore alla funzione 'snd_pcm_writei' !")
 Next  

' Va in chiusura:
 snd_pcm_close(handle)  
     
End


Creare ed eseguire onde sinusoidali con una tastiera Midi

Il seguente semplice codice genera ed esegue onde sinusoidali, inviando dati Midi grezzi mediante una tastiera Midi e gestendo il suono con le risorse del API di ALSA. In particolare viene generata un'onda sinusoidale sulla base del numero di nota Midi inviata al programma dalla tastiera Midi esterna. Tale onda sinusoidale viene infine eseguita dal sub-sistema PCM di ALSA.

(In questo esempio la modalità di esecuzione è monofonica, pertanto premendo due o più tasti della tastiera Midi contemporaneamente verrà generato un errore.)

Private fl As File
Private dati As New Byte[]
Private handle As Pointer
Private Const BUFFER_LEN As Integer = 44100


Library "libasound:2.0.0"  
     
Private Const device As String = "default"
Private Const SND_PCM_STREAM_PLAYBACK As Integer = 0  
Private Const SND_PCM_FORMAT_FLOAT As Integer = 14  
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, name As String, stream_t 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, format_t As Integer, access_t As Integer, channels As Integer, rate As Integer, 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, buffSn As Single[], sInt 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

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


Public Sub Main()
 
 Dim dev As String
 
' Cerca il file-device Midi:
 dev = Dir("/dev/snd", "midiC*", gb.Device)[0]
 If IsNull(dev) Then Error.Raise("Dispositivo Midi non trovato !")
    
 dev = "/dev/snd" &/ dev
 Print "Dispositivo Midi: "; dev; "\n\n"
  
' Apre il file-device Midi in lettura e lo pone in "osservazione":
 fl = Open dev For Read Watch  
 
End


Public Sub File_Read()
 
 Dim freq As Single
 Dim b As Byte
 
 Read #fl, b
 If b > 239 Then Return
 dati.Push(b)
 
 If dati.Count = 3 Then
   If dati[2] = 100 Then
' Sulla base del numero della nota Midi, inviata dalla tastiera Midi esterna,Genera i dati della frequenza dell'onda sonora da eseguire:
     freq = CSingle(16.3516 * (2 ^ (1 / 12)) ^ (dati[1] - 12))
     Suona(freq)
     Write "\rNota Midi: " & CStr(dati[1] + 12) & "   Freq.: " & CStr(Round(freq, -3)) & " hertz"
   Endif
 Endif
     
 If b = 0 Then 
   snd_pcm_close(handle)
   dati = New Byte[]
 Endif
   
End


Private Procedure Suona(hz As Single)
 
 Dim err, k, frames As Integer
 Dim buffer As New Single[]
 
 err = snd_pcm_open(VarPtr(handle), device, SND_PCM_STREAM_PLAYBACK, 0)
 If err < 0 Then GestioneErrore(err)
 
 err = snd_pcm_set_params(handle, SND_PCM_FORMAT_FLOAT, SND_PCM_ACCESS_RW_INTERLEAVED, 1, BUFFER_LEN, 1, 500000)
 If err < 0 Then GestioneErrore(err)
   
' Genera i valori dell'onda sinusoidale:
 For k = 0 To BUFFER_LEN - 1
   buffer.Push(Sin(2 * Pi * hz / BUFFER_LEN * k))
 Next  
 
' Invia i valori dell'onda sinusoidale al dispositivo sonoro PCM di ALSA:
 frames = snd_pcm_writei(handle, buffer, BUFFER_LEN \ 2)
 If frames < 0 Then GestioneErrore(err)
   
End


Private Procedure GestioneErrore(er As Integer)
 
 fl.Close
 Error.Raise("Errore: " & snd_strerror(er))

End


Note

[1] L'accesso INTERLEAVED prevede un'organizzazione dei dati in cui i campioni di ogni canale, che saranno eseguiti allo stesso tempo, si susseguono in sequenza: il campione di un canale segue il campione dell'altro canale.

[2] L'accesso NONINTERLEAVED prevede un'organizzazione dei dati in cui i campioni di un singolo canale si susseguono sequenzialmente; i campioni per l'altro canale si trovano o in un altro buffer o in un'altra parte di questo buffer.

[3] Un campione audio è un valore singolo che rappresenta l'ampiezza del segnale audio in un determinato attimo di tempo su un singolo canale. Con l'audio digitale spesso si parla di dati che rappresentano tutti i canali in un unico momento temporale. Questa è una raccolta di campioni, una per canale, ed è generalmente chiamata "frame".

[4] L'errore di Buffer Underrun, o errore di svuotamento del buffer, si ottiene quando il processo di scrittura dei dati nel dispositivo PCM si interrompe inaspettatamente a causa della mancanza di dati nel buffer (buffer vuoto) da inviare al predetto dispositivo.

[5] Conoscendo la frequenza di una nota, la formula per calcolare la frequenza di tutte le altre note, compresi i semitoni, è: 2^(n/12) * hz
ossia 2 elevato a n/12 e moltiplicato per la frequenza della nota di riferimento; dove n è il numero dei semitoni di distanza dalla nota di riferimento stessa.
Ad esempio: rispetto al La a 440 Hz, la frequenza del Si, distante da La due semitoni, è: 2 ^(2/12) * 440 = 1,1225 * 440 = 493,9

Un'altra modalità, per calcolare la frequenza delle altre note partendo dalla frequenza conosciuta di una nota, è: hz * c ^ n
laddove hz è la frequenza conosciuta di una nota, c è il coefficiente uguale a 1.0594630943592952645 ed n è il numero di semitoni esistenti fra la nota di riferimento e la nota di cui si intende trovare la frequenza.
Ad esempio: rispetto al La a 440 Hz, la frequenza del Si, distante da La due semitoni, è: 440 * c ^ 2 = 493,8833

Se invece si vuole ottenere la frequenza di una nota Midi (0 - 127):

Dim frequenza As Float
Dim nota_Midi As Byte

 nota_Midi = 69   '  Numero della nota Midi corrispondente al La (hz 440)
 
 frequenza = 261.63 * (2 ^ (1 / 12)) ^ (nota_Midi - 60)