[alsa-devel] Using snd_pcm_set_params() on a capture handle
Hi all,
I've got a simple PCM capture test program below, which is basically a slightly tweak version of something from:
http://equalarea.com/paul/alsa-audio.html
The program uses two different methods of setting up the capture handle; one that works (and is complex) and one that doesn't but seems like it should (and is simple). I'm trying to figure out why the simple one (using snd_pcm_set_params) doesn't work.
The program can be compiled and run in two ways:
gcc -Wall -O2 -DUSE_SET_PARAMS=1 alsa_capture.c -lasound -o alsa_capture ./alsa_capture
which fails with:
Read from audio interface failed (Input/output error)
and:
gcc -Wall -O2 -DUSE_SET_PARAMS=0 alsa_capture.c -lasound -o alsa_capture ./alsa_capture
which works as expected.
Questions:
* Is snd_pcm_set_params() supposed to work on capture handles (I've used it successfully on playback handles)?
* Is my use of snd_pcm_set_params() missing something and if so what?
* If snd_pcm_set_params() is not supposed to work on capture handles would it be possible to update the documentation to reflect that?
Thanks, Erik
//--------------------------------------------- #include <stdio.h> #include <stdlib.h>
#include <alsa/asoundlib.h> #include <alsa/pcm.h>
int main (int argc, char *argv[]) { int i; int err; char *buffer; int buffer_frames = 128; unsigned int rate = 44100; snd_pcm_t *capture_handle; snd_pcm_format_t format = SND_PCM_FORMAT_S32_LE;
char* device = "default"; if (argc > 1) device = argv[1];
if ((err = snd_pcm_open (&capture_handle, device, SND_PCM_STREAM_CAPTURE, 0)) < 0) { printf ("Cannot open audio device %s (%s)\n", device, snd_strerror (err)); exit (1) ; }
if (USE_SET_PARAMS) { if ((err = snd_pcm_set_params (capture_handle, format, SND_PCM_ACCESS_RW_INTERLEAVED, 2, rate, 0, 500000)) < 0) { /* 0.5sec */ printf ("Capture open error: %s\n", snd_strerror (err)); exit (1) ; } } else { snd_pcm_hw_params_t *hw_params;
if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) { printf ("Cannot allocate hardware parameter structure (%s)\n", snd_strerror (err)); exit (1) ; }
if ((err = snd_pcm_hw_params_any (capture_handle, hw_params)) < 0) { printf ("Cannot initialize hardware parameter structure (%s)\n", snd_strerror (err)); exit (1) ; }
if ((err = snd_pcm_hw_params_set_access (capture_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) { printf ("cannot set access type (%s)\n", snd_strerror (err)); exit (1) ; }
if ((err = snd_pcm_hw_params_set_format (capture_handle, hw_params, format)) < 0) { printf ("Cannot set sample format (%s)\n", snd_strerror (err)); exit (1) ; }
if ((err = snd_pcm_hw_params_set_rate_near (capture_handle, hw_params, &rate, 0)) < 0) { printf ("Cannot set sample rate (%s)\n", snd_strerror (err)); exit (1) ; }
if ((err = snd_pcm_hw_params_set_channels (capture_handle, hw_params, 2)) < 0) { printf ("cannot set channel count (%s)\n", snd_strerror (err)); exit (1) ; }
if ((err = snd_pcm_hw_params (capture_handle, hw_params)) < 0) { printf ("cannot set parameters (%s)\n", snd_strerror (err)); exit (1) ; }
snd_pcm_hw_params_free (hw_params); }
buffer = malloc (buffer_frames * snd_pcm_format_width(format) / 8 * 2);
if ((err = snd_pcm_prepare (capture_handle)) < 0) { printf ("cannot prepare audio interface for use (%s)\n", snd_strerror (err)); exit (1) ; }
for (i = 0; i < 10; ++i) { if ((err = snd_pcm_readi (capture_handle, buffer, buffer_frames)) != buffer_frames) { printf ("Read from audio interface failed (%s)\n", snd_strerror (err)); exit (1) ; break; } printf("read %d done\n", i); }
free(buffer);
snd_pcm_close (capture_handle); puts ("Audio interface closed");
return 0; }
Erik de Castro Lopo wrote:
- Is snd_pcm_set_params() supposed to work on capture handles?
Yes.
- Is my use of snd_pcm_set_params() missing something and if so what?
No. But it does not set exactly the same parameters as the manual hw_params calls.
Please check for differences with snd_pcm_hw_params_dump() (or look into /proc/asound/cardX/pcm0c/sub0/hw_params).
The program can be compiled and run in two ways:
gcc -Wall -O2 -DUSE_SET_PARAMS=1 alsa_capture.c -lasound -o alsa_capture ./alsa_capture
which fails with:
Read from audio interface failed (Input/output error)
Which driver? Any messages in the system log?
if ((err = snd_pcm_prepare (capture_handle)) < 0) {
This is done automatically by snd_pcm_hw_params().
if ((err = snd_pcm_readi (capture_handle, buffer, buffer_frames)) != buffer_frames) {
If less than the full number of frames have been read, you get a return value that is not an error code but a positive number.
Regards, Clemens
Clemens Ladisch wrote:
- Is my use of snd_pcm_set_params() missing something and if so what?
No. But it does not set exactly the same parameters as the manual hw_params calls.
Please check for differences with snd_pcm_hw_params_dump()
That showed some differences. The params when its working:
ACCESS: RW_INTERLEAVED FORMAT: S32_LE SUBFORMAT: STD SAMPLE_BITS: 32 FRAME_BITS: 64 CHANNELS: 2 RATE: 44100 PERIOD_TIME: (5011 5012) PERIOD_SIZE: 221 PERIOD_BYTES: 1768 PERIODS: (593 594) BUFFER_TIME: (2972154 2972155) BUFFER_SIZE: 131072 BUFFER_BYTES: 1048576 TICK_TIME: 0
and the one that doesn't:
ACCESS: MMAP_INTERLEAVED RW_INTERLEAVED FORMAT: S32_LE SUBFORMAT: STD SAMPLE_BITS: 32 FRAME_BITS: 64 CHANNELS: 2 RATE: [44100 96000] PERIOD_TIME: [125 1486078) PERIOD_SIZE: [8 65536] PERIOD_BYTES: [64 524288] PERIODS: [2 1024] BUFFER_TIME: (166 2972155) BUFFER_SIZE: [16 131072] BUFFER_BYTES: [128 1048576] TICK_TIME: ALL
which seems rather odd!
Which driver?
snd_usb_audio
Any messages in the system log?
Just the connect message when the device is plugged in:
usb 1-1: new high-speed USB device number 43 using xhci_hcd usb 1-1: New USB device found, idVendor=1235, idProduct=8016 usb 1-1: New USB device strings: Mfr=1, Product=2, SerialNumber=0 usb 1-1: Product: Scarlett 2i2 USB usb 1-1: Manufacturer: Focusrite
If less than the full number of frames have been read, you get a return value that is not an error code but a positive number.
Its definitely an error message. I changed the code to:
if ((err = snd_pcm_readi (capture_handle, buffer, buffer_frames)) < 0) { printf ("Read from audio interface failed (%d, %s)\n", err, snd_strerror (err)); break; }
and that results in:
Read from audio interface failed (-5, Input/output error)
Cheers, Erik
Clemens Ladisch wrote:
Erik de Castro Lopo wrote:
RATE: [44100 96000]
snd_pcm_set_params() should have chosen a single rate. Did you dump this after that call?
Yep, the code is:
snd_pcm_format_t format = SND_PCM_FORMAT_S32_LE; snd_pcm_hw_params_t *hw_params; snd_output_t *output ; unsigned int rate = 48000 ;
if ((err = snd_pcm_set_params (capture_handle, format, SND_PCM_ACCESS_RW_INTERLEAVED, 2, rate, 0, 25000)) < 0) { /* 0.5sec */ printf ("Capture open error: %s\n", snd_strerror (err));exit(1); }
if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) { printf ("Cannot allocate hardware parameter structure (%s)\n", snd_strerror (err));exit(1); }
if ((err = snd_pcm_hw_params_any (capture_handle, hw_params)) < 0) { printf ("Cannot initialize hardware parameter structure (%s)\n", snd_strerror (err));exit(1); }
snd_output_stdio_attach (&output, stderr, 0) ; snd_pcm_hw_params_dump (hw_params, output) ; snd_output_close (output) ;
snd_pcm_hw_params_free (hw_params);
Erik
Erik de Castro Lopo wrote:
Clemens Ladisch wrote:
Erik de Castro Lopo wrote:
RATE: [44100 96000]
snd_pcm_set_params() should have chosen a single rate. Did you dump this after that call?
Yep, the code is:
snd_pcm_set_params (capture_handle, format, SND_PCM_ACCESS_RW_INTERLEAVED, 2, rate, 0, 25000)) snd_pcm_hw_params_any (capture_handle, hw_params) snd_pcm_hw_params_dump (hw_params, output) ;
snd_pcm_hw_params_any() gets a _new_ set of parameters. You would use it when you want to configure the device for the first time, or when you want to reconfigure it from scratch.
To get the current parameters of the device, use snd_pcm_hw_params_current().
Regards, Clemens
Clemens Ladisch wrote:
To get the current parameters of the device, use snd_pcm_hw_params_current().
Ah, that makes sense. Now I get:
ACCESS: RW_INTERLEAVED FORMAT: S32_LE SUBFORMAT: STD SAMPLE_BITS: [0 0] FRAME_BITS: [64 64] CHANNELS: [2 2] RATE: [48000 48000] PERIOD_TIME: [6250 6250] PERIOD_SIZE: [300 300] PERIOD_BYTES: [0 0] PERIODS: [0 0] BUFFER_TIME: [0 0] BUFFER_SIZE: [1200 1200] BUFFER_BYTES: [9600 9600] TICK_TIME: [0 0]
Why is SAMPLE_BITS == [0 0], even though the format is S32_LE?
Erik
On 10/26/2015 10:54 AM, Erik de Castro Lopo wrote:
Clemens Ladisch wrote:
To get the current parameters of the device, use snd_pcm_hw_params_current().
Ah, that makes sense. Now I get:
ACCESS: RW_INTERLEAVED FORMAT: S32_LE SUBFORMAT: STD SAMPLE_BITS: [0 0] FRAME_BITS: [64 64] CHANNELS: [2 2] RATE: [48000 48000] PERIOD_TIME: [6250 6250] PERIOD_SIZE: [300 300] PERIOD_BYTES: [0 0] PERIODS: [0 0] BUFFER_TIME: [0 0] BUFFER_SIZE: [1200 1200] BUFFER_BYTES: [9600 9600] TICK_TIME: [0 0]
Why is SAMPLE_BITS == [0 0], even though the format is S32_LE?
That appears to be a bug in snd_pcm_hw_params_current() which does simply not set SAMPLE_BITS.
- Lars
I've got a simple PCM capture test program below, which is basically a slightly tweak version of something from:
http://equalarea.com/paul/alsa-audio.html
The program uses two different methods of setting up the capture handle; one that works (and is complex) and one that doesn't but seems like it should (and is simple). I'm trying to figure out why the simple one (using snd_pcm_set_params) doesn't work.
The program can be compiled and run in two ways:
gcc -Wall -O2 -DUSE_SET_PARAMS=1 alsa_capture.c -lasound -o
alsa_capture
./alsa_capture
which fails with:
Read from audio interface failed (Input/output error)
and:
gcc -Wall -O2 -DUSE_SET_PARAMS=0 alsa_capture.c -lasound -o
alsa_capture
./alsa_capture
which works as expected.
Questions:
Is snd_pcm_set_params() supposed to work on capture handles (I've used it successfully on playback handles)?
Is my use of snd_pcm_set_params() missing something and if so what?
Do your sound card driver support 0.5 second buffer?
If snd_pcm_set_params() is not supposed to work on capture handles would it be possible to update the documentation to reflect that?
int buffer_frames = 128; unsigned int rate = 44100; snd_pcm_t *capture_handle; snd_pcm_format_t format = SND_PCM_FORMAT_S32_LE;
if (USE_SET_PARAMS) { if ((err = snd_pcm_set_params (capture_handle, format,
SND_PCM_ACCESS_RW_INTERLEAVED, 2, rate, 0, 500000)) < 0) { /* 0.5sec */
printf ("Capture open error: %s\n", snd_strerror
(err)); exit (1) ;
} } else { snd_pcm_hw_params_t *hw_params; if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0) { printf ("Cannot allocate hardware parameter
structure (%s)\n", snd_strerror (err)); exit (1) ;
} if ((err = snd_pcm_hw_params_any (capture_handle,
hw_params)) < 0) {
printf ("Cannot initialize hardware parameter
structure (%s)\n", snd_strerror (err)); exit (1) ;
} if ((err = snd_pcm_hw_params_set_access (capture_handle,
hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
printf ("cannot set access type (%s)\n",
snd_strerror (err)); exit (1) ;
} if ((err = snd_pcm_hw_params_set_format (capture_handle,
hw_params, format)) < 0) {
printf ("Cannot set sample format (%s)\n",
snd_strerror (err)); exit (1) ;
} if ((err = snd_pcm_hw_params_set_rate_near
(capture_handle, hw_params, &rate, 0)) < 0) {
printf ("Cannot set sample rate (%s)\n",
snd_strerror (err)); exit (1) ;
} if ((err = snd_pcm_hw_params_set_channels
(capture_handle, hw_params, 2)) < 0) {
printf ("cannot set channel count (%s)\n",
snd_strerror (err)); exit (1) ;
} if ((err = snd_pcm_hw_params (capture_handle, hw_params))
< 0) {
printf ("cannot set parameters (%s)\n",
snd_strerror (err)); exit (1) ;
}
If you does not specify period, period_size or buffer size, period_time or buffer_time before calling snd_pcm_hw_params , you have to use use those hw_params_get_* functions after calling snd_pcm_hw_params to get back those values
buffer = malloc (buffer_frames * snd_pcm_format_width(format) / 8
* 2);
if ((err = snd_pcm_prepare (capture_handle)) < 0) { printf ("cannot prepare audio interface for use (%s)\n",
snd_strerror (err)); exit (1) ;
} for (i = 0; i < 10; ++i) { if ((err = snd_pcm_readi (capture_handle, buffer,
buffer_frames)) != buffer_frames) {
printf ("Read from audio interface failed
(%s)\n", snd_strerror (err)); exit (1) ;
break; } printf("read %d done\n", i); }
Raymond Yau wrote:
Do your sound card driver support 0.5 second buffer?
If it doesn't, it should return an error instead of just not working.
Erik
Do your sound card driver support 0.5 second buffer?
If it doesn't, it should return an error instead of just not working.
The application have to use snd_pcm_get_params after calling snd_pcmset_params
snd_pcm_get_params ( snd_pcm_t * pcm, snd_pcm_uframes_t * buffer_size, snd_pcm_uframes_t * period_size )
Get the transfer size parameters in a simple way.
participants (4)
-
Clemens Ladisch
-
Erik de Castro Lopo
-
Lars-Peter Clausen
-
Raymond Yau