examples

Toy examples in single C files.
git clone git://henryandlizzy.uk/examples
Log | Files | Refs

commit 6c9c48853e5012285bfd0d04094d9c2c948db23a
parent 86ed3d35616d58be660771325d65f2e1e3abb0ca
Author: Henry Wilson <henry@henryandlizzy.uk>
Date:   Fri, 26 Aug 2022 23:40:35 +0100

pulse: Add pulse async client

Diffstat:
Mmakefile | 3++-
Ares/pluck.mono44100s16le | 0
Asrc/pulse-async-client.cpp | 169+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 171 insertions(+), 1 deletion(-)

diff --git a/makefile b/makefile @@ -18,8 +18,9 @@ clean: $(RM) $(c_targets) $(cpp_targets) aio: -lrt +gl-asteroids: -lglfw -lGL -lm io_uring: -luring +pulse-async-client: -lpulse pulse-simple-client: -lpulse-simple -lm -gl-asteroids: -lglfw -lGL -lm .PHONY: clean all all-c all-cpp diff --git a/res/pluck.mono44100s16le b/res/pluck.mono44100s16le Binary files differ. diff --git a/src/pulse-async-client.cpp b/src/pulse-async-client.cpp @@ -0,0 +1,169 @@ +#include <pulse/pulseaudio.h> +#include <unistd.h> + +#include <iostream> +#include <vector> +#include <span> +#include <memory> +#include <fstream> + +bool ready = false; + +std::vector<short> sample; +std::vector<short>::const_iterator pos; + +void play_sample(std::span<short> buf) +{ + for (auto& s : buf) + { + if (pos != sample.cend()) + s = *pos++; + else + s = 0; + } +} + +void wavegen(std::span<short> buf) +{ + static unsigned i; + for (auto& s : buf) + { + unsigned x; + + if (i > 100) + x = 100 - i; + else + x = i; + + x *= 2 * SHRT_MAX / 100; + s = x - SHRT_MAX; + + if (++i > 200) + i = 0; + } +} + +void pulse_state_cb(pa_context* c, void*) +{ + std::cout << "pulse state = " << pa_context_get_state(c) << '\n'; + switch (pa_context_get_state(c)) + { + case PA_CONTEXT_READY: + ready = true; + break; + } +} + +void write_cb(pa_stream* s, size_t, void*) +{ + void* buf; + size_t len = -1; + pa_stream_begin_write(s, &buf, &len); + + auto v = (short*)buf; + play_sample({v, len/2}); + pa_stream_write(s, buf, len, nullptr, 0, PA_SEEK_RELATIVE); +} + +void underflow_cb(pa_stream* s, void*) +{ + std::cout << __func__ << '\n'; +} + +struct mainloop +{ + mainloop(void) + : h(pa_mainloop_new(), &pa_mainloop_free) + { + assert(h); + } + + pa_mainloop_api* get_api(void) + { + return pa_mainloop_get_api(h.get()); + } + + operator pa_mainloop*(void) + { + return h.get(); + } + +private: + std::unique_ptr<pa_mainloop, void(*)(pa_mainloop*)> h; +}; + +struct context +{ + context(pa_mainloop_api* api, char const* name) + : h(pa_context_new_with_proplist(api, name, nullptr), pa_context_unref) + { + assert(h); + } + + operator pa_context*(void) + { + return h.get(); + } + +private: + std::unique_ptr<pa_context, void(*)(pa_context*)> h; +}; + +int main(void) +{ + { + std::ifstream file{"res/pluck.mono44100s16le", std::ios::binary | std::ios::ate}; + assert(file); + auto size = file.tellg(); + sample.resize(size/2); + file.seekg(0); + assert(file.read((char*)sample.data(), size)); + pos = sample.cbegin(); + } + + int latency = 20000; + + mainloop mainloop{}; + auto api = mainloop.get_api(); + + context ctx{api, "pulse-test"}; + assert(ctx); + + pa_context_set_state_callback(ctx, pulse_state_cb, nullptr); + if (pa_context_connect(ctx, nullptr, PA_CONTEXT_NOAUTOSPAWN, nullptr)) + perror("pa_context_connect"); + + while(not ready) + pa_mainloop_iterate(mainloop, 0, nullptr); + + pa_sample_spec ss = + { + .format = PA_SAMPLE_S16LE, + .rate = 44100, + .channels = 1, + }; + auto playstream = pa_stream_new(ctx, "playback", &ss, nullptr); + assert(playstream); + + pa_stream_set_write_callback(playstream, write_cb, nullptr); + pa_stream_set_underflow_callback(playstream, underflow_cb, nullptr); + + uint32_t len = pa_usec_to_bytes(latency, &ss); + uint32_t minreq = pa_usec_to_bytes(0, &ss); + pa_buffer_attr bufattr = + { + .maxlength = (uint32_t)-1, + .tlength = (uint32_t)-1, + .prebuf = (uint32_t)-1, + .minreq = (uint32_t)-1, + .fragsize = (uint32_t)-1, + }; + auto r = pa_stream_connect_playback(playstream, nullptr, &bufattr, PA_STREAM_NOFLAGS /*PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE*/, nullptr, nullptr); + assert(not r); + + pa_mainloop_run(mainloop, nullptr); + + pa_context_disconnect(ctx); + + return 0; +}