Takashi Iwai wrote:

[...]
 
>so far, so good..
>
>> calling snd_pcm_close() takes a few seconds during which i see
>> this:
>> 
>> [~] cat /proc/asound/ice/pcm0p/sub0/status 
>> state: DRAINING
>> trigger_time: 1023125380.999721
>> tstamp      : 1023125381.905192
>> delay       : -39746
>> avail       : 40002
>> avail_max   : 40002
>> -----
>> hw_ptr      : 40002
>> appl_ptr    : 256
>
>the minus delay value is definitely wrong.
>something weird goes there...
>could you check sw_params during this happens?

yes i could, but i won't if you don't insist (they don't change anyway
iirc). instead i have done some more investigations:

remember capture + play are linked, and audio data has been written. 
now draining the playback stream *always* fails in the way already
described.

consequences: if you close capture first and then playback, things
work ok. if you unlink the streams before closing them, things work
ok, and the order of closing does not matter.

i have attached testing boilerplate code that you can use to 
reproduce the error.

tim

ps: now asking for the third time, why is it necessary to 'drain' a
playback stream if it is about to be closed? 
/*
	 ALSA stream boilerplate, tabsize is 2.
	 
	 (c) 2002 Tim Goetze <[EMAIL PROTECTED]>

	 This program is free software; you can redistribute it and/or
	 modify it under the terms of the GNU General Public License
	 as published by the Free Software Foundation; either version 2
	 of the License, or (at your option) any later version.

	 This program is distributed in the hope that it will be useful,
	 but WITHOUT ANY WARRANTY; without even the implied warranty of
	 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	 GNU General Public License for more details.

	 You should have received a copy of the GNU General Public License
	 along with this program; if not, write to the Free Software
	 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
	 02111-1307, USA or point your web browser to http://www.gnu.org.
*/

#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

#include <alsa/asoundlib.h>

#define abs(x) ((x) < 0 ? -(x) : (x))

void
throw (int err, char * s)
{
	if (err)
		fprintf (stderr, "%s: %s\n", s, strerror (abs (err)));
	else
		fprintf (stderr, "%s\n", s);
	exit (1);
}

#define DO(what,ifnot) \
	{ \
		int error = what; \
		if (error < 0) throw (error, ifnot); \
	}

typedef struct
{
	snd_pcm_t * handle;
	
	snd_pcm_hw_params_t * hw_params;
	snd_pcm_sw_params_t * sw_params;

	int bytes_per_sample, channels;
} AudioStream;	
	
void
AudioStream_construct (AudioStream * s)
{
	int error;
	
	s->handle = 0;
	s->hw_params = 0;
	s->sw_params = 0;

	DO (snd_pcm_hw_params_malloc (&s->hw_params), "hw_params_malloc fails")
	DO (snd_pcm_sw_params_malloc (&s->sw_params), "sw_params_malloc fails")
}

void
AudioStream_destruct (AudioStream * s)
{
	if (s->handle)
		DO (snd_pcm_close (s->handle), "pcm_close fails");
	
	if (s->hw_params)
		snd_pcm_hw_params_free (s->hw_params);
	if (s->sw_params)
		snd_pcm_sw_params_free (s->sw_params);
}

void
AudioStream_open (AudioStream *s, char * name, int type)
{
	DO (snd_pcm_open (&s->handle, name, type, 0), "pcm_open fails")
}

void
AudioStream_prepare (AudioStream *s)
{
	DO (snd_pcm_prepare (s->handle), "pcm_open fails")
}

void
AudioStream_link (AudioStream *s, AudioStream *t)
{
	DO (snd_pcm_link (s->handle, t->handle), "pcm_link fails")
}

void
AudioStream_unlink (AudioStream *s)
{
	DO (snd_pcm_unlink (s->handle), "pcm_unlink fails")
}

void
AudioStream_drain (AudioStream *s)
{
	DO (snd_pcm_drain (s->handle), "pcm_drain fails")
}

void
AudioStream_configure (AudioStream * s, uint sample_rate, uint frames_per_cycle)
{
	int i;
	int error;
	int interleaved;
	int bytes_per_sample;
	int channels;

	snd_pcm_format_t formats [3] = {
		SND_PCM_FORMAT_S32,
		SND_PCM_FORMAT_S16,
		SND_PCM_FORMAT_S8
	};

	DO (snd_pcm_hw_params_any (s->handle, s->hw_params), 
		"no 'any' configuration to start with.");

	DO (snd_pcm_hw_params_set_periods_integer (s->handle, s->hw_params),
		"no integer period size.");
	
	error = snd_pcm_hw_params_set_access 
			(s->handle, s->hw_params, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
			
	if (error == 0)
		interleaved = 0;
	else
	{
		DO (snd_pcm_hw_params_set_access 
				(s->handle, s->hw_params, SND_PCM_ACCESS_MMAP_INTERLEAVED),
				"memory-mapped access method refused.");
		interleaved = 1;
	}

	/* determine sample format */
	bytes_per_sample = ~0;
	for (i = 0; i < 3; i++)
	{
		error = snd_pcm_hw_params_set_format (s->handle, s->hw_params, formats[i]);
		if (error == 0)
		{
			s->bytes_per_sample =
			bytes_per_sample = 4 >> i;
			break;
		}
	}
			
	if (bytes_per_sample > 4)
		throw (0, "none of (32, 16, 8) bit sample sizes available.");

	DO (snd_pcm_hw_params_set_rate (s->handle, s->hw_params, sample_rate, 0),
		"unsupported sample rate.");

	s->channels =
	channels = snd_pcm_hw_params_get_channels_max (s->hw_params);

	DO (snd_pcm_hw_params_set_channels (s->handle, s->hw_params, channels),
			"no channels available.");

	DO (snd_pcm_hw_params_set_period_size 
		(s->handle, s->hw_params, (uint) frames_per_cycle, 0),
		"hardware refuses this frames_per_cycle setting.");

	DO (snd_pcm_hw_params_set_periods
		(s->handle, s->hw_params, 2, 0),
		"hardware refuses 2 cycles per buffer.");

	DO (snd_pcm_hw_params_set_buffer_size 
		(s->handle, s->hw_params, 2 * frames_per_cycle),
		"hardware refuses a buffer size of 2 * frames_per_cycle.");

	DO (snd_pcm_hw_params (s->handle, s->hw_params),
		"hardware setup failed.");

	/* sw params */
	snd_pcm_sw_params_current (s->handle, s->sw_params);
	
	DO (snd_pcm_sw_params_set_start_threshold (s->handle, s->sw_params, ~0u),
		"cannot turn off start threshold.");
		
	DO (snd_pcm_sw_params_set_stop_threshold (s->handle, s->sw_params, ~0u),
		"cannot turn off stop threshold.");

	DO (snd_pcm_sw_params_set_silence_threshold (s->handle, s->sw_params, 0),
		"cannot set 0 silence threshold.");

	DO (snd_pcm_sw_params_set_silence_size (s->handle, s->sw_params, 0),
		"cannot set 0 silence size.");

	DO (snd_pcm_sw_params_set_avail_min (s->handle, s->sw_params, frames_per_cycle),
		"software setup for minimum available frames failed.");

	DO (snd_pcm_sw_params (s->handle, s->sw_params),
		"software setup failed.");

	fprintf (stderr, "configured for %d %dbit %d %s.\n",
			sample_rate, 8 * bytes_per_sample, frames_per_cycle,
			interleaved ? "interleaved" : "linear");
}

void
AudioStream_fill (AudioStream * s, uint nframes)
{
	const snd_pcm_channel_area_t * areas;
	snd_pcm_uframes_t offset, frames;
	int i;

	while (nframes)
	{
		frames = nframes;

		DO (snd_pcm_mmap_begin (s->handle, &areas, &offset, &frames),
				"pcm_mmap_begin fails");

		/* drain makes the contents of the ringbuffer audible, so we need some
		 * silence here. this quick hack is probably the most efficient sample 
		 * loop you'll ever see. ;) 
		 */
		for (i = 0; i < s->channels; ++i)
		{
			char * ptr = (char *) areas[i].addr + 
					((areas[i].first + areas[i].step * offset) >> 3);
			int n = frames;

			while (n--)
			{
				memset (ptr, 0, s->bytes_per_sample);
				ptr += areas[i].step >> 3;
			}
		}

		DO (snd_pcm_mmap_commit (s->handle, offset, frames),
				"pcm_mmap_commit fails");
		
		nframes -= frames;

		printf ("%u frames commited.\n", frames);
	}
}

int 
main (int argc, char ** argv)
{
	AudioStream capture, playback;

	/* frames / cycle, aka period_size */
	int fpc = 2048;

	if (argc != 2)
		throw (0, "give me an <alsa-pcm-device-name>.");
	
	AudioStream_construct (&capture);
	AudioStream_construct (&playback);

	AudioStream_open (&capture, argv[1], SND_PCM_STREAM_CAPTURE);
	AudioStream_open (&playback, argv[1], SND_PCM_STREAM_PLAYBACK);

	AudioStream_configure (&capture, 44100, fpc);
	AudioStream_configure (&playback, 44100, fpc);

	AudioStream_link (&capture, &playback);

	AudioStream_prepare (&playback);
	AudioStream_fill (&playback, 2 * fpc);

	printf ("hit enter to close.\n");
	fflush (stdout);
	fgetc (stdin);

	/* draining playback now makes the bug manifest. */
	#if 0
	printf ("draining.\n");
	fflush (stdout);
	AudioStream_drain (&playback);
	#endif

	/* unlinking the streams makes the bug not manifest. */
	#if 0
	printf ("unlink.\n");
	fflush (stdout);
	AudioStream_unlink (&playback);
	#endif
	
	/* reordering these two calls makes the bug not manifest because the
	 * capture stream isn't linked to playback anymore.
	 */
	printf ("closing.\n");
	fflush (stdout);
	AudioStream_destruct (&playback);
	AudioStream_destruct (&capture);

	return 0;
}

Reply via email to