#include #include #include "pipewire/pipewire.h" #include "spa/param/audio/format-utils.h" struct local_data { pw_main_loop* loop; pw_stream* stream; std::mt19937 gen; std::normal_distribution nd{0, 0.3}; }; void on_process(local_data* ld) { pw_buffer *b; spa_buffer *buf; if((b = pw_stream_dequeue_buffer(ld->stream)) == NULL) { pw_log_warn("No buffers left!"); return; } buf = b->buffer; float* dst; if((dst = (float*)buf->datas[0].data) == NULL) { return; } int n_frames = buf->datas[0].maxsize / sizeof(float); if(b->requested) { n_frames = SPA_MIN(b->requested, n_frames); } for(int i = 0; i < n_frames; ++i) { *dst++ = ld->nd(ld->gen); } buf->datas[0].chunk->offset = 0; buf->datas[0].chunk->stride = sizeof(float); buf->datas[0].chunk->size = n_frames * sizeof(float); pw_stream_queue_buffer(ld->stream, b); } const struct pw_stream_events stream_events { PW_VERSION_STREAM_EVENTS, .process = (void(*)(void*))on_process, }; int main(int argc, char** argv) { // We do NOT seed the MT engine -> the time series will be predictable! local_data bookkeep{nullptr,nullptr,}; pw_init(&argc, &argv); bookkeep.loop = pw_main_loop_new(NULL); bookkeep.stream = pw_stream_new_simple( pw_main_loop_get_loop(bookkeep.loop), "audio-src", pw_properties_new( PW_KEY_MEDIA_TYPE, "Audio", PW_KEY_MEDIA_CATEGORY, "Playback", PW_KEY_MEDIA_ROLE, "Test", NULL), &stream_events, &bookkeep); const spa_pod* params[1]; uint8_t buffer[1024]; spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer, sizeof(buffer)); auto audio_info = SPA_AUDIO_INFO_RAW_INIT( .format = SPA_AUDIO_FORMAT_F32_LE, .rate = 44100, .channels = 1,); params[0] = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat,&audio_info); pw_stream_connect(bookkeep.stream, PW_DIRECTION_OUTPUT, PW_ID_ANY, (pw_stream_flags)(PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_MAP_BUFFERS | PW_STREAM_FLAG_RT_PROCESS), params, 1); pw_main_loop_run(bookkeep.loop); pw_stream_destroy(bookkeep.stream); pw_main_loop_destroy(bookkeep.loop); return 0; }