Differenze tra le versioni di "Eseguire onde sonore con Alsa"
Riga 134: | Riga 134: | ||
===Esempio di codice per generare un bip=== | ===Esempio di codice per generare un bip=== | ||
− | Private Const | + | Private Const CAMPIONAMENTO As Integer = 11025 <FONT color=gray>' ''Frequenza di campionamento dell'onda''</font> |
Private Const FREQUENZA As Single = 880.0 <FONT color=gray>' ''Frequenza sonora dell'onda''</font> | Private Const FREQUENZA As Single = 880.0 <FONT color=gray>' ''Frequenza sonora dell'onda''</font> | ||
Riga 175: | Riga 175: | ||
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)) | ||
− | err = snd_pcm_set_params(handle, SND_PCM_FORMAT_FLOAT, SND_PCM_ACCESS_RW_INTERLEAVED, 1, | + | 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)) | If err < 0 Then Error.Raise("Errore nell'impostazione dei parametri del sub-sistema PCM: " & snd_strerror(err)) | ||
<FONT color=gray>' ''Genera i valori dell'onda sinusoidale:''</font> | <FONT color=gray>' ''Genera i valori dell'onda sinusoidale:''</font> | ||
− | For k = 0 To | + | For k = 0 To CAMPIONAMENTO - 1 |
− | buffer.Push(Sin(2 * Pi * FREQUENZA / | + | buffer.Push(Sin(2 * Pi * FREQUENZA / CAMPIONAMENTO * k)) |
Next | Next | ||
<FONT color=gray>' ''Invia i valori dell'onda sinusoidale al dispositivo sonoro PCM di ALSA:''</font> | <FONT color=gray>' ''Invia i valori dell'onda sinusoidale al dispositivo sonoro PCM di ALSA:''</font> | ||
For j = 1 To 2 | For j = 1 To 2 | ||
− | frames = snd_pcm_writei(handle, buffer, | + | frames = snd_pcm_writei(handle, buffer, CAMPIONAMENTO / 3.5) <FONT color=gray>' ''"CAMPIONAMENTO / 3.5" imposta la durata del bip''</font> |
If frames < 0 Then Error.Raise("Errore alla funzione 'snd_pcm_writei' !") | If frames < 0 Then Error.Raise("Errore alla funzione 'snd_pcm_writei' !") | ||
Next | Next | ||
Riga 191: | Riga 191: | ||
<FONT color=gray>' ''Va in chiusura:''</font> | <FONT color=gray>' ''Va in chiusura:''</font> | ||
snd_pcm_close(handle) | snd_pcm_close(handle) | ||
− | + | ||
End | End | ||
Versione delle 13:55, 1 ott 2023
Mostriamo di seguito alcuni esempi di codice per generare ed eseguire onde di diverso tipo.
Indice
- 1 Codice base esemplificativo
- 2 Uso di codice semplificato
- 2.1 Esempio di codice per generare un bip
- 2.2 Esempio di codice per generare un'onda sinusoidale
- 2.3 Eseguire una scala cromatica di 10 ottave con un'onda sinusoidale
- 2.4 Esempio di codice per emettere un'onda "triangolare"
- 2.5 Esempio di codice per emettere un'onda "quadra"
- 2.6 Creare ed eseguire onde sinusoidali con una tastiera Midi
- 3 Note
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: For j = 0 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 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, b, 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 j = 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
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 BUFFER_LEN As Integer = 44100 ' Frequenza di campionamento dell'onda Private 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, BUFFER_LEN, 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 BUFFER_LEN - 1 buffer.Push(Sin(2 * Pi * FREQUENZA / BUFFER_LEN * k)) Next ' Invia i valori dell'onda sinusoidale al dispositivo sonoro PCM di ALSA: For j = 0 To 4 frames = snd_pcm_writei(handle, buffer, BUFFER_LEN) 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)