On Thu, Dec 14, 2023 at 11:41:55PM +0100, Sebastien Alaiwan wrote:
Hi,
I've been running some experiments on the Behringer UMC1820, which is a USB audio interface, supposedly class-compliant. I wrote a tiny program "alsa-sine" that simply plays silence on a given ALSA device ( please find the source for it at the bottom of this message ). This tiny program allows to specify the buffer size, and the period size ( Here, I'll always be using a buffer size of 256 ).
Here, i'm testing 5 values for the period size (the last command line argument). All period sizes work, except ... 127.
Hi Sebastien,
See https://www.alsa-project.org/wiki/FramesPeriods
Common period sizes are 2 or 3 since this is the number of times the hardware will interrupt per buffer. It's curious behavior you have found but I don't think it's a bug.
Regards, Geraldo Nascimento
See below:
$ ./alsa_sine 'hw:CARD=UMC1820,DEV=0' 256 125 buffer_size=256 (asked 256), period_size=125 (asked 125), channels=12, format=S24_3LE (24 bpp) OK
$ ./alsa_sine 'hw:CARD=UMC1820,DEV=0' 256 126 buffer_size=256 (asked 256), period_size=126 (asked 126), channels=12, format=S24_3LE (24 bpp) OK
$ ./alsa_sine 'hw:CARD=UMC1820,DEV=0' 256 127 buffer_size=256 (asked 256), period_size=127 (asked 127), channels=12, format=S24_3LE (24 bpp) [alsa_sine.c:66] error: snd_pcm_drain(pcm) : Input/output error Aborting.
$ ./alsa_sine 'hw:CARD=UMC1820,DEV=0' 256 128 buffer_size=256 (asked 256), period_size=128 (asked 128), channels=12, format=S24_3LE (24 bpp) OK
$ ./alsa_sine 'hw:CARD=UMC1820,DEV=0' 256 129 buffer_size=256 (asked 256), period_size=128 (asked 129), channels=12, format=S24_3LE (24 bpp) OK
I also own a Virus TI, which also implements an USB audio interface, also supposedly class-compliant. So I ran the same test on it, and interestingly, there's no error:
$ ./alsa_sine 'hw:CARD=TI,DEV=0' 256 125 buffer_size=256 (asked 256), period_size=125 (asked 125), channels=2, format=S16_LE (16 bpp) OK
$ ./alsa_sine 'hw:CARD=TI,DEV=0' 256 126 buffer_size=256 (asked 256), period_size=126 (asked 126), channels=2, format=S16_LE (16 bpp) OK
$ ./alsa_sine 'hw:CARD=TI,DEV=0' 256 127 buffer_size=256 (asked 256), period_size=127 (asked 127), channels=2, format=S16_LE (16 bpp) OK
$ ./alsa_sine 'hw:CARD=TI,DEV=0' 256 128 buffer_size=256 (asked 256), period_size=128 (asked 128), channels=2, format=S16_LE (16 bpp) OK
$ ./alsa_sine 'hw:CARD=TI,DEV=0' 256 129 buffer_size=256 (asked 256), period_size=128 (asked 129), channels=2, format=S16_LE (16 bpp) OK
So at this point, I'm wondering if I might be doing something wrong. I'm not trying to achieve anything here other than trying to understand what's going on. Maybe the UMC1820 doesn't support a period size of 127 ? ( does it even make sense to say this? ) If so, how is it possible for an application to know about this limitation? Maybe my test program is simply incorrect, and it's behavior is undefined? But then, how can we explain the difference in behavior between the UMC1820 and the Virus TI ?
My kernel version is: Linux ANTEC 6.5.0-5-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.5.13-1 (2023-11-29) x86_64 GNU/Linux Also, Debian tells me I'm using the package: libasound 1.2.10-1
BTW, please let me know if this is not the proper mailing list to talk about such things!
Thanks, Sebastien Alaiwan
//--------------------------------------------------------------- // gcc alsa_sine.c -lasound #include <alsa/asoundlib.h> #include <stdio.h>
const int SampleRate = 48000;
void checkCall(int ret, const char* expr, const char* file, int line) { if(ret < 0) { fprintf(stderr, "[%s:%d] error: %s : %s\nAborting.\n", file, line, expr, snd_strerror(ret)); exit(1); } }
#define CHECK_CALL(a) checkCall(a, # a, __FILE__, __LINE__)
int main(int argc, char* argv[]) { if(argc != 4) { fprintf(stderr, "Usage: %s <device> <buffer_size> <period_size>\n", argv[0]); return 1; }
const char* devName = argv[1]; const snd_pcm_uframes_t asked_buffer_size = atoi(argv[2]); const snd_pcm_uframes_t asked_period_size = atoi(argv[3]);
snd_pcm_t* pcm = NULL; CHECK_CALL(snd_pcm_open(&pcm, devName, SND_PCM_STREAM_PLAYBACK, 0));
snd_pcm_hw_params_t* hw_params = NULL; CHECK_CALL(snd_pcm_hw_params_malloc(&hw_params)); CHECK_CALL(snd_pcm_hw_params_any(pcm, hw_params));
snd_pcm_format_t fmt; unsigned int channels = 0; CHECK_CALL(snd_pcm_hw_params_get_format(hw_params, &fmt)); CHECK_CALL(snd_pcm_hw_params_get_channels_min(hw_params, &channels));
snd_pcm_uframes_t buffer_size = asked_buffer_size; snd_pcm_uframes_t period_size = asked_period_size; CHECK_CALL(snd_pcm_hw_params_set_rate(pcm, hw_params, SampleRate, 0)); CHECK_CALL(snd_pcm_hw_params_set_access(pcm, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)); CHECK_CALL(snd_pcm_hw_params_set_buffer_size_near(pcm, hw_params, &buffer_size)); CHECK_CALL(snd_pcm_hw_params_set_period_size_near(pcm, hw_params, &period_size, NULL)); CHECK_CALL(snd_pcm_hw_params(pcm, hw_params)); snd_pcm_hw_params_free(hw_params);
printf("buffer_size=%d (asked %d), ", (int)buffer_size, (int)asked_buffer_size); printf("period_size=%d (asked %d), ", (int)period_size, (int)asked_period_size); printf("channels=%d, ", channels); printf("format=%s (%d bpp)\n", snd_pcm_format_name(fmt), snd_pcm_format_width(fmt));
const int totalSamples = SampleRate * 1.0;
int bitsPerSample = snd_pcm_format_width(fmt); assert(bitsPerSample % 8 == 0); int size = totalSamples * channels * bitsPerSample / 8; uint8_t* buffer = (uint8_t*)malloc(size); memset(buffer, 0, size); CHECK_CALL(snd_pcm_writei(pcm, buffer, totalSamples)); free(buffer);
CHECK_CALL(snd_pcm_drain(pcm)); snd_pcm_close(pcm);
printf("OK\n"); return 0; } //---------------------------------------------------------------