Hi,
I wrote the attached simple test application that captures audio and
plays it back, using ALSA API.

It is working when I run it without pulseaudio (hw:0), but when I use
the pulse device I keep on getting playback underrun and I am not able
to figure it out why...

What am I doing wrong?
(there are some line to print some timing statistics)

Thank you in advance,
Ludovico

ps: I am running pulseaudio 0.9.15
#include <stdio.h>
#include <asoundlib.h>
#include <unistd.h>
#include <sys/time.h>


static inline suseconds_t msecdiff(const struct timeval &tv2, const struct timeval &tv1) {
    return (tv2.tv_sec-tv1.tv_sec)*1000+(tv2.tv_usec-tv1.tv_usec)/1000;
}

static inline
void CHECK_FAIL(int e, const char *n) { if(e < 0) { fprintf(stderr, "%s: %s (%d)\n", n, snd_strerror(e), e); exit(1); } else { fprintf(stderr, "%s: ok\n", n); } }

#define NCH 2

static const int nchannels = NCH;
static const int bytes_per_frame = 2*NCH;
snd_pcm_uframes_t buffer_frame_size_frames;
unsigned int rate = 48000;
unsigned period_time = 30;
unsigned int periods = 4;

snd_pcm_t * setup(const char *devname, snd_pcm_stream_t mode) {
    snd_pcm_t * pcm_handle;
    CHECK_FAIL(snd_pcm_open(&pcm_handle, devname, mode, 0), "snd_pcm_open");

    snd_pcm_hw_params_t *hwparams;
    snd_pcm_hw_params_alloca(&hwparams);

    CHECK_FAIL(snd_pcm_hw_params_any(pcm_handle, hwparams), "snd_pcm_hw_params_any");
    CHECK_FAIL(snd_pcm_hw_params_set_rate_resample(pcm_handle, hwparams, 0), "snd_pcm_hw_params_set_rate_resample");

    CHECK_FAIL(snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED), "snd_pcm_hw_params_set_access");
    CHECK_FAIL(snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_LE), "snd_pcm_hw_params_set_format");
    CHECK_FAIL(snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &rate, 0), "snd_pcm_hw_params_set_rate_near");
    printf("rate: %u\n", rate);
    CHECK_FAIL(snd_pcm_hw_params_set_channels(pcm_handle, hwparams, nchannels), "snd_pcm_hw_params_set_channels");
    CHECK_FAIL(snd_pcm_hw_params_get_buffer_size_max(hwparams, &buffer_frame_size_frames), "snd_pcm_hw_params_get_buffer_size_max");
    printf("buffer size in frames: %lu, in ms: %lu\n", buffer_frame_size_frames, buffer_frame_size_frames*1000/rate);
    CHECK_FAIL(snd_pcm_hw_params_set_periods_integer(pcm_handle, hwparams), "snd_pcm_hw_params_set_periods_integer");
    unsigned frames_per_ms = rate/1000;
    unsigned int period_size_frames = period_time*frames_per_ms;
    int dir = 0;
    CHECK_FAIL(snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &periods, &dir), "snd_pcm_hw_params_set_periods_near");
    printf("periods: %u (dir=%d)\n", periods, dir);
    buffer_frame_size_frames = (periods * period_size_frames);

    CHECK_FAIL(snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &buffer_frame_size_frames), "snd_pcm_hw_params_set_buffer_size_near");
    printf("buffer size in frames: %lu, in ms: %lu\n", buffer_frame_size_frames, buffer_frame_size_frames/frames_per_ms);
    CHECK_FAIL(snd_pcm_hw_params(pcm_handle, hwparams), "snd_pcm_hw_params");

    CHECK_FAIL(snd_pcm_prepare(pcm_handle), "snd_pcm_prepare");

    snd_pcm_info_t *pcm_info = NULL;
    snd_pcm_info_alloca(&pcm_info);
    CHECK_FAIL(snd_pcm_info(pcm_handle, pcm_info), "snd_pcm_info");

    snd_output_t *out;
    CHECK_FAIL(snd_output_buffer_open(&out), "snd_output_buffer_open");
    CHECK_FAIL(snd_pcm_dump(pcm_handle, out), "snd_pcm_dump");
    char *s = NULL;
    snd_output_buffer_string(out, &s);
    printf("%s\n", s);

    return pcm_handle;
}

int main(int argc, char *argv[]) {
    const char *in = argc >= 2 ? argv[1] : "default";
    const char *out = argc >= 3 ? argv[2] : in;


    // setup devices
    snd_pcm_t *pcm_handle_in = setup(in, SND_PCM_STREAM_CAPTURE);
    snd_pcm_t *pcm_handle_out = setup(out, SND_PCM_STREAM_PLAYBACK);


    struct timeval tv_read, tv_written, tv_prepared, tv2;
    unsigned tot;
    char buf[buffer_frame_size_frames*bytes_per_frame];

    // start capture
    CHECK_FAIL(snd_pcm_start(pcm_handle_in), "snd_pcm_start");

    int started = 0;
    gettimeofday(&tv_written, 0);

    while(1) {
        printf("P %ld C %ld A\n", snd_pcm_avail(pcm_handle_out), snd_pcm_avail(pcm_handle_in));

        // read two periods (even if I ask for one periods, it takes the time of two periods!)
        int n = snd_pcm_readi(pcm_handle_in, buf, buffer_frame_size_frames/periods*2);

        gettimeofday(&tv2, 0);
        printf("P %ld C %ld A\n", snd_pcm_avail(pcm_handle_out), snd_pcm_avail(pcm_handle_in));
        printf("T between read      %5ld\n", msecdiff(tv2, tv_read));
        printf("T duration of read  %5ld\n", msecdiff(tv2, tv_written));
        tv_read = tv2;
        if(n < 0) {
            if(n == -EAGAIN) {
                printf("*");
                fflush(stdout);
                continue;
            } else {
                CHECK_FAIL(n, "snd_pcm_avail_update");
            }
        }
        printf("frames read: %d\n", n);

        // if we are starting the pcm output now, let's add a period time delay,
        // in order to reduce the change of an uderrun
        if(!started) {
            printf("sleeping %d ms\n", period_time);
            usleep(period_time*1000);
            started = 1;
        }

        printf("P %ld C %ld A\n", snd_pcm_avail(pcm_handle_out), snd_pcm_avail(pcm_handle_in));

        // write the capture data to the output pcm
        int wret = snd_pcm_writei(pcm_handle_out, buf, n);

        gettimeofday(&tv2, 0);
        printf("P %ld C %ld A\n", snd_pcm_avail(pcm_handle_out), snd_pcm_avail(pcm_handle_in));
        printf("T between write     %5ld\n", msecdiff(tv2, tv_written));
        printf("T duration of write %5ld\n", msecdiff(tv2, tv_read));
        tv_written = tv2;

        if(wret == -EPIPE) {
            printf("******************\n");

            // recover from uderrun
            CHECK_FAIL(snd_pcm_prepare(pcm_handle_out), "snd_pcm_prepare");
            gettimeofday(&tv2, 0);
            printf("T between perp      %5ld\n", msecdiff(tv2, tv_prepared));
            printf("T duration of prep  %5ld\n", msecdiff(tv2, tv_written));
            tv_prepared = tv2;
            tv_written = tv2;
            started = 0;
        } else {
            CHECK_FAIL(wret, "snd_pcm_writei");
        }

        tot += n;
        if(tot >= 5*8000*100) {
            break;
        }
        printf("\n");
    }

    CHECK_FAIL(snd_pcm_close(pcm_handle_in), "snd_pcm_close");
    CHECK_FAIL(snd_pcm_close(pcm_handle_out), "snd_pcm_close");

    return 0;
}
_______________________________________________
pulseaudio-discuss mailing list
[email protected]
https://tango.0pointer.de/mailman/listinfo/pulseaudio-discuss

Reply via email to