suddenfall
suddenfall

Reputation: 11

Capturing samples with ALSA using poll() does not work

I want to write an application with exactly one ALSA thread that uses poll() to handle all the events of the mixer, playback, and capture. I have successfully implemented a little program for playback. Currently I have problems capturing samples using poll(). I don't receive any capture events from poll(). This is my code:

#include <stdio.h>
#include <alsa/asoundlib.h>
#include <sys/eventfd.h>
#include <pthread.h>

#define COUNT_SAMPLES 1024
#define COUNT_CHANNELS 2
#define DEVICE "plughw:CARD=DSC8,DEV=0"
#define SAMPLE_RATE 48000

#define FD_IGNORE -1
enum ALSA_FD {
    FD_CONFIG,
    FD_PLAYBACK,
    FD_CAPTURE,
    FD_MIXER,
    FD_COUNT
};

static snd_pcm_t *capture_handle = NULL;
static struct pollfd fds[FD_COUNT];
static int fds_initialized = 0;

#define exit_on_fail(result, msg) { \
    int error = result; \
    if (error < 0) { \
        printf("ERROR: %s (%s)\n", msg, snd_strerror(error)); \
        exit(0); \
    } \
}

void set_parameters(snd_pcm_t *handle, unsigned sample_rate, snd_pcm_format_t format, unsigned int count_channels) {
    unsigned long period_size = COUNT_SAMPLES;
    snd_pcm_sw_params_t *sw_params = NULL;
    snd_pcm_hw_params_t *hw_params = NULL;

    snd_pcm_sw_params_malloc(&sw_params);
    snd_pcm_sw_params_current(handle, sw_params);
    snd_pcm_hw_params_alloca(&hw_params);
    exit_on_fail(snd_pcm_hw_params_any(handle, hw_params), "Failed to retrieve HW params");
    exit_on_fail(snd_pcm_hw_params_set_access(handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED), "Can't set interleaved mode.");
    exit_on_fail(snd_pcm_hw_params_set_format(handle, hw_params, format), "Can't set format.");
    exit_on_fail(snd_pcm_hw_params_set_channels(handle, hw_params, count_channels), "Can't set channels number.");
    exit_on_fail(snd_pcm_hw_params_set_rate_near(handle, hw_params, &sample_rate, 0), "Can't set rate.");
    exit_on_fail(snd_pcm_hw_params_set_period_size_near(handle, hw_params, &period_size, 0), "Can't set period size");
    exit_on_fail(snd_pcm_hw_params(handle, hw_params), "Failed to set HW params");
}

void * alsa_thread(void * arg) {
    int status;
    int terminated = 0;

    printf("Starting alsa thread\n");

    fds[FD_CONFIG].fd = FD_IGNORE; // EFD_NONBLOCK
    fds[FD_CONFIG].fd = eventfd(0, EFD_NONBLOCK); // EFD_NONBLOCK
    fds[FD_CONFIG].events = POLLIN;

    fds[FD_CAPTURE].fd = FD_IGNORE;
    fds[FD_CAPTURE].events = POLLIN;

    fds[FD_PLAYBACK].fd = FD_IGNORE;
    fds[FD_PLAYBACK].events = POLLOUT;

    fds[FD_MIXER].fd = FD_IGNORE;
    fds[FD_MIXER].events = POLLIN;

    fds_initialized = 1;

    while (!terminated) {
        status = poll(fds, FD_COUNT, -1);
        if (status > 0) {
            printf("Status: %d\n", status);
            if (fds[FD_PLAYBACK].revents & POLLOUT) {
                printf("PLAYBACK\n");
            }
            if (fds[FD_CAPTURE].revents & POLLIN) {
                printf("CAPTURE\n");
            }
            if (fds[FD_MIXER].revents & POLLOUT) {
                printf("MIXER\n");
            }
            if (fds[FD_CONFIG].revents & POLLIN) {
                eventfd_t value;
                eventfd_read(fds[FD_CONFIG].fd, &value);
                printf("CONFIG\n");
            }
        } else if (status < 0) {
            printf("Error: %d", status);
        }
    }

    close(fds[FD_CONFIG].fd);

    return NULL;
}

void notify_config_changed() {
    while (!fds_initialized);
    eventfd_write(fds[FD_CONFIG].fd, 1);
}

void start_capture(const char *device, unsigned sample_rate, unsigned count_channels) {
    exit_on_fail(snd_pcm_open(&capture_handle, device, SND_PCM_STREAM_CAPTURE, 0), "Failed to open PCM device");
    set_parameters(capture_handle, sample_rate, SND_PCM_FORMAT_S32_LE, count_channels);
    exit_on_fail(snd_pcm_poll_descriptors(capture_handle, &fds[FD_CAPTURE], 1), "Failed to set file descriptor");

    notify_config_changed();

    printf("Capturing ...\n");
}

int main(int argc, char **argv) {
    pthread_t tid_alsa;
    pthread_create(&tid_alsa, NULL, alsa_thread, NULL);
    start_capture(DEVICE, SAMPLE_RATE, COUNT_CHANNELS);
    for(;;);
    return 0;
}

My console output is:

Starting alsa thread
Capturing ...
Status: 1
CONFIG

Any ideas are welcome!

Upvotes: 0

Views: 99

Answers (1)

suddenfall
suddenfall

Reputation: 11

Finally, I have found the problem. In the function start_capture I have added after the instruction

exit_on_fail(snd_pcm_poll_descriptors(capture_handle, &fds[FD_CAPTURE], 1), "Failed to set file descriptor");

this line:

exit_on_fail(snd_pcm_start(capture_handle), "Starting capturing ...");`

This starts the capturing and the events are correctly generated with poll. Here is the complete code of the function:

void start_capture(const char *device, unsigned sample_rate, unsigned count_channels) {
    exit_on_fail(snd_pcm_open(&capture_handle, device, SND_PCM_STREAM_CAPTURE, 0), "Failed to open PCM device");
    set_parameters(capture_handle, sample_rate, SND_PCM_FORMAT_S32_LE, count_channels);
    exit_on_fail(snd_pcm_poll_descriptors(capture_handle, &fds[FD_CAPTURE], 1), "Failed to set file descriptor");
    exit_on_fail(snd_pcm_start(capture_handle), "Starting capturing ...");

    notify_config_changed();

    printf("Capturing ...\n");
}

Upvotes: 1

Related Questions