ALSA: non-interleaved access not supported?

alsa, c

Solution

The `hw_params_set_*` functions accept only those values that are supported by the device.

Most of the default devices (`plughw`, `default`, etc.) support automatic conversions, and thus accept all formats. `hw` devices do not.

Problem

ALSA's snd_pcm_hw_params_set_access function fails when called with SND_PCM_ACCESS_RW_NONINTERLEAVED access type, reporting that there was an invalid argument. The same code works fine with SND_PCM_ACCESS_RW_INTERLEAVED access. I tried to change the order of calls to snd_pcm_hw_params_* functions with no effect. Next I thought that my hardware might not support non-interleaved playback, but according to this post ALSA subsystem will interleave non-interleaved data before sending it to the hardware if it does not support non-interleaved playback itself. Thus, non-interleaved access should always be available. Why is it then, that non-interleaved access seems not be unsupported? Following is the code that works fine for interleaved playback, but produces this problem for non-interleaved playback: ``` int err; if ((err = snd_pcm_hw_params_malloc(&hw_params)) < 0) { fprintf(stderr, "ALSA: allocate hw_params error: %s\n", snd_strerror(err)); throw 5; } if ((err = snd_pcm_hw_params_any(pb_dev, hw_params)) < 0) { fprintf(stderr, "ALSA: hw_params_any error: %s\n", snd_strerror(err)); throw 5; } if ((err = snd_pcm_hw_params_set_access(pb_dev, hw_params, (pAudioCtx->sample_fmt < AV_SAMPLE_FMT_U8P) ? SND_PCM_ACCESS_RW_INTERLEAVED : SND_PCM_ACCESS_RW_NONINTERLEAVED)) < 0) { fprintf(stderr, "ALSA: set access type error: %s\n", snd_strerror(err)); throw 5; } if ((err = snd_pcm_hw_params_set_channels(pb_dev, hw_params, pAudioCtx->channels)) < 0) { fprintf(stderr, "ALSA: set channel count error: %s\n", snd_strerror(err)); throw 5; } snd_pcm_format_t sample_format; switch (pAudioCtx->sample_fmt) { case AV_SAMPLE_FMT_U8: case AV_SAMPLE_FMT_U8P: sample_format = SND_PCM_FORMAT_U8; break; case AV_SAMPLE_FMT_S16: case AV_SAMPLE_FMT_S16P: sample_format = SND_PCM_FORMAT_S16; break; case AV_SAMPLE_FMT_S32: case AV_SAMPLE_FMT_S32P: sample_format = SND_PCM_FORMAT_S32; break; case AV_SAMPLE_FMT_FLT: case AV_SAMPLE_FMT_FLTP: sample_format = SND_PCM_FORMAT_FLOAT; break; case AV_SAMPLE_FMT_DBL: case AV_SAMPLE_FMT_DBLP: sample_format = SND_PCM_FORMAT_FLOAT64; break; default: fprintf(stderr, "sampleformat %d is not supported\n", pAudioCtx->sample_fmt); throw 5; } if ((err = snd_pcm_hw_params_set_format(pb_dev, hw_params, sample_format)) < 0) { fprintf(stderr, "ALSA: set sample format error: %s\n", snd_strerror(err)); throw 5; } if ((err = snd_pcm_hw_params_set_rate_near(pb_dev, hw_params, (unsigned int*)&pAudioCtx->sample_rate, 0)) < 0) { fprintf(stderr, "ALSA: set sample rate error: %s\n", snd_strerror(err)); throw 5; } if ((err = snd_pcm_hw_params(pb_dev, hw_params)) < 0) { fprintf(stderr, "ALSA: set parameters error: %s\n", snd_strerror(err)); throw 5; } ``` Executing this yields following output: ``` ALSA: set access type error: Invalid argument ```

Original source