[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