/* build with: * $ gcc -lpulse -pthread -o main main.c */ #include #include #include #include #include #include #include #define RATE 44100 #define CH 2 static pa_mainloop *pulse_ml; static pa_context *pulse_ctx; static pthread_mutex_t pulse_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t pulse_cond = PTHREAD_COND_INITIALIZER; static pthread_t pulse_thread; static int pulse_poll_func(struct pollfd *ufds, unsigned long nfds, int timeout, void *user) { int r; pthread_mutex_unlock(&pulse_lock); r = poll(ufds, nfds, timeout); pthread_mutex_lock(&pulse_lock); return r; } static void *pulse_mainloop_thread(void *user) { int ret; pulse_ml = pa_mainloop_new(); if(!pulse_ml){ printf("pa_mainloop_new() failed\n"); return (void*)1; } pa_mainloop_set_poll_func(pulse_ml, pulse_poll_func, NULL); pthread_mutex_lock(&pulse_lock); pthread_cond_signal(&pulse_cond); pa_mainloop_run(pulse_ml, &ret); pthread_mutex_unlock(&pulse_lock); pa_mainloop_free(pulse_ml); return (void*)(size_t)ret; } static void context_state_cb(pa_context *ctx, void *user) { pthread_cond_signal(&pulse_cond); } static int ready_pulse(void) { int err; pa_context_state_t state; err = pthread_create(&pulse_thread, NULL, &pulse_mainloop_thread, NULL); if(err < 0){ printf("pthread_create: %u (%s)\n", err, strerror(err)); return 1; } pthread_cond_wait(&pulse_cond, &pulse_lock); pulse_ctx = pa_context_new(pa_mainloop_get_api(pulse_ml), "appname"); if(!pulse_ctx){ printf("pa_context_new failed\n"); return 1; } pa_context_set_state_callback(pulse_ctx, context_state_cb, NULL); err = pa_context_connect(pulse_ctx, NULL, 0, NULL); if(err < 0){ printf("pa_context_connect: %u (%s)\n", err, strerror(err)); return 1; } do{ pthread_cond_wait(&pulse_cond, &pulse_lock); state = pa_context_get_state(pulse_ctx); if(state == PA_CONTEXT_FAILED || state == PA_CONTEXT_TERMINATED){ printf("got invalid state: %u\n", state); return 1; } }while(state != PA_CONTEXT_READY); printf("pulse is ready now!\n"); return 0; } static void stream_state_cb(pa_stream *stream, void *user) { pthread_cond_signal(&pulse_cond); } static int ready_stream(pa_stream **out, const char *name) { pa_buffer_attr attr; pa_stream *stream; pa_sample_spec spec; int err; spec.format = PA_SAMPLE_FLOAT32LE; spec.rate = RATE; spec.channels = CH; stream = pa_stream_new(pulse_ctx, name, &spec, NULL); if(!stream){ printf("pa_stream_new failed\n"); return 1; } pa_stream_set_state_callback(stream, stream_state_cb, NULL); err = pa_stream_connect_record(stream, NULL, NULL, PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE); if(err != 0){ printf("pa_stream_connect_record failed: %d\n", err); return 1; } while(pa_stream_get_state(stream) == PA_STREAM_CREATING) pthread_cond_wait(&pulse_cond, &pulse_lock); if(pa_stream_get_state(stream) != PA_STREAM_READY){ printf("Something went wrong with the stream connecting!\n"); return 1; } printf("stream is ready now!\n"); *out = stream; return 0; } static void get_server_info_cb(pa_context *ctx, const pa_server_info *info, void *user) { pa_server_info *out = user; *out = *info; } static int get_default_fmt(void) { pa_operation *o; pa_server_info info; o = pa_context_get_server_info(pulse_ctx, get_server_info_cb, &info); pthread_mutex_unlock(&pulse_lock); while(pa_operation_get_state(o) == PA_OPERATION_RUNNING) ; pthread_mutex_lock(&pulse_lock); if(pa_operation_get_state(o) != PA_OPERATION_DONE){ pa_operation_unref(o); return 1; } pa_operation_unref(o); printf("format: %u\n", info.sample_spec.format); printf("rate: %u\n", info.sample_spec.rate); printf("channels: %u\n", info.sample_spec.channels); return 0; } int main(int argc, char **argv) { pa_stream *stream; pa_operation *o; const pa_buffer_attr *attr; size_t avail; pthread_mutex_lock(&pulse_lock); if(ready_pulse() != 0) return 1; if(get_default_fmt() != 0) return 1; if(ready_stream(&stream, "stream 1") != 0) return 1; avail = pa_stream_readable_size(stream); printf("pre-sleep avail: %u bytes\n", avail); pthread_mutex_unlock(&pulse_lock); sleep(1); pthread_mutex_lock(&pulse_lock); o = pa_stream_cork(stream, 1, NULL, NULL); if(!o) printf("cork failed\n"); pthread_mutex_unlock(&pulse_lock); while(pa_operation_get_state(o) == PA_OPERATION_RUNNING); pthread_mutex_lock(&pulse_lock); if(pa_operation_get_state(o) != PA_OPERATION_DONE) printf("cork operation failed!\n"); pa_operation_unref(o); avail = pa_stream_readable_size(stream); printf("post-cork avail: %u bytes\n", avail); o = pa_stream_flush(stream, NULL, NULL); if(!o) printf("flush failed\n"); pthread_mutex_unlock(&pulse_lock); while(pa_operation_get_state(o) == PA_OPERATION_RUNNING); pthread_mutex_lock(&pulse_lock); if(pa_operation_get_state(o) != PA_OPERATION_DONE) printf("flush operation failed!\n"); pa_operation_unref(o); avail = pa_stream_readable_size(stream); printf("post-flush avail: %u bytes\n", avail); pthread_mutex_unlock(&pulse_lock); return 0; }