Hi all,
I'm trying to play sampled data on my RME Digi96/8 PAD card using
the plughw:0,0 plugin. It works (of course it does) but not with
a sampling rate that is not supported by the hardware. My test
program (see attachment) bombs out due to an assertion failure:
pcm_plugin.c:525: snd_pcm_plugin_mmap_commit: Assertion `size == 0' failed.
This is the snd_pcm_plugin_mmap_commit() routine in alsa-lib
pcm_plugin.c:
snd_pcm_sframes_t snd_pcm_plugin_mmap_commit(pcm, offset, size)
{
.
.
slave_size = snd_pcm_avail_update(slave);
while (size > 0 && slave_size > 0) {
.
.
.
size -= frames;
slave_size -= frames;
}
assert(size == 0);
}
After studying this routine I decided to uncomment the assert()
because to it seems to me the assertion will always fail if
size and slave_size are not the same before entering the while
loop.
After commenting out the assert() the test program runs fine, as
far as I can tell. I presume however there is a reason for this
assertion and probably it shouldn't be removed. But in that case
I would like to know what else could be wrong.
Regards,
Theo
/*
prob.c
This test program bombs out with assertion failure
pcm_plugin.c:525: snd_pcm_plugin_mmap_commit: Assertion `size == 0' failed.
when sample rate is set to a value not supported by the card (32000,
48000 OK, 16000, 11025 etc not OK).
Sound card RME Digi96/8 PAD
ALSA 0.9.6
plugin plughw:0,0
gcc -o prob prob.c -lasound
*/
#include <stdio.h>
#include <math.h>
#define ALSA_PCM_NEW_HW_PARAMS_API
#define ALSA_PCM_NEW_SW_PARAMS_API
#include <alsa/asoundlib.h>
#define BUFFER_TIME 500 // [ms]
snd_pcm_t* handle;
unsigned int nchannels;
unsigned int samplerate;
unsigned int periodsize;
unsigned int buffersize;
unsigned int nperiods;
int setHWParams(snd_pcm_hw_params_t* hwparams, unsigned int nch, unsigned int rate)
{
int err;
snd_pcm_uframes_t val;
// Choose all hardware parameters.
err = snd_pcm_hw_params_any(handle, hwparams);
if (err < 0) {
fprintf(stderr, "No playback configurations available: %s\n",
snd_strerror(err));
return -1;
}
// Set access type: interleaved
err = snd_pcm_hw_params_set_access(handle, hwparams,
SND_PCM_ACCESS_RW_INTERLEAVED);
if (err < 0) {
fprintf(stderr, "Interleaved access not available: %s\n", snd_strerror(err));
return -1;
}
// Set sample format: float
err = snd_pcm_hw_params_set_format(handle, hwparams, SND_PCM_FORMAT_FLOAT);
if (err < 0) {
fprintf(stderr, "Float sample format not available: %s\n", snd_strerror(err));
return -1;
}
// Set number of channels: nch
nchannels = nch;
err = snd_pcm_hw_params_set_channels(handle, hwparams, nch);
if (err < 0) {
fprintf(stderr, "Unsupported number of channels (%u): %s\n", nch,
snd_strerror(err));
return -1;
}
// Set sampling rate: rate
samplerate = rate;
err = snd_pcm_hw_params_set_rate_near(handle, hwparams, &rate, 0);
if (err < 0) {
fprintf(stderr, "Unsupported sampling rate (%u Hz): %s\n", rate,
snd_strerror(err));
return -1;
}
if (rate != samplerate) {
fprintf(stderr, "Requested sampling rate %u Hz, got %u Hz.\n", samplerate,
rate);
}
samplerate = rate;
// Set period size: lowest supported value
snd_pcm_hw_params_get_period_size_min(hwparams, &val, 0);
err = snd_pcm_hw_params_set_period_size_near(handle, hwparams, &val, 0);
if (err < 0) {
fprintf(stderr, "Unable to set period size to %u: %s\n", (int)val,
snd_strerror(err));
return -1;
}
err = snd_pcm_hw_params_get_period_size(hwparams, &val, 0);
if (err > 0) {
fprintf(stderr, "Unable to get period size: %s\n", snd_strerror(err));
return -1;
}
periodsize = val;
// Set number of periods: about BUFFER_TIME / periodtime
nperiods = (BUFFER_TIME * samplerate / 1000) / periodsize;
err = snd_pcm_hw_params_set_periods_near(handle, hwparams, &nperiods, 0);
if (err < 0) {
fprintf(stderr, "Unable to set number of periods to %u: %s\n", nperiods,
snd_strerror(err));
return -1;
}
err = snd_pcm_hw_params_get_periods(hwparams, &nperiods, 0);
if (err > 0) {
fprintf(stderr, "Unable to get number of periods: %s\n", snd_strerror(err));
return -1;
}
// Get buffer size.
err = snd_pcm_hw_params_get_buffer_size(hwparams, &val);
if (err < 0) {
fprintf(stderr, "Unable to get buffer size: %s\n", snd_strerror(err));
return -1;
}
buffersize = val;
// Write hardware parameters to device.
err = snd_pcm_hw_params(handle, hwparams);
if (err < 0) {
fprintf(stderr, "Unable to set hardware parameters: %s\n", snd_strerror(err));
return -1;
}
printf("num channels: %u\n", nchannels);
printf("sample rate: %u\n", samplerate);
printf("buffer size: %u\n", buffersize);
printf("period size: %u\n", periodsize);
printf("num periods: %u\n", nperiods);
return 0;
}
int setSWParams(snd_pcm_sw_params_t* swparams)
{
int err;
// Get current software parameters.
err = snd_pcm_sw_params_current(handle, swparams);
if (err < 0) {
fprintf(stderr, "Unable to determine current software parameters: %s\n",
snd_strerror(err));
return -1;
}
// Start transfer when one complete period is available.
err = snd_pcm_sw_params_set_start_threshold(handle, swparams, periodsize);
if (err < 0) {
fprintf(stderr, "Unable to set start threshold: %s\n", snd_strerror(err));
return -1;
}
// Allow transfer when at least one period is available.
err = snd_pcm_sw_params_set_avail_min(handle, swparams, periodsize);
if (err < 0) {
fprintf(stderr, "Unable to set min avail: %s\n", snd_strerror(err));
return -1;
}
// Align all transfers to 1 sample.
err = snd_pcm_sw_params_set_xfer_align(handle, swparams, 1);
if (err < 0) {
fprintf(stderr, "Unable to set transfer align: %s\n", snd_strerror(err));
return -1;
}
// Write software parameters to device.
err = snd_pcm_sw_params(handle, swparams);
if (err < 0) {
fprintf(stderr, "Unable to set software parameters: %s\n", snd_strerror(err));
return -1;
}
return 0;
}
int openPCM(unsigned int nch, unsigned int rate)
{
int err;
snd_pcm_hw_params_t* hwparams;
snd_pcm_sw_params_t* swparams;
if ((err = snd_pcm_open(&handle, "plughw:0,0", SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
fprintf(stderr, "Playback open error: %s\n", snd_strerror(err));
return err;
}
snd_pcm_hw_params_malloc(&hwparams);
err = setHWParams(hwparams, nch, rate);
snd_pcm_hw_params_free(hwparams);
if (err) {
snd_pcm_close(handle);
return err;
}
snd_pcm_sw_params_malloc(&swparams);
err = setSWParams(swparams);
snd_pcm_sw_params_free(swparams);
if (err) {
snd_pcm_close(handle);
return err;
}
return 0;
}
void play(const float* data, unsigned int nframes)
{
int xrun = 0;
while (nframes > 0) {
int cnt = snd_pcm_writei(handle, data, nframes);
// printf("cnt = %d\n", cnt);
if (cnt < 0) {
if (cnt == -EAGAIN) continue;
if (cnt == -EPIPE) {
snd_pcm_prepare(handle);
if (++xrun <= 1) continue;
}
fprintf(stderr, "Error playing sampled data: %s.\n", snd_strerror(cnt));
break;
}
data += cnt * nchannels;
nframes -= cnt;
}
}
int main(void)
{
double Fs, Ts, t = 0.0, amp = 0.9;
int NS, n;
float* s;
if (openPCM(1, 16000) != 0) return -1;
Fs = samplerate;
Ts = 1.0 / Fs;
NS = (int)(1.0 * Fs + 0.5);
s = (float*)malloc(NS * sizeof(float));
for (n = 0; n < NS; n++) {
s[n] = amp * sin(2.0 * M_PI * 1000.0 * t);
t = t + Ts;
}
play(s, NS);
snd_pcm_drain(handle);
snd_pcm_close(handle);
return 0;
}