I am having trouble getting alsa to output clean audio, every thing I
have tried so far has resulted in very broken sound.

I have attached the latest attempt.

What I am trying to do (in write_alsa_output) is wait till the buffer is
empty, fill it, and keep doing this till there is no more data.
output_data is 16bit signed stereo data, output_size is the count in
byte.

I do have an oss version of this but it doesn't work with alsa's oss 
emulation on my stsyem.

The oss version has a look at where the driver is reading from in the
buffer, and if there is enough space between the read and write poiters
it writes the data, without overwriting the drivers read position, and
does this till there is no more.

I tried a simular thing with alsa but failed misrably. ie: see how many
frames are available, write to those frames, commit those frames ... but
this was the wrong approach and also resulted in broken audio output.
What I need is to know where alsa is reading from in the buffer, so I
can write behind it.

This needs to be non-blocking and mmap'd due to the whole program taking
advantage of the delay between writing to the buffer and actual output
to do heavier loads at times and catch up when the load aint so heavey.

Please can you point out where I have gone wrong, and possable
solutions.

ps: I tried that wave generator example in the source, but I ended up
getting the same broken sound result.

Hope you can help.

Thankx in advance
Chris Ison      
char *buffer = NULL;
unsigned long int max_buffer;
unsigned long int buffer_pos;
static snd_pcm_t  *pcm;

int write_alsa_output (char * output_data, int output_size);
void close_alsa_output ( void );

int
open_alsa_output(void) {
	snd_pcm_hw_params_t     *hw;
	snd_pcm_sw_params_t     *sw;
	int err, frag_size;
	const snd_pcm_channel_area_t *areas;
	snd_pcm_uframes_t offset;
	snd_pcm_uframes_t nframes = 2;
			
	
	snd_pcm_hw_params_alloca (&hw);
	snd_pcm_sw_params_alloca (&sw);
	
	if ((err = snd_pcm_open (&pcm, "plughw:0,0", SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) {
		printf("Error: audio open error: %s\n", snd_strerror (err));
		return -1;
	}
	
	snd_pcm_hw_params_any (pcm, hw);
	
	if (snd_pcm_hw_params_set_rate_near(pcm, hw, rate, 0) < 0) {
		printf("ALSA does not support %iHz for your soundcard\n",rate);
		close_alsa_output();
		return -1;
	}
	frag_size = 64 * rate / 11025;
	if (snd_pcm_hw_params_set_format (pcm, hw, SND_PCM_FORMAT_S16) < 0) {
		printf("ALSA does not support 16bit signed audio for your soundcard\n",rate);
		close_alsa_output();
		return -1;
	}
	if (snd_pcm_hw_params_set_access (pcm, hw, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0) {
		printf ("ALSA: interleaved is not supported\n");
		close_alsa_output();
		return -1;
	}
	if (snd_pcm_hw_params_set_channels (pcm, hw, 2) < 0) {
		printf("ALSA does not support stereo for your soundcard\n");
		close_alsa_output();
		return -1;
	}
	snd_pcm_hw_params_set_period_size_near (pcm, hw, frag_size, 0);
	if ((err = snd_pcm_hw_params (pcm, hw)) < 0) {
		printf ("ALSA: unable to install hw params\n");
		close_alsa_output();
		return -1;
	}
	snd_pcm_sw_params_current (pcm, sw);
	snd_pcm_sw_params_set_start_threshold (pcm, sw, ~0U);
	snd_pcm_sw_params_set_stop_threshold (pcm, sw, ~0U);

	if ((err = snd_pcm_sw_params (pcm, sw)) < 0) {
		printf ("ALSA: unable to install sw params\n");
		close_alsa_output();
		return -1;
	}
	max_buffer = snd_pcm_hw_params_get_buffer_size (hw) * 2;
	snd_pcm_avail_update (pcm);
	snd_pcm_mmap_begin (pcm, &areas, &offset, &nframes);
	buffer = areas->addr;
	buffer_pos = offset + 4;

	send_output = write_alsa_output;
	close_output = close_alsa_output;
	return 0;			
}

int 
write_alsa_output (char * output_data, int output_size) {
	const snd_pcm_channel_area_t *areas;
	snd_pcm_uframes_t offset = 0;
	snd_pcm_uframes_t nframes = 0, avail = 0;
	int data_pos = 0;
	int count;
	int state;

	while (output_size) {
		if (output_size > (max_buffer - buffer_pos)) {
			count = max_buffer - buffer_pos;
		} else {
			count = output_size;
		}
		memcpy(&buffer[buffer_pos], &output_data[data_pos], count);
		buffer_pos += count;
		output_size -= count;
		data_pos += count;
		if (buffer_pos >= max_buffer) {
			buffer_pos = 0;
			while (nframes < (max_buffer / 4)) {
				nframes = snd_pcm_avail_update (pcm);
				usleep (5000);
			}
			snd_pcm_mmap_begin (pcm, &areas, &offset, &nframes);
			state = snd_pcm_state (pcm);
			switch (state) {
				case SND_PCM_STATE_PREPARED:
					snd_pcm_mmap_commit (pcm, offset, nframes);
					snd_pcm_start (pcm);
					break;
				case SND_PCM_STATE_RUNNING:
					snd_pcm_mmap_commit (pcm, offset, nframes);
					break;
				default:
					break;
			}
		}
	}
}

void 
close_alsa_output ( void ) {
	snd_pcm_close (pcm);
}

Reply via email to