PulseAudioWrappers.cpp 19 KB

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