Hi,
I have a reproducible problem with the Intel8x0 driver. The code to
reproduce it is attached. Basically when the capture is set to a single
channel snd_pcm_status_get_avail() always returns a positive value. As
there is usually no data waiting this results in a block or EAGAIN when
attempting to read the data, both undesirable.
Some info from /proc/asound while this is running:
[EMAIL PROTECTED]:~$ cat /proc/asound/cards
0 [I82801DBICH4 ]: ICH - Intel 82801DB-ICH4
Intel 82801DB-ICH4 at 0xd8000c00, irq 17
[EMAIL PROTECTED]:~$
[EMAIL PROTECTED]:~$ cat /proc/asound/I82801DBICH4/pcm0c/sub0/info
card: 0
device: 0
subdevice: 0
stream: CAPTURE
id: Intel ICH
name: Intel 82801DB-ICH4
subname: subdevice #0
class: 0
subclass: 0
subdevices_count: 1
subdevices_avail: 0
[EMAIL PROTECTED]:~$
[EMAIL PROTECTED]:~$ cat /proc/asound/I82801DBICH4/pcm0c/sub0/hw_params
access: MMAP_INTERLEAVED
format: S16_LE
subformat: STD
channels: 2
rate: 16000 (16000/1)
period_size: 2666
buffer_size: 5332
tick_time: 10000
[EMAIL PROTECTED]:~$
[EMAIL PROTECTED]:~$ cat /proc/asound/I82801DBICH4/pcm0c/sub0/status
state: RUNNING
trigger_time: 1060143844.854766000
tstamp : 1060144000.171701000
delay : 2
avail : 2
avail_max : 2
-----
hw_ptr : 2485656
appl_ptr : 2485654
[EMAIL PROTECTED]:~$
[EMAIL PROTECTED]:~$ cat /proc/asound/I82801DBICH4/pcm0c/sub0/sw_params
tstamp_mode: NONE
period_step: 1
sleep_min: 0
avail_min: 4
xfer_align: 1
start_threshold: 5332
stop_threshold: 5332
silence_threshold: 0
silence_size: 0
boundary: 1397751808
[EMAIL PROTECTED]:~$
Cheers,
Steve
--
Steve Smith | "I still say using Monopoly tokens
Vislab, University of Sydney | in a game of Risk violates the
| Geneva convention." - Roadkill
#define ALSA_PCM_NEW_HW_PARAMS_API
#define ALSA_PCM_NEW_SW_PARAMS_API
#include <alsa/asoundlib.h>
#include <stdio.h>
#include <stdarg.h>
/*
* Current open audio device
*/
#define TRUE 1
#define FALSE 0
typedef struct _pcm_stream_t {
snd_pcm_t *handle;
snd_pcm_uframes_t buffer_size;
snd_pcm_uframes_t period_size;
int channels;
} pcm_stream_t;
static struct current_t {
unsigned bytes_per_block;
pcm_stream_t rx;
} current;
static void clear_current()
{
current.rx.handle = NULL;
}
#define CHECKERR(msg) \
{ \
if (err < 0) \
{ \
fprintf(stderr, msg ": %s\n", snd_strerror(err)); \
exit(-1); \
} \
}
static void __attribute__((unused)) dump_alsa_current(snd_pcm_t *handle)
{
int err;
snd_output_t *out;
err = snd_output_stdio_attach(&out, stderr, 0);
snd_output_printf(out, "--- MY IO\n");
err = snd_pcm_dump_setup(handle, out);
snd_output_printf(out, "--- SW\n");
err = snd_pcm_dump_sw_setup(handle, out);
snd_output_printf(out, "--- HW\n");
err = snd_pcm_dump_hw_setup(handle, out);
snd_output_printf(out, "--- DONE\n");
snd_output_close(out);
}
/* *** Alsa driver implementation. *** */
static int open_stream(pcm_stream_t *stream, snd_pcm_stream_t type)
{
int err;
size_t bsize;
snd_pcm_uframes_t frames;
unsigned int rrate;
snd_pcm_hw_params_t *hw_params;
snd_pcm_sw_params_t *sw_params;
err = snd_pcm_open(&stream->handle, "plughw:0,0",
type, SND_PCM_NONBLOCK);
CHECKERR("Card open failed");
snd_pcm_hw_params_alloca (&hw_params);
err = snd_pcm_hw_params_any (stream->handle, hw_params);
CHECKERR("Failed to initialise HW parameters");
err = snd_pcm_hw_params_set_access(stream->handle, hw_params,
SND_PCM_ACCESS_RW_INTERLEAVED);
CHECKERR("Failed to set interleaved access");
err = snd_pcm_hw_params_set_format (stream->handle, hw_params,
SND_PCM_FORMAT_S16);
CHECKERR("Failed to set encoding");
err = snd_pcm_hw_params_set_channels (stream->handle, hw_params, 1);
CHECKERR("Failed to set channels");
rrate = 16000;
err = snd_pcm_hw_params_set_rate_near (stream->handle, hw_params,
&rrate, 0);
CHECKERR("Failed to set sample rate");
if (rrate != 16000) {
fprintf(stderr, "ALSA rate set to %d when we wanted %d\n",
rrate, 16000);
return FALSE;
}
// Setup the buffer size. This stuff's all in frames, BTW. We can't
// convert with the helper functions at this point as they require
// a working handle, and ours isn't setup yet. We don't actually do
// anything with these values anyway.
bsize = snd_pcm_format_size (SND_PCM_FORMAT_S16, 16000 / 6);
frames = bsize;
err = snd_pcm_hw_params_set_buffer_size_near(stream->handle, hw_params,
&frames);
CHECKERR("Failed to set buffer size");
stream->buffer_size = frames;
printf("Buffer == %d\n", stream->buffer_size);
frames = bsize / 2;
err = snd_pcm_hw_params_set_period_size_near(stream->handle, hw_params,
&frames, 0);
CHECKERR("Failed to set period size");
stream->period_size = frames;
printf("Period == %d\n", stream->period_size);
err = snd_pcm_hw_params (stream->handle, hw_params);
CHECKERR("Failed to install HW parameters");
// ALSA software settings
snd_pcm_sw_params_alloca(&sw_params);
err = snd_pcm_sw_params_current(stream->handle, sw_params);
CHECKERR("Failed to initialise SW params");
err = snd_pcm_sw_params_set_start_threshold(stream->handle, sw_params,
stream->buffer_size);
CHECKERR("Failed to set threshold value");
err = snd_pcm_sw_params_set_avail_min(stream->handle, sw_params, 4);
CHECKERR("Failed to set min available value");
err = snd_pcm_sw_params_set_xfer_align(stream->handle, sw_params, 1);
CHECKERR("Failed to set xfer align value");
err = snd_pcm_sw_params(stream->handle, sw_params);
CHECKERR("Failed to set SW params");
return TRUE;
}
int alsa_audio_open()
{
int err;
current.bytes_per_block = 320;
if (!open_stream(¤t.rx, SND_PCM_STREAM_CAPTURE)) {
fprintf(stderr, "Failed to open device for capture\n");
return FALSE;
}
err = snd_pcm_start(current.rx.handle);
CHECKERR("Failed to start PCM capture");
return TRUE;
}
/*
* Shutdown.
*/
void alsa_audio_close()
{
int err;
printf("Closing device\n");
err = snd_pcm_close(current.rx.handle);
CHECKERR("Error closing capture PCM");
clear_current();
}
/*
* Set options on audio device to be non-blocking.
*/
void alsa_audio_non_block()
{
int err;
printf("Set nonblocking\n");
err = snd_pcm_nonblock(current.rx.handle, TRUE);
CHECKERR("Error setting RX non-blocking");
}
int alsa_audio_is_ready()
{
snd_pcm_status_t *status;
snd_pcm_uframes_t avail;
int err, bytes;
snd_pcm_status_alloca(&status);
err = snd_pcm_status(current.rx.handle, status);
CHECKERR("Can't get status of rx");
avail = snd_pcm_status_get_avail(status);
bytes = snd_pcm_frames_to_bytes(current.rx.handle, avail);
printf("Audio ready == %d (%d bytes)\n", avail, bytes);
return (bytes);
}
int alsa_audio_read(unsigned char *buf, int bytes)
{
snd_pcm_sframes_t frames = snd_pcm_bytes_to_frames(current.rx.handle, bytes);
snd_pcm_sframes_t fread;
int err;
fread = snd_pcm_readi(current.rx.handle, buf, frames);
if (fread >= 0) {
// Normal case
fread = snd_pcm_frames_to_bytes(current.rx.handle, fread);
printf("Read %d bytes\n", fread);
return fread;
}
// Something happened
switch (fread)
{
case -EAGAIN:
// Normal when non-blocking
return 0;
case -EPIPE:
printf("Got capture XRUN\n");
err = snd_pcm_prepare(current.rx.handle);
CHECKERR("Can't recover from capture overrun");
return FALSE;
case -ESTRPIPE:
printf("Got capture ESTRPIPE\n");
while ((err = snd_pcm_resume(current.rx.handle)) == -EAGAIN)
sleep(1); /* wait until the suspend flag is released */
if (err < 0) {
err = snd_pcm_prepare(current.rx.handle);
CHECKERR("Can't recovery from capture suspend");
}
return FALSE;
default:
printf("Write failed status=%d: %s\n", snd_strerror(fread));
return 0;
}
}
void alsa_audio_wait_for(int delay_ms)
{
printf("Audio wait %d\n", delay_ms);
snd_pcm_wait(current.rx.handle, delay_ms);
}
int main()
{
unsigned char buf[1024];
clear_current();
alsa_audio_open();
alsa_audio_non_block();
while (1) {
int avail = alsa_audio_is_ready();
if (avail) {
alsa_audio_read(buf, (avail > 1024) ? 1024 : avail);
} else {
alsa_audio_wait_for(50);
}
}
return 0;
}