diff --git a/src/examples/audio-src.c b/src/examples/audio-src.c index f6120b345..af78461e4 100644 --- a/src/examples/audio-src.c +++ b/src/examples/audio-src.c @@ -19,6 +19,7 @@ #define M_PI_M2 ( M_PI + M_PI ) +#define DEFAULT_QUANTUM 1764 #define DEFAULT_RATE 44100 #define DEFAULT_CHANNELS 2 #define DEFAULT_VOLUME 0.7 @@ -28,6 +29,7 @@ struct data { struct pw_stream *stream; double accumulator; + uint64_t outputSample; }; static void fill_f32(struct data *d, void *dest, int n_frames) @@ -63,17 +65,41 @@ static void on_process(void *userdata) int n_frames, stride; uint8_t *p; + struct pw_time streamTime; + uint64_t driverSample; + int64_t queued; + + pw_stream_get_time_n(data->stream, &streamTime, sizeof(streamTime)); + if ((b = pw_stream_dequeue_buffer(data->stream)) == NULL) { pw_log_warn("out of buffers: %m"); return; } + /* calculate the current sample in the driver's timeline */ + driverSample = streamTime.ticks * DEFAULT_RATE * streamTime.rate.num / streamTime.rate.denom; + + /* find how many samples we have queued but have not yet appeared in the driver's timeline; + these are queued in the audioadapter but pw_stream doesn't report them accurately */ + queued = data->outputSample - driverSample; + if (queued < 0) { + /* XRun, resync our timeline */ + pw_log_info("XRun! resync, data->outputSample += %"PRIi64, -queued); + data->outputSample += -queued; + queued = 0; + } + + pw_log_info("process, b.req %"PRIu64", s.ticks %"PRIu64 ", sample(our) %"PRIu64", sample(dr) %"PRIu64",\n" + " s.buf %"PRIu64 ", s.queued %"PRIu64 ", queued (real) %"PRIi64 ", s.delay %"PRIu64, + b->requested, streamTime.ticks, data->outputSample, driverSample, + streamTime.buffered, streamTime.queued, queued, streamTime.delay); + buf = b->buffer; if ((p = buf->datas[0].data) == NULL) return; stride = sizeof(float) * DEFAULT_CHANNELS; - n_frames = SPA_MIN(b->requested, buf->datas[0].maxsize / stride); + n_frames = SPA_MIN((uint64_t) DEFAULT_QUANTUM, buf->datas[0].maxsize / stride); fill_f32(data, p, n_frames); @@ -81,6 +107,9 @@ static void on_process(void *userdata) buf->datas[0].chunk->stride = stride; buf->datas[0].chunk->size = n_frames * stride; + b->size = n_frames; + data->outputSample += n_frames; + pw_stream_queue_buffer(data->stream, b); } @@ -98,7 +127,7 @@ static void do_quit(void *userdata, int signal_number) int main(int argc, char *argv[]) { struct data data = { 0, }; - const struct spa_pod *params[1]; + const struct spa_pod *params[2]; uint8_t buffer[1024]; struct pw_properties *props; struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); @@ -126,6 +155,7 @@ int main(int argc, char *argv[]) props = pw_properties_new(PW_KEY_MEDIA_TYPE, "Audio", PW_KEY_MEDIA_CATEGORY, "Playback", PW_KEY_MEDIA_ROLE, "Music", + PW_KEY_NODE_LATENCY, SPA_STRINGIFY (DEFAULT_QUANTUM) "/" SPA_STRINGIFY (DEFAULT_RATE), NULL); if (argc > 1) /* Set stream target if given on command line */ @@ -144,6 +174,13 @@ int main(int argc, char *argv[]) .format = SPA_AUDIO_FORMAT_F32, .channels = DEFAULT_CHANNELS, .rate = DEFAULT_RATE )); + params[1] = spa_pod_builder_add_object(&b, + SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, + SPA_PARAM_BUFFERS_buffers, SPA_POD_Int(4), + SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1), + SPA_PARAM_BUFFERS_size, SPA_POD_Int(DEFAULT_QUANTUM * sizeof(float) * DEFAULT_CHANNELS), + SPA_PARAM_BUFFERS_stride, SPA_POD_Int(sizeof(float) * DEFAULT_CHANNELS), + SPA_PARAM_BUFFERS_dataType, SPA_POD_CHOICE_FLAGS_Int((1<