[alsa-devel] issue with sound stopping and not being able to restart

Andrew Kohlsmith (Mailing List Account) aklists at mixdown.ca
Mon Jan 18 16:04:52 CET 2010


Good morning,

I am new to ALSA development, but experienced in C. I wrote up a little test 
application which simply opens one playback and one capture device and spits 
noise into the playback and recording it on the capture device.  I cross-
correlate the data streams to determine the "depth" or latency of the pipe, 
end-to-end.

I'm using playback and capture callbacks to handle the audio.

The program seems to work reasonably well, but at seemingly random times 
either the playback or capture stream will "hiccup" (xrun) -- I am handling 
this case, but it's fairly often that the stream does NOT resume and I'm stuck 
with either no playback or no capture.

I've looked at the example programs and the documentation and I do not see 
what it is I'm doing wrong.  I'm posting my code here, hopefully someone can 
show me the error of my way.

Capture/playback setup (error checking removed):
snd_pcm_open(handle, fname, stream, SND_PCM_NONBLOCK);
snd_pcm_hw_params_malloc(hwptr);
snd_pcm_hw_params_any(h, hwp);
snd_pcm_hw_params_set_access(h, hwp, 
SND_PCM_ACCESS_RW_INTERLEAVED);
snd_pcm_hw_params_set_format(h, hwp, SND_PCM_FORMAT_S16_LE);
snd_pcm_hw_params_set_rate(h, hwp, 8000, 0);
snd_pcm_hw_params_set_channels(h, hwp, 2);
snd_pcm_hw_params_set_buffer_size(h, hwp, BUF_SIZE);
snd_pcm_hw_params(h, hwp);

At this point I have the capture or playback device set up for 16 bit signed 
linear data in interleaved format and two channels. I've set up the buffers to 
be 160 frames, or 20ms. Each of these calls is checked for errors, and I do 
verify that the rate, buffer size and buffer time are what I expect.  There is 
nothing unusual going on with these calls that I can determine.

I set up the playback and capture callbacks thusly (again checking for errors)
snd_async_add_pcm_handler(&ahp, hp, playback_callback, &pb_data);
snd_async_add_pcm_handler(&ahc, hc, capture_callback, &cap_data);

At this point I believe I'm ready to go, all I have to do is start playback 
and capture:
if((fcount = snd_pcm_writei(h[PLAYBACK], pb_data.buf, BUF_SIZE)) != BUF_SIZE) 
{
	if(fcount < 0) {
		if((e = snd_pcm_recover(h[PLAYBACK], fcount, 0)) < 0)
			fprintf(stderr, "initial: can't writei: %s\n", snd_strerror(e));
	} else {
		fprintf(stderr, "initial: short write: %d instead of %d\n", fcount, 
BUF_SIZE);
	}
}

if((e = snd_pcm_start(h[CAPTURE])) < 0) {
	fprintf(stderr, "capture prepare error: %s\n", snd_strerror(e));
	return 1;
}

Now the playback and capture handlers are called by ALSA as required.  The 
capture handler looks like this:

/* capture callback. Called by ALSA whenever a period time is up */
void capture_callback(snd_async_handler_t *ah)
{
	snd_pcm_t *h = snd_async_handler_get_pcm(ah);
	struct async_data *d = snd_async_handler_get_callback_private(ah);
	snd_pcm_sframes_t fa;
	short *p;
	int i, fc, e, faloops;
	struct timeval tv;
	static int c=0;

/* read in whatever is available */

	fa = snd_pcm_avail_update(h);
	faloops = 0;
	while(fa > 0) {
		if(fa > BUF_SIZE)
			fa = BUF_SIZE;

		if((fc = snd_pcm_readi(h, d->buf, fa)) != fa) {
			if(fc < 0) {
				if((e = snd_pcm_recover(h, e, 0)) < 0) {
					fprintf(stderr, "callback: can't readi: %s\n", 
snd_strerror(e));
				}

				fc = 0;
			} else {
				fprintf(stderr, "callback: short read: %d instead of %ld\n", fc, 
fa);
			}
		}

		for(i=0,p=&d->buf[0]; i<fc; i++) {
			*d->ptr++ = *p++;
			p++;

			if(d->ptr > &in[MAX_BUFS * (BUF_SIZE - 1)]) {
				d->ptr = (short *)&in[0];
			}
		}

		c += fc;
		if(c >= BUF_SIZE) {
			gettimeofday(&tv, NULL);
			c -= BUF_SIZE;

			in_step++;
			if(in_step >= MAX_BUFS)
				in_step = 0;
		}

		frames_cap += fc;
		fa = snd_pcm_avail_update(h);
		faloops++;
	};

	cap_faloops=faloops;
}

The playback callback is very similar:

/* playback callback. Called by ALSA whenever a period time is up */
void playback_callback(snd_async_handler_t *ah)
{
	snd_pcm_t *h = snd_async_handler_get_pcm(ah);
	struct async_data *d = snd_async_handler_get_callback_private(ah);
	snd_pcm_sframes_t fa;
	int i, fc, e, faloops;
	static int c=0;
	struct timeval tv;

	fa = snd_pcm_avail_update(h);
	faloops = 0;
	while(fa > 0) {
		if(fa > BUF_SIZE)
			fa = BUF_SIZE;

		for(i=0; i<fa; i++) {
			d->buf[i*2] = awgn(&noise_source);
			d->buf[i*2+1] = awgn(&noise_source);
		}

		if((fc = snd_pcm_writei(h, d->buf, fa)) != fa) {
			if(fc < 0) {
				if((e = snd_pcm_recover(h, e, 0)) < 0) {
					fprintf(stderr, "callback: can't writei: error %x %s\n", e, 
snd_strerror(e));
				}

				fc = 0;
			} else {
				fprintf(stderr, "callback: short write: %d instead of %ld\n", fc, 
fa);
			}
		}

		for(i=0; i<fc; i++) {
			*d->ptr++ = d->buf[i*2];
			if(d->ptr > &out[MAX_BUFS * (BUF_SIZE - 1)]) {
				d->ptr = (short *)&out[0];
			}
		}

		c += fc;
		if(c >= BUF_SIZE) {
			gettimeofday(&tv, NULL);
			c -= BUF_SIZE;
			out_step++;
			if(out_step >= MAX_BUFS)
				out_step = 0;
		}

		frames_pb += fc;
		fa = snd_pcm_avail_update(h);
		faloops++;
	};

	pb_faloops = faloops;
}

That's really it; the main loop keeps track of the in_step and out_step 
variables and does the cross-correlation as the data comes in.  Now it seems 
that it's the capture playback that is getting xruns, but I when capture xruns 
occur my playback callback seems to stop, and sometimes the entire program 
just hangs as if it is waiting for ALSA to call one of the callbacks.

Can anyone shed any light on this? I'm certain it's something very simple and 
braindead, but I can't find it.

Regards,
Andrew


More information about the Alsa-devel mailing list