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; }