[alsa-devel] How to play a sine wave in alsa?
Timo Schneider
timo.schneider at s2004.tu-chemnitz.de
Tue May 4 00:07:11 CEST 2010
Hello!
I am trying to use alsa to play a sine wave in async mode. Everything
seems to work fine, except what I am hearing is _not_ a sine wave. I am
checking for XRUNs, but none occur, so I guess the problem is that I am
supplying the wrong sample data to alsa.
I want the tone only on one channel, that seems to work fine the way i
do it, so i guess my assumptions on how the interleaved mode works are
not completely wrong.
Can anyone give me a hint on what I am doing wrong in the example
program below? I already checked the example in tests/pcm.c but it
didn't really help me.
Regards,
Timo
Example Program (compile with cc test.c -o test -lasound):
#include <math.h>
#include <stdlib.h>
#include <inttypes.h>
#include <alsa/asoundlib.h>
struct sound_settings {
// alsa parameters
snd_pcm_t *pcm_handle;
snd_pcm_hw_params_t *hw_params;
snd_pcm_sw_params_t *sw_params;
snd_async_handler_t *pcm_callback;
snd_pcm_uframes_t buffer_size;
snd_pcm_uframes_t period_size;
unsigned int sample_rate;
char *sample_buffer;
size_t sample_buffer_size;
// user settings made in gui
char *cardname;
char *modename;
int startfreq;
int endfreq;
int duration;
int channels;
// keep track of gnerated samples
long int actsample;
long int lastsample;
};
struct sound_settings sdata;
void generate_samples(struct sound_settings *sdata) {
int i = 0;
if (sdata->channels == 1) i=0;
if (sdata->channels == 2) i=1;
if (sdata->channels == 3) i=0;
if (strcmp(sdata->modename, "Sine")==0) {
while (i<(sdata->sample_buffer_size / 8)) {
int samplerate = 44100;
int freq = sdata->startfreq;
double sample = sin(sdata->actsample * (double) 2*M_PI / ((double)
samplerate / (double)freq));
int16_t rsamp = sample * 65535;
sdata->actsample++;
*(((int16_t*) (sdata->sample_buffer))+i) = rsamp;
i += 2;
}
}
}
void MyCallback(snd_async_handler_t *pcm_callback) {
snd_pcm_t *pcm_handle = snd_async_handler_get_pcm(pcm_callback);
snd_pcm_sframes_t avail;
struct sound_settings *sdata = (struct sound_settings*)
snd_async_handler_get_callback_private(pcm_callback);
avail = snd_pcm_avail_update(pcm_handle);
if (avail == -EPIPE) {
snd_pcm_prepare (pcm_handle);
printf("Underrun occured\n");
}
while (avail >= sdata->period_size) {
generate_samples(sdata);
snd_pcm_writei(pcm_handle, sdata->sample_buffer, sdata->period_size);
avail = snd_pcm_avail_update(pcm_handle);
}
}
void init_alsa(struct sound_settings *sdata) {
int rc;
// set some sound_settings data (i.e., sample_rate) to sensible
defaults
sdata->buffer_size = 1024;
sdata->period_size = 64;
sdata->sample_rate = 44100;
// Open PCM device for playback
rc = snd_pcm_open(&(sdata->pcm_handle), "default",
SND_PCM_STREAM_PLAYBACK, 0);
if (rc < 0) fprintf(stderr, "unable to open pcm device: %s\n",
snd_strerror(rc));
// Allocate a hardware parameters object
snd_pcm_hw_params_malloc (&(sdata->hw_params));
snd_pcm_hw_params_any(sdata->pcm_handle, sdata->hw_params);
snd_pcm_hw_params_set_access(sdata->pcm_handle, sdata->hw_params,
SND_PCM_ACCESS_RW_INTERLEAVED);
snd_pcm_hw_params_set_format(sdata->pcm_handle, sdata->hw_params,
SND_PCM_FORMAT_S16_LE);
snd_pcm_hw_params_set_channels(sdata->pcm_handle, sdata->hw_params, 2);
snd_pcm_hw_params_set_rate_near(sdata->pcm_handle, sdata->hw_params,
&(sdata->sample_rate), NULL);
snd_pcm_hw_params_set_buffer_size_near(sdata->pcm_handle,
sdata->hw_params, &(sdata->buffer_size));
snd_pcm_hw_params_set_period_size_near (sdata->pcm_handle,
sdata->hw_params, &(sdata->period_size), NULL);
rc = snd_pcm_hw_params(sdata->pcm_handle, sdata->hw_params);
if (rc < 0) fprintf(stderr, "unable to set hw parameters: %s\n",
snd_strerror(rc));
// tell alsa we want to work asynchronously
snd_pcm_sw_params_malloc(&(sdata->sw_params));
snd_pcm_sw_params_current(sdata->pcm_handle, sdata->sw_params);
snd_pcm_sw_params_set_start_threshold(sdata->pcm_handle,
sdata->sw_params, sdata->buffer_size - sdata->period_size);
snd_pcm_sw_params_set_avail_min(sdata->pcm_handle, sdata->sw_params,
sdata->period_size);
rc = snd_pcm_sw_params(sdata->pcm_handle, sdata->sw_params);
if (rc < 0) fprintf(stderr, "unable to set sw parameters: %s\n",
snd_strerror(rc));
// Use a buffer large enough to hold two periods
snd_pcm_hw_params_get_period_size(sdata->hw_params,
&(sdata->period_size), NULL);
sdata->sample_buffer_size = sdata->period_size * 8; // 2 bytes/sample,
2 channels, two periods
sdata->sample_buffer = (char *) malloc(sdata->sample_buffer_size);
generate_samples(sdata);
// prepare dev so that we can write
snd_pcm_prepare(sdata->pcm_handle);
/* fill buffer with some data */
generate_samples(sdata);
/* write first chunk of data */
rc = snd_pcm_writei(sdata->pcm_handle, sdata->sample_buffer,
sdata->period_size);
if (rc == -EPIPE) snd_pcm_prepare(sdata->pcm_handle);
/* register callback */
rc = snd_async_add_pcm_handler(&(sdata->pcm_callback),
sdata->pcm_handle, MyCallback, sdata);
snd_pcm_start(sdata->pcm_handle);
}
int main(int argc, char **argv) {
sdata.channels = 1;
sdata.modename = "Sine";
sdata.startfreq = 220;
sdata.actsample = 0;
init_alsa(&sdata);
static int i=0;
while (i<1000000) {
i++;
sleep(1);
}
return 0;
}
More information about the Alsa-devel
mailing list