PulseAudioWrappers.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. /*
  2. * Copyright (c) 2023, Gregory Bertilson <zaggy1024@gmail.com>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include "PulseAudioWrappers.h"
  7. #include <AK/WeakPtr.h>
  8. #include <LibThreading/Mutex.h>
  9. namespace Audio {
  10. ErrorOr<NonnullRefPtr<PulseAudioContext>> PulseAudioContext::instance()
  11. {
  12. // Use a weak pointer to allow the context to be shut down if we stop outputting audio.
  13. static WeakPtr<PulseAudioContext> the_instance;
  14. static Threading::Mutex instantiation_mutex;
  15. auto instantiation_locker = Threading::MutexLocker(instantiation_mutex);
  16. RefPtr<PulseAudioContext> strong_instance_pointer = the_instance.strong_ref();
  17. if (strong_instance_pointer == nullptr) {
  18. auto* main_loop = pa_threaded_mainloop_new();
  19. if (main_loop == nullptr)
  20. return Error::from_string_literal("Failed to create PulseAudio main loop");
  21. auto* api = pa_threaded_mainloop_get_api(main_loop);
  22. if (api == nullptr)
  23. return Error::from_string_literal("Failed to get PulseAudio API");
  24. auto* context = pa_context_new(api, "Ladybird");
  25. if (context == nullptr)
  26. return Error::from_string_literal("Failed to get PulseAudio connection context");
  27. strong_instance_pointer = make_ref_counted<PulseAudioContext>(main_loop, api, context);
  28. // Set a callback to signal ourselves to wake when the state changes, so that we can
  29. // synchronously wait for the connection.
  30. pa_context_set_state_callback(
  31. context, [](pa_context*, void* user_data) {
  32. static_cast<PulseAudioContext*>(user_data)->signal_to_wake();
  33. },
  34. strong_instance_pointer.ptr());
  35. if (auto error = pa_context_connect(context, nullptr, PA_CONTEXT_NOFLAGS, nullptr); error < 0) {
  36. warnln("Starting PulseAudio context connection failed with error: {}", pulse_audio_error_to_string(static_cast<PulseAudioErrorCode>(-error)));
  37. return Error::from_string_literal("Error while starting PulseAudio daemon connection");
  38. }
  39. if (auto error = pa_threaded_mainloop_start(main_loop); error < 0) {
  40. warnln("Starting PulseAudio main loop failed with error: {}", pulse_audio_error_to_string(static_cast<PulseAudioErrorCode>(-error)));
  41. return Error::from_string_literal("Failed to start PulseAudio main loop");
  42. }
  43. {
  44. auto locker = strong_instance_pointer->main_loop_locker();
  45. while (true) {
  46. bool is_ready = false;
  47. switch (strong_instance_pointer->get_connection_state()) {
  48. case PulseAudioContextState::Connecting:
  49. case PulseAudioContextState::Authorizing:
  50. case PulseAudioContextState::SettingName:
  51. break;
  52. case PulseAudioContextState::Ready:
  53. is_ready = true;
  54. break;
  55. case PulseAudioContextState::Failed:
  56. warnln("PulseAudio server connection failed with error: {}", pulse_audio_error_to_string(strong_instance_pointer->get_last_error()));
  57. return Error::from_string_literal("Failed to connect to PulseAudio server");
  58. case PulseAudioContextState::Unconnected:
  59. case PulseAudioContextState::Terminated:
  60. VERIFY_NOT_REACHED();
  61. break;
  62. }
  63. if (is_ready)
  64. break;
  65. strong_instance_pointer->wait_for_signal();
  66. }
  67. pa_context_set_state_callback(context, nullptr, nullptr);
  68. }
  69. the_instance = strong_instance_pointer;
  70. }
  71. return strong_instance_pointer.release_nonnull();
  72. }
  73. PulseAudioContext::PulseAudioContext(pa_threaded_mainloop* main_loop, pa_mainloop_api* api, pa_context* context)
  74. : m_main_loop(main_loop)
  75. , m_api(api)
  76. , m_context(context)
  77. {
  78. }
  79. PulseAudioContext::~PulseAudioContext()
  80. {
  81. pa_context_disconnect(m_context);
  82. pa_context_unref(m_context);
  83. pa_threaded_mainloop_stop(m_main_loop);
  84. pa_threaded_mainloop_free(m_main_loop);
  85. }
  86. bool PulseAudioContext::current_thread_is_main_loop_thread()
  87. {
  88. return static_cast<bool>(pa_threaded_mainloop_in_thread(m_main_loop));
  89. }
  90. void PulseAudioContext::lock_main_loop()
  91. {
  92. if (!current_thread_is_main_loop_thread())
  93. pa_threaded_mainloop_lock(m_main_loop);
  94. }
  95. void PulseAudioContext::unlock_main_loop()
  96. {
  97. if (!current_thread_is_main_loop_thread())
  98. pa_threaded_mainloop_unlock(m_main_loop);
  99. }
  100. void PulseAudioContext::wait_for_signal()
  101. {
  102. pa_threaded_mainloop_wait(m_main_loop);
  103. }
  104. void PulseAudioContext::signal_to_wake()
  105. {
  106. pa_threaded_mainloop_signal(m_main_loop, 0);
  107. }
  108. PulseAudioContextState PulseAudioContext::get_connection_state()
  109. {
  110. return static_cast<PulseAudioContextState>(pa_context_get_state(m_context));
  111. }
  112. bool PulseAudioContext::connection_is_good()
  113. {
  114. return PA_CONTEXT_IS_GOOD(pa_context_get_state(m_context));
  115. }
  116. PulseAudioErrorCode PulseAudioContext::get_last_error()
  117. {
  118. return static_cast<PulseAudioErrorCode>(pa_context_errno(m_context));
  119. }
  120. #define STREAM_SIGNAL_CALLBACK(stream) \
  121. [](auto*, int, void* user_data) { \
  122. static_cast<PulseAudioStream*>(user_data)->m_context->signal_to_wake(); \
  123. }, \
  124. (stream)
  125. ErrorOr<NonnullRefPtr<PulseAudioStream>> PulseAudioContext::create_stream(OutputState initial_state, u32 sample_rate, u8 channels, u32 target_latency_ms, PulseAudioDataRequestCallback write_callback)
  126. {
  127. auto locker = main_loop_locker();
  128. VERIFY(get_connection_state() == PulseAudioContextState::Ready);
  129. pa_sample_spec sample_specification {
  130. // FIXME: Support more audio sample types.
  131. __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ ? PA_SAMPLE_FLOAT32LE : PA_SAMPLE_FLOAT32BE,
  132. sample_rate,
  133. channels,
  134. };
  135. // Check the sample specification and channel map here. These are also checked by stream_new(),
  136. // but we can return a more accurate error if we check beforehand.
  137. if (pa_sample_spec_valid(&sample_specification) == 0)
  138. return Error::from_string_literal("PulseAudio sample specification is invalid");
  139. pa_channel_map channel_map;
  140. if (pa_channel_map_init_auto(&channel_map, sample_specification.channels, PA_CHANNEL_MAP_DEFAULT) == 0) {
  141. warnln("Getting default PulseAudio channel map failed with error: {}", pulse_audio_error_to_string(get_last_error()));
  142. return Error::from_string_literal("Failed to get default PulseAudio channel map");
  143. }
  144. // Create the stream object and set a callback to signal ourselves to wake when the stream changes states,
  145. // allowing us to wait synchronously for it to become Ready or Failed.
  146. auto* stream = pa_stream_new_with_proplist(m_context, "Audio Stream", &sample_specification, &channel_map, nullptr);
  147. if (stream == nullptr) {
  148. warnln("Instantiating PulseAudio stream failed with error: {}", pulse_audio_error_to_string(get_last_error()));
  149. return Error::from_string_literal("Failed to create PulseAudio stream");
  150. }
  151. pa_stream_set_state_callback(
  152. stream, [](pa_stream*, void* user_data) {
  153. static_cast<PulseAudioContext*>(user_data)->signal_to_wake();
  154. },
  155. this);
  156. auto stream_wrapper = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) PulseAudioStream(NonnullRefPtr(*this), stream)));
  157. stream_wrapper->m_write_callback = move(write_callback);
  158. pa_stream_set_write_callback(
  159. stream, [](pa_stream* stream, size_t bytes_to_write, void* user_data) {
  160. auto& stream_wrapper = *static_cast<PulseAudioStream*>(user_data);
  161. VERIFY(stream_wrapper.m_stream == stream);
  162. stream_wrapper.on_write_requested(bytes_to_write);
  163. },
  164. stream_wrapper.ptr());
  165. // Borrowing logic from cubeb to set reasonable buffer sizes for a target latency:
  166. // https://searchfox.org/mozilla-central/rev/3b707c8fd7e978eebf24279ee51ccf07895cfbcb/third_party/rust/cubeb-sys/libcubeb/src/cubeb_pulse.c#910-927
  167. pa_buffer_attr buffer_attributes;
  168. buffer_attributes.maxlength = -1;
  169. buffer_attributes.prebuf = -1;
  170. buffer_attributes.tlength = target_latency_ms * sample_rate / 1000;
  171. buffer_attributes.minreq = buffer_attributes.tlength / 4;
  172. buffer_attributes.fragsize = buffer_attributes.minreq;
  173. auto flags = static_cast<pa_stream_flags>(PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_RELATIVE_VOLUME);
  174. if (initial_state == OutputState::Suspended) {
  175. stream_wrapper->m_suspended = true;
  176. flags = static_cast<pa_stream_flags>(static_cast<u32>(flags) | PA_STREAM_START_CORKED);
  177. }
  178. // This is a workaround for an issue with starting the stream corked, see PulseAudioPlaybackStream::total_time_played().
  179. pa_stream_set_started_callback(
  180. stream, [](pa_stream* stream, void* user_data) {
  181. static_cast<PulseAudioStream*>(user_data)->m_started_playback = true;
  182. pa_stream_set_started_callback(stream, nullptr, nullptr);
  183. },
  184. stream_wrapper.ptr());
  185. pa_stream_set_underflow_callback(
  186. stream, [](pa_stream*, void* user_data) {
  187. auto& stream = *static_cast<PulseAudioStream*>(user_data);
  188. if (stream.m_underrun_callback)
  189. stream.m_underrun_callback();
  190. },
  191. stream_wrapper.ptr());
  192. if (auto error = pa_stream_connect_playback(stream, nullptr, &buffer_attributes, flags, nullptr, nullptr); error != 0) {
  193. warnln("Failed to start PulseAudio stream connection with error: {}", pulse_audio_error_to_string(static_cast<PulseAudioErrorCode>(error)));
  194. return Error::from_string_literal("Error while connecting the PulseAudio stream");
  195. }
  196. // FIXME: This should be asynchronous if connection can take longer than a fraction of a second.
  197. while (true) {
  198. bool is_ready = false;
  199. switch (stream_wrapper->get_connection_state()) {
  200. case PulseAudioStreamState::Creating:
  201. break;
  202. case PulseAudioStreamState::Ready:
  203. is_ready = true;
  204. break;
  205. case PulseAudioStreamState::Failed:
  206. warnln("PulseAudio stream connection failed with error: {}", pulse_audio_error_to_string(get_last_error()));
  207. return Error::from_string_literal("Failed to connect to PulseAudio daemon");
  208. case PulseAudioStreamState::Unconnected:
  209. case PulseAudioStreamState::Terminated:
  210. VERIFY_NOT_REACHED();
  211. break;
  212. }
  213. if (is_ready)
  214. break;
  215. wait_for_signal();
  216. }
  217. return stream_wrapper;
  218. }
  219. PulseAudioStream::~PulseAudioStream()
  220. {
  221. pa_stream_unref(m_stream);
  222. }
  223. PulseAudioStreamState PulseAudioStream::get_connection_state()
  224. {
  225. return static_cast<PulseAudioStreamState>(pa_stream_get_state(m_stream));
  226. }
  227. bool PulseAudioStream::connection_is_good()
  228. {
  229. return PA_STREAM_IS_GOOD(pa_stream_get_state(m_stream));
  230. }
  231. void PulseAudioStream::set_underrun_callback(Function<void()> callback)
  232. {
  233. auto locker = m_context->main_loop_locker();
  234. m_underrun_callback = move(callback);
  235. }
  236. u32 PulseAudioStream::sample_rate()
  237. {
  238. return pa_stream_get_sample_spec(m_stream)->rate;
  239. }
  240. size_t PulseAudioStream::sample_size()
  241. {
  242. return pa_sample_size(pa_stream_get_sample_spec(m_stream));
  243. }
  244. size_t PulseAudioStream::frame_size()
  245. {
  246. return pa_frame_size(pa_stream_get_sample_spec(m_stream));
  247. }
  248. u8 PulseAudioStream::channel_count()
  249. {
  250. return pa_stream_get_sample_spec(m_stream)->channels;
  251. }
  252. void PulseAudioStream::on_write_requested(size_t bytes_to_write)
  253. {
  254. VERIFY(m_write_callback);
  255. if (m_suspended)
  256. return;
  257. while (bytes_to_write > 0) {
  258. auto buffer = begin_write(bytes_to_write).release_value_but_fixme_should_propagate_errors();
  259. auto frame_size = this->frame_size();
  260. VERIFY(buffer.size() % frame_size == 0);
  261. auto written_buffer = m_write_callback(*this, buffer, buffer.size() / frame_size);
  262. if (written_buffer.size() == 0) {
  263. cancel_write().release_value_but_fixme_should_propagate_errors();
  264. break;
  265. }
  266. bytes_to_write -= written_buffer.size();
  267. write(written_buffer).release_value_but_fixme_should_propagate_errors();
  268. }
  269. }
  270. ErrorOr<Bytes> PulseAudioStream::begin_write(size_t bytes_to_write)
  271. {
  272. void* data_pointer;
  273. size_t data_size = bytes_to_write;
  274. if (pa_stream_begin_write(m_stream, &data_pointer, &data_size) != 0 || data_pointer == nullptr)
  275. return Error::from_string_literal("Failed to get the playback stream's write buffer from PulseAudio");
  276. return Bytes { data_pointer, data_size };
  277. }
  278. ErrorOr<void> PulseAudioStream::write(ReadonlyBytes data)
  279. {
  280. if (pa_stream_write(m_stream, data.data(), data.size(), nullptr, 0, PA_SEEK_RELATIVE) != 0)
  281. return Error::from_string_literal("Failed to write data to PulseAudio playback stream");
  282. return {};
  283. }
  284. ErrorOr<void> PulseAudioStream::cancel_write()
  285. {
  286. if (pa_stream_cancel_write(m_stream) != 0)
  287. return Error::from_string_literal("Failed to get the playback stream's write buffer from PulseAudio");
  288. return {};
  289. }
  290. bool PulseAudioStream::is_suspended() const
  291. {
  292. return m_suspended;
  293. }
  294. StringView pulse_audio_error_to_string(PulseAudioErrorCode code)
  295. {
  296. if (code < PulseAudioErrorCode::OK || code >= PulseAudioErrorCode::Sentinel)
  297. return "Unknown error code"sv;
  298. char const* string = pa_strerror(static_cast<int>(code));
  299. return StringView { string, strlen(string) };
  300. }
  301. ErrorOr<void> PulseAudioStream::wait_for_operation(pa_operation* operation, StringView error_message)
  302. {
  303. while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
  304. m_context->wait_for_signal();
  305. if (!m_context->connection_is_good() || !this->connection_is_good()) {
  306. auto pulse_audio_error_name = pulse_audio_error_to_string(m_context->get_last_error());
  307. warnln("Encountered stream error: {}", pulse_audio_error_name);
  308. return Error::from_string_view(error_message);
  309. }
  310. pa_operation_unref(operation);
  311. return {};
  312. }
  313. ErrorOr<void> PulseAudioStream::drain_and_suspend()
  314. {
  315. auto locker = m_context->main_loop_locker();
  316. if (m_suspended)
  317. return {};
  318. m_suspended = true;
  319. if (pa_stream_is_corked(m_stream) > 0)
  320. return {};
  321. TRY(wait_for_operation(pa_stream_drain(m_stream, STREAM_SIGNAL_CALLBACK(this)), "Draining PulseAudio stream failed"sv));
  322. TRY(wait_for_operation(pa_stream_cork(m_stream, 1, STREAM_SIGNAL_CALLBACK(this)), "Corking PulseAudio stream after drain failed"sv));
  323. return {};
  324. }
  325. ErrorOr<void> PulseAudioStream::flush_and_suspend()
  326. {
  327. auto locker = m_context->main_loop_locker();
  328. if (m_suspended)
  329. return {};
  330. m_suspended = true;
  331. if (pa_stream_is_corked(m_stream) > 0)
  332. return {};
  333. TRY(wait_for_operation(pa_stream_flush(m_stream, STREAM_SIGNAL_CALLBACK(this)), "Flushing PulseAudio stream failed"sv));
  334. TRY(wait_for_operation(pa_stream_cork(m_stream, 1, STREAM_SIGNAL_CALLBACK(this)), "Corking PulseAudio stream after flush failed"sv));
  335. return {};
  336. }
  337. ErrorOr<void> PulseAudioStream::resume()
  338. {
  339. auto locker = m_context->main_loop_locker();
  340. if (!m_suspended)
  341. return {};
  342. m_suspended = false;
  343. TRY(wait_for_operation(pa_stream_cork(m_stream, 0, STREAM_SIGNAL_CALLBACK(this)), "Uncorking PulseAudio stream failed"sv));
  344. // Defer a write to the playback buffer on the PulseAudio main loop. Otherwise, playback will not
  345. // begin again, despite the fact that we uncorked.
  346. // NOTE: We ref here and then unref in the callback so that this stream will not be deleted until
  347. // it finishes.
  348. ref();
  349. pa_mainloop_api_once(
  350. m_context->m_api, [](pa_mainloop_api*, void* user_data) {
  351. auto& stream = *static_cast<PulseAudioStream*>(user_data);
  352. // NOTE: writable_size() returns -1 in case of an error. However, the value is still safe
  353. // since begin_write() will interpret -1 as a default parameter and choose a good size.
  354. auto bytes_to_write = pa_stream_writable_size(stream.m_stream);
  355. stream.on_write_requested(bytes_to_write);
  356. stream.unref();
  357. },
  358. this);
  359. return {};
  360. }
  361. ErrorOr<Duration> PulseAudioStream::total_time_played()
  362. {
  363. auto locker = m_context->main_loop_locker();
  364. // NOTE: This is a workaround for a PulseAudio issue. When a stream is started corked,
  365. // the time smoother doesn't seem to be aware of it, so it will return the time
  366. // since the stream was connected. Once the playback actually starts, the time
  367. // resets back to zero. However, since we request monotonically-increasing time,
  368. // this means that the smoother will register that it had a larger time before,
  369. // and return that time instead, until we reach a timestamp greater than the
  370. // last-returned time. If we never call pa_stream_get_time() until after giving
  371. // the stream its first samples, the issue never occurs.
  372. if (!m_started_playback)
  373. return Duration::zero();
  374. pa_usec_t time = 0;
  375. auto error = pa_stream_get_time(m_stream, &time);
  376. if (error == -PA_ERR_NODATA)
  377. return Duration::zero();
  378. if (error != 0)
  379. return Error::from_string_literal("Failed to get time from PulseAudio stream");
  380. if (time > NumericLimits<i64>::max()) {
  381. warnln("WARNING: Audio time is too large!");
  382. time -= NumericLimits<i64>::max();
  383. }
  384. return Duration::from_microseconds(static_cast<i64>(time));
  385. }
  386. ErrorOr<void> PulseAudioStream::set_volume(double volume)
  387. {
  388. auto locker = m_context->main_loop_locker();
  389. auto index = pa_stream_get_index(m_stream);
  390. if (index == PA_INVALID_INDEX)
  391. return Error::from_string_literal("Failed to get PulseAudio stream index while setting volume");
  392. auto pulse_volume = pa_sw_volume_from_linear(volume);
  393. pa_cvolume per_channel_volumes;
  394. pa_cvolume_set(&per_channel_volumes, channel_count(), pulse_volume);
  395. auto* operation = pa_context_set_sink_input_volume(m_context->m_context, index, &per_channel_volumes, STREAM_SIGNAL_CALLBACK(this));
  396. return wait_for_operation(operation, "Failed to set PulseAudio stream volume"sv);
  397. }
  398. }