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(&current.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;
}

Reply via email to