![]() |
Un'onda sinusoidale PCM |
Qualche
tempo fa (= due anni fa :D) vi ho mostrato come riprodurre un'onda
sonora tramite l'interfaccia WaveOut di Windows, oggi vi mostrerò
come riprodurre un'onda sonora su Linux tramite l'interfaccia ALSA,
la libreria di basso livello divenuta
uno standard de facto per
il sonoro su Linux, tant'è che
oggi viene distribuita col kernel di Linux direttamente.
Cos'è ALSA?
ALSA
(Advanced Linux Sound
Architecture) è un modulo
del kernel di linux, ovvero un programma che estende le funzionalità
del kernel. Per la sua vicinanza al kernel, è molto usata dalle
applicazioni che vogliono comunicare direttamente con la scheda
audio, anche se esistono molte altre comode librerie (PulseAudio,
Gstreamer) che si
interfacciano a loro volta ad ALSA ma che semplificano di molto la
vita ai programmatori.
Prima
dell'avvento di ALSA (che supporta anche le periferiche MIDI)
esisteva un altro “standard” su linux, OSS (Open
Sound
System),
il quale aveva la particolarità di permettere al programmatore di
comunicare direttamente con la scheda audio tramite il device a
caratteri (un file che, nei sistemi Unix, comunica coi driver di un
qualsiasi dispositivo) /dev/dsp, la sua forza stava nel fatto di
scrivere dati su questo file come se fosse un qualunque altro file, e
di leggerli: la prima operazione riproduceva un sonoro, la seconda
permetteva la registrazione. Oggi
OSS non viene più distribuito su Ubuntu e il device a caratteri
/dev/dsp non c'è più fisicamente, ma se non sbaglio (devo
informarmi di più su questo argomento) ALSA dovrebbe mantenere una
retrocompatibilità con OSS.
Setting up ALSA (preparazione)
Per
utilizzare ALSA dobbiamo scaricare i file di sviluppo (header e
librerie), e quindi dobbiamo installare il pacchetto libasound2-dev
scaricando i sorgenti dal sito ufficiale e compilando oppure, più
semplicemente, su Debian Ubuntu andare sul terminale e scrivere:
sudo
apt-get install libasound2-dev
che
farà tutto lui. Per compilare un'applicazione ALSA
(tipo quella che verrà trattata in questo post) basta compilare da
terminale scrivendo:
gcc
esempio.c -o esempio -lasound
quindi
lanciare il programma compilato con:
./esempio
Volendo
si può compilare anche con pkg-config se l'avete configurato, ma
visto che ALSA non vuole linkate molte librerie il suo uso anzi è
più complicato che scrivere solamente -lasound.
- Se avete installato il pacchetto con apt-get (come la riga sopra) allora avrete i file nei posti giusti e il compilatore troverà sia l'header che noi indichiamo nel file .c, che la libreria lasound
- Se avete compilato e installato manualmente dovrete indicare a gcc sia la cartella dell'header di alsa che la libreria libasound2
A
questo punto possiamo passare al codice.
Un po' di codice
Il codice è veramente breve e semplicissimo se avete già letto il vecchio post che spiega come creare l'onda sinusoidale. Creare l'onda è la difficoltà maggiore, perché una volta stabiliti i parametri del file wave, le funzioni di ALSA sono molto intuitive. Riporto per intero il codice:
#include
<stdio.h>
#include
<alsa/asoundlib.h>
typedef enum {
TIME = 5,
// Durata della traccia (secondi)
CHANNELS =
1, // Numero dei canali
SAMPLE_RATE
= 44100, // Frequenza di campionamento (Hz)
BITS_PER_SAMPLE
= 8, // Bits per campione
BLOCK_ALIGN
= CHANNELS * BITS_PER_SAMPLE / 8, // Allineamento del blocco
BYTES_PER_SECOND
= SAMPLE_RATE * BLOCK_ALIGN, // Frequenza di campionamento in Hz
SAMPLES =
CHANNELS * SAMPLE_RATE * TIME, // Numero di campioni richiesti per la
durata scelta
} WAVE;
int main() {
/*
* Creo
puntatore con l'onda sinusoidale
*/
char* ptr;
int
tone = 440; //440 Hertz
float
volume = 10.0f;
float
pulsazione = ( 2.0f * 3.14f / (float) SAMPLE_RATE / CHANNELS ) *
(float) tone;
int i;
if ((ptr =
(char*)malloc(SAMPLES))==NULL){
fprintf
(stderr,"Si e' verificato un errore!\n");
return
EXIT_FAILURE;
}
for (i=0 ;
i < SAMPLES; i++ )
ptr[i]
= (char) ( cos( (float) i * pulsazione ) * volume + 128.0f );
/*
*
Inzializzo ALSA
*/
snd_pcm_t
*handle; //handle del
device
int err;
err
= snd_pcm_open(&handle, "default",
SND_PCM_STREAM_PLAYBACK, SND_PCM_ASYNC); //apro
il device
if (err<0){
fprintf
(stderr,"Si e' verificato un errore!\n");
return
err;
}
err =
snd_pcm_set_params(handle,SND_PCM_FORMAT_U8
,SND_PCM_ACCESS_RW_INTERLEAVED,CHANNELS,SAMPLE_RATE,0,0);
if (err<0){
fprintf
(stderr,"Si e' verificato un errore!\n");
return
err;
}
/*
* invio
dati
*/
snd_pcm_writei(handle,ptr,SAMPLES);
snd_pcm_close(handle);
}
err = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, SND_PCM_ASYNC);
assegniamo
apriamo il device sonoro di default, conservando il suo handle in
“handle”, specificando che apriamo il device per la riproduzione
(SND_PCM_STREAM_PLAYBACK), in maniera asincrona (SND_PCM_ASYNC) anche
se non ce ne facciamo niente del fatto che lo apriamo in maniera
asincrona perché dovremmo specificare la funzione di callback per
ricevere asincronicamente dei segnali (non è argomento di questo
post).
Con
l'instruzione
err
= snd_pcm_set_params(handle,SND_PCM_FORMAT_U8
,SND_PCM_ACCESS_RW_INTERLEAVED,CHANNELS,SAMPLE_RATE,0,0);
settiamo
i parametri necessari per la riproduzione sul device “handle” in
particolare gli diciamo che il formato dei samples PCM è unsigned 8
bit (ovvero il char), specificando il modo di accesso
(SND_PCM_ACCESS_RW_INTERLEAVED, adatto per quello che dobbiamo fare),
il numero di canali, la frequenza di campionamento, se ALSA deve
ricampionare i campioni (0 per NO, 1 per SI), e la latenza della
scheda audio, che supponiamo in questa sede che sia nulla.
Per
l'invio dei campioni al device usiamo:
snd_pcm_writei(handle,ptr,SAMPLES);
dove
specifichiamo il device (handle), il puntatore ai campioni (ptr) e il
numero di sample da inviare (SAMPLES).
Rimando
a un'altra volta (magari fra due anni :P) lo sfruttamento della
modalità asincrona.
Fine.
Nessun commento:
Posta un commento