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