[alsa-devel] snd_pcm_avail_update returns bogus values with the OSS PCM plugin
Hi.
For the attached test case snd_pcm_avail_update returns values higher than the buffer size when run with the OSS PCM plugin. Output of alsa-info.sh is at http://www.alsa-project.org/db/?f=77a873e55d5d6ca50d67a035a9d6b7e10cca179d .
--- alsa_oss_pcm_avail_bug.c --- /* * Public domain test case to illustrate a bug in the ALSA OSS PCM plugin * where snd_pcm_avail_update returns bogus values. */
#include <alsa/asoundlib.h> #include <stdio.h> #include <stdlib.h> #include <string.h>
static short buf[803 * 2]; static snd_pcm_t *pcm_handle = NULL;
void close_pcm(void) { if (pcm_handle) snd_pcm_close(pcm_handle);
pcm_handle = NULL; }
static int set_pcm_parameters(void) { snd_pcm_hw_params_t *hwparams; snd_pcm_hw_params_alloca(&hwparams);
if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) { fprintf(stderr, "snd_pcm_hw_params_any failed.\n"); return -1; }
if (snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) { fprintf(stderr, "snd_pcm_hw_params_set_access failed.\n"); return -1; }
if (snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16) < 0) { fprintf(stderr, "snd_pcm_hw_params_set_format failed.\n"); return -1; }
{ unsigned rate = 48000;
if (snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &rate, 0) < 0) { fprintf(stderr, "snd_pcm_hw_params_set_rate_near failed.\n"); return -1; } }
if (snd_pcm_hw_params_set_channels(pcm_handle, hwparams, 2) < 0) { fprintf(stderr, "snd_pcm_hw_params_set_channels failed.\n"); return -1; }
{ unsigned ulatency = 100000;
if (snd_pcm_hw_params_set_buffer_time_near(pcm_handle, hwparams, &ulatency, 0) < 0) { fprintf(stderr, "snd_pcm_hw_params_set_buffer_time_near failed.\n"); return -1; } }
if (snd_pcm_hw_params(pcm_handle, hwparams) < 0) { fprintf(stderr, "snd_pcm_hw_params failed.\n"); return -1; }
{ snd_pcm_uframes_t bSize = 0;
if (snd_pcm_hw_params_get_buffer_size(hwparams, &bSize) < 0) { fprintf(stderr, "snd_pcm_hw_params_get_buffer_size failed\n"); return -1; }
printf("buffer_size: %u\n", (unsigned int) bSize); }
return 0; }
static void test_loop(int n) { while (n--) { snd_pcm_sframes_t sframes = snd_pcm_avail_update(pcm_handle); printf("snd_pcm_avail: %d\n", (int) sframes); printf("writing %d frames\n", (int) sizeof(buf) / 4); sframes = snd_pcm_writei(pcm_handle, buf, sizeof(buf) / 4);
if (sframes != sizeof(buf) / 4) { if (sframes < 0) snd_pcm_prepare(pcm_handle);
printf("snd_pcm_writei returned %d\n", (int) sframes); } } }
int main(int argc, char **argv) { atexit(close_pcm);
if (argc < 2) { printf("usage: %s <ALSA pcm device>\n", argv[0]); return 0; }
if (snd_pcm_open(&pcm_handle, argv[1], SND_PCM_STREAM_PLAYBACK, 0) < 0) { fprintf(stderr, "Error opening PCM device %s\n", argv[1]); pcm_handle = NULL; return -1; }
if (set_pcm_parameters() < 0) return -1;
memset(buf, 0, sizeof(buf));
test_loop(500);
return 0; }
-- Sindre Aamås
At Fri, 10 Jul 2009 10:29:55 +0200, Sindre Aamås wrote:
Hi.
For the attached test case snd_pcm_avail_update returns values higher than the buffer size when run with the OSS PCM plugin. Output of alsa-info.sh is at http://www.alsa-project.org/db/?f=77a873e55d5d6ca50d67a035a9d6b7e10cca179d .
This is obviously a bug in OSS plugin (who thought of using this plugin seriously? :)
The oneliner below should fix the problem.
thanks,
Takashi
---
Quoting Takashi Iwai tiwai@suse.de:
At Fri, 10 Jul 2009 10:29:55 +0200, Sindre Aamås wrote:
Hi.
For the attached test case snd_pcm_avail_update returns values higher than the buffer size when run with the OSS PCM plugin. Output of alsa-info.sh is at http://www.alsa-project.org/db/?f=77a873e55d5d6ca50d67a035a9d6b7e10cca179d .
This is obviously a bug in OSS plugin (who thought of using this plugin seriously? :)
The oneliner below should fix the problem.
thanks,
Takashi
Thanks! That largely fixes it.
There's still a problem when OSS doesn't support the requested period size, however. It tends to use a larger fragment size if the one requested is too small. The real fragment size isn't generally returned by SNDCTL_DSP_SETFRAGMENT, but needs to be queried for with SNDCTL_DSP_GETOSPACE/SNDCTL_DSP_GETISPACE.
There may not be a nice solution for this, since there's no way to get a list/range of supported fragment sizes. It's a bit troublesome though, as it seems ALSA picks a small period size if it can, and the minimum allowed by the OSS plugin is often smaller than OSS supports, which means that all clients that don't restrict the minimum period size will typically be affected.
thanks, Sindre
participants (2)
-
Sindre Aamås
-
Takashi Iwai