pulse-async-client.cpp (3025B)
1 #include <pulse/pulseaudio.h> 2 #include <unistd.h> 3 4 #include <iostream> 5 #include <vector> 6 #include <span> 7 #include <memory> 8 #include <fstream> 9 10 bool ready = false; 11 12 std::vector<short> sample; 13 std::vector<short>::const_iterator pos; 14 15 void play_sample(std::span<short> buf) 16 { 17 for (auto& s : buf) 18 { 19 if (pos != sample.cend()) 20 s = *pos++; 21 else 22 s = 0; 23 } 24 } 25 26 void wavegen(std::span<short> buf) 27 { 28 static unsigned i; 29 for (auto& s : buf) 30 { 31 unsigned x; 32 33 if (i > 100) 34 x = 100 - i; 35 else 36 x = i; 37 38 x *= 2 * SHRT_MAX / 100; 39 s = x - SHRT_MAX; 40 41 if (++i > 200) 42 i = 0; 43 } 44 } 45 46 void pulse_state_cb(pa_context* c, void*) 47 { 48 std::cout << "pulse state = " << pa_context_get_state(c) << '\n'; 49 if (pa_context_get_state(c) == PA_CONTEXT_READY) 50 ready = true; 51 } 52 53 void write_cb(pa_stream* s, size_t, void*) 54 { 55 void* buf; 56 size_t len = -1; 57 pa_stream_begin_write(s, &buf, &len); 58 59 auto v = (short*)buf; 60 play_sample({v, len/2}); 61 pa_stream_write(s, buf, len, nullptr, 0, PA_SEEK_RELATIVE); 62 } 63 64 void underflow_cb(pa_stream*, void*) 65 { 66 std::cout << __func__ << '\n'; 67 } 68 69 struct mainloop 70 { 71 mainloop(void) 72 : h(pa_mainloop_new(), &pa_mainloop_free) 73 { 74 assert(h); 75 } 76 77 pa_mainloop_api* get_api(void) 78 { 79 return pa_mainloop_get_api(h.get()); 80 } 81 82 operator pa_mainloop*(void) 83 { 84 return h.get(); 85 } 86 87 private: 88 std::unique_ptr<pa_mainloop, void(*)(pa_mainloop*)> h; 89 }; 90 91 struct context 92 { 93 context(pa_mainloop_api* api, char const* name) 94 : h(pa_context_new_with_proplist(api, name, nullptr), pa_context_unref) 95 { 96 assert(h); 97 } 98 99 operator pa_context*(void) 100 { 101 return h.get(); 102 } 103 104 private: 105 std::unique_ptr<pa_context, void(*)(pa_context*)> h; 106 }; 107 108 int main(void) 109 { 110 { 111 std::ifstream file{"res/pluck.mono44100s16le", std::ios::binary | std::ios::ate}; 112 assert(file); 113 auto size = file.tellg(); 114 sample.resize(size/2); 115 file.seekg(0); 116 assert(file.read((char*)sample.data(), size)); 117 pos = sample.cbegin(); 118 } 119 120 mainloop mainloop{}; 121 auto api = mainloop.get_api(); 122 123 context ctx{api, "pulse-test"}; 124 assert(ctx); 125 126 pa_context_set_state_callback(ctx, pulse_state_cb, nullptr); 127 if (pa_context_connect(ctx, nullptr, PA_CONTEXT_NOAUTOSPAWN, nullptr)) 128 perror("pa_context_connect"); 129 130 while(not ready) 131 pa_mainloop_iterate(mainloop, 0, nullptr); 132 133 pa_sample_spec ss = 134 { 135 .format = PA_SAMPLE_S16LE, 136 .rate = 44100, 137 .channels = 1, 138 }; 139 auto playstream = pa_stream_new(ctx, "playback", &ss, nullptr); 140 assert(playstream); 141 142 pa_stream_set_write_callback(playstream, write_cb, nullptr); 143 pa_stream_set_underflow_callback(playstream, underflow_cb, nullptr); 144 145 pa_buffer_attr bufattr = 146 { 147 .maxlength = (uint32_t)-1, 148 .tlength = (uint32_t)-1, 149 .prebuf = (uint32_t)-1, 150 .minreq = (uint32_t)-1, 151 .fragsize = (uint32_t)-1, 152 }; 153 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); 154 assert(not r); 155 156 pa_mainloop_run(mainloop, nullptr); 157 158 pa_context_disconnect(ctx); 159 160 return 0; 161 }