/* Test program using the asynchronous API. * This is demonstrating a problem where the playback under-runs, but * yet the stream lists a writable size of 0. Those are contracidtory, * if it is under-running it should have space available, if there isn't * any space available it shouldn't be under-running. Adding some * debugging to src/pulse/stream.c shows that the s->requested_bytes * is negative, but returns 0 on negative. * * At least this is the case as of PulseAudio 0.9.21, seems to be fixed * in the git code grabbed on 09-18-2010, I didn't look into when it * was fixed. */ #include #include #include #include // If it isn't a multiple of the sample size, 4, 8, 12, etc the pulseaudio // daemon asserts and dies. #define SEEK 1 void writable(pa_stream *p, size_t nbytes, void *userdata) { char *buffer; /* pa_stream_begin_write "If you place (size_t) -1 in *nbytes on * invocation the memory size will be chosen automatically (which is * recommended to do)." (from the OggPulseAudio.h) */ nbytes=(size_t)-1; pa_stream_begin_write(p, (void**)&buffer, &nbytes); // fill the buffer with audio here... memset(buffer, 0, nbytes); pa_stream_write(p, buffer, nbytes, NULL, SEEK, PA_SEEK_RELATIVE); printf("wrote %ld, offset %d\n", 1L*nbytes, SEEK); } int main(int argc, char **argv) { pa_threaded_mainloop *mainloop=NULL; pa_mainloop_api *api=NULL; pa_context *context=NULL; pa_stream *stream=NULL; pa_sample_spec spec; pa_context_state_t cstate; mainloop=pa_threaded_mainloop_new(); if(!mainloop) { fprintf(stderr, "Failed to create PulseAudio main loop\n"); return 1; } api=pa_threaded_mainloop_get_api(mainloop); pa_threaded_mainloop_start(mainloop); pa_threaded_mainloop_lock(mainloop); context=pa_context_new(api, __PRETTY_FUNCTION__); pa_context_connect(context, NULL, (pa_context_flags_t)0, NULL); pa_threaded_mainloop_unlock(mainloop); spec.format=PA_SAMPLE_S16LE; spec.rate=48000; spec.channels=2; pa_channel_map map; pa_channel_map_init_auto(&map, spec.channels, PA_CHANNEL_MAP_DEFAULT); for(;;) { pa_threaded_mainloop_lock(mainloop); cstate=pa_context_get_state(context); pa_threaded_mainloop_unlock(mainloop); if(cstate>=PA_CONTEXT_READY) break; usleep(1000); } pa_threaded_mainloop_lock(mainloop); stream=pa_stream_new(context, __func__, &spec, &map); if(!stream) return 1; pa_stream_set_write_callback(stream, writable, NULL); pa_stream_flags_t flags = (pa_stream_flags_t)( PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE); pa_stream_connect_playback(stream, NULL, NULL, flags, NULL, NULL); pa_threaded_mainloop_unlock(mainloop); for(;;) { sleep(1); pa_threaded_mainloop_lock(mainloop); printf("Stream state %d, suspended %d, " "corked %d, writable size %ld\n", pa_stream_get_state(stream), pa_stream_is_suspended(stream), pa_stream_is_corked(stream), 1L*pa_stream_writable_size(stream)); pa_threaded_mainloop_unlock(mainloop); } // shutdown pa_stream_disconnect(stream); pa_stream_unref(stream); if(mainloop) { pa_context_disconnect(context); pa_context_unref(context); context=NULL; pa_threaded_mainloop_free(mainloop); mainloop=NULL; api=NULL; // freed with mainloop } return 0; }