PulseAudioWrappers.h 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. /*
  2. * Copyright (c) 2023, Gregory Bertilson <zaggy1024@gmail.com>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #pragma once
  7. #include <AK/AtomicRefCounted.h>
  8. #include <AK/Error.h>
  9. #include <AK/NonnullRefPtr.h>
  10. #include <AK/Time.h>
  11. #include <LibAudio/Forward.h>
  12. #include <LibAudio/PlaybackStream.h>
  13. #include <LibAudio/SampleFormats.h>
  14. #include <LibThreading/Thread.h>
  15. #include <pulse/pulseaudio.h>
  16. namespace Audio {
  17. class PulseAudioStream;
  18. enum class PulseAudioContextState {
  19. Unconnected = PA_CONTEXT_UNCONNECTED,
  20. Connecting = PA_CONTEXT_CONNECTING,
  21. Authorizing = PA_CONTEXT_AUTHORIZING,
  22. SettingName = PA_CONTEXT_SETTING_NAME,
  23. Ready = PA_CONTEXT_READY,
  24. Failed = PA_CONTEXT_FAILED,
  25. Terminated = PA_CONTEXT_TERMINATED,
  26. };
  27. enum class PulseAudioErrorCode;
  28. using PulseAudioDataRequestCallback = Function<ReadonlyBytes(PulseAudioStream&, Bytes buffer, size_t sample_count)>;
  29. // A wrapper around the PulseAudio main loop and context structs.
  30. // Generally, only one instance of this should be needed for a single process.
  31. class PulseAudioContext
  32. : public AtomicRefCounted<PulseAudioContext>
  33. , public Weakable<PulseAudioContext> {
  34. public:
  35. static AK::WeakPtr<PulseAudioContext> weak_instance();
  36. static ErrorOr<NonnullRefPtr<PulseAudioContext>> instance();
  37. explicit PulseAudioContext(pa_threaded_mainloop*, pa_mainloop_api*, pa_context*);
  38. PulseAudioContext(PulseAudioContext const& other) = delete;
  39. ~PulseAudioContext();
  40. bool current_thread_is_main_loop_thread();
  41. void lock_main_loop();
  42. void unlock_main_loop();
  43. [[nodiscard]] auto main_loop_locker()
  44. {
  45. lock_main_loop();
  46. return ScopeGuard([this]() { unlock_main_loop(); });
  47. }
  48. // Waits for signal_to_wake() to be called.
  49. // This must be called with the main loop locked.
  50. void wait_for_signal();
  51. // Signals to wake all threads from calls to signal_to_wake()
  52. void signal_to_wake();
  53. PulseAudioContextState get_connection_state();
  54. bool connection_is_good();
  55. PulseAudioErrorCode get_last_error();
  56. ErrorOr<NonnullRefPtr<PulseAudioStream>> create_stream(OutputState initial_state, u32 sample_rate, u8 channels, u32 target_latency_ms, PulseAudioDataRequestCallback write_callback);
  57. private:
  58. friend class PulseAudioStream;
  59. pa_threaded_mainloop* m_main_loop { nullptr };
  60. pa_mainloop_api* m_api { nullptr };
  61. pa_context* m_context;
  62. };
  63. enum class PulseAudioStreamState {
  64. Unconnected = PA_STREAM_UNCONNECTED,
  65. Creating = PA_STREAM_CREATING,
  66. Ready = PA_STREAM_READY,
  67. Failed = PA_STREAM_FAILED,
  68. Terminated = PA_STREAM_TERMINATED,
  69. };
  70. class PulseAudioStream : public AtomicRefCounted<PulseAudioStream> {
  71. public:
  72. static constexpr bool start_corked = true;
  73. ~PulseAudioStream();
  74. PulseAudioStreamState get_connection_state();
  75. bool connection_is_good();
  76. // Sets the callback to be run when the server consumes more of the buffer than
  77. // has been written yet.
  78. void set_underrun_callback(Function<void()>);
  79. u32 sample_rate();
  80. size_t sample_size();
  81. size_t frame_size();
  82. u8 channel_count();
  83. // Gets a data buffer that can be written to and then passed back to PulseAudio through
  84. // the write() function. This avoids a copy vs directly calling write().
  85. ErrorOr<Bytes> begin_write(size_t bytes_to_write = NumericLimits<size_t>::max());
  86. // Writes a data buffer to the playback stream.
  87. ErrorOr<void> write(ReadonlyBytes data);
  88. // Cancels the previous begin_write() call.
  89. ErrorOr<void> cancel_write();
  90. bool is_suspended() const;
  91. // Plays back all buffered data and corks the stream. Until resume() is called, no data
  92. // will be written to the stream.
  93. ErrorOr<void> drain_and_suspend();
  94. // Drops all buffered data and corks the stream. Until resume() is called, no data will
  95. // be written to the stream.
  96. ErrorOr<void> flush_and_suspend();
  97. // Uncorks the stream and forces data to be written to the buffers to force playback to
  98. // resume as soon as possible.
  99. ErrorOr<void> resume();
  100. ErrorOr<Duration> total_time_played();
  101. ErrorOr<void> set_volume(double volume);
  102. PulseAudioContext& context() { return *m_context; }
  103. private:
  104. friend class PulseAudioContext;
  105. explicit PulseAudioStream(NonnullRefPtr<PulseAudioContext>&& context, pa_stream* stream)
  106. : m_context(context)
  107. , m_stream(stream)
  108. {
  109. }
  110. PulseAudioStream(PulseAudioStream const& other) = delete;
  111. ErrorOr<void> wait_for_operation(pa_operation*, StringView error_message);
  112. void on_write_requested(size_t bytes_to_write);
  113. NonnullRefPtr<PulseAudioContext> m_context;
  114. pa_stream* m_stream { nullptr };
  115. bool m_started_playback { false };
  116. PulseAudioDataRequestCallback m_write_callback { nullptr };
  117. // Determines whether we will allow the write callback to run. This should only be true
  118. // if the stream is becoming or is already corked.
  119. bool m_suspended { false };
  120. Function<void()> m_underrun_callback;
  121. };
  122. enum class PulseAudioErrorCode {
  123. OK = 0,
  124. AccessFailure,
  125. UnknownCommand,
  126. InvalidArgument,
  127. EntityExists,
  128. NoSuchEntity,
  129. ConnectionRefused,
  130. ProtocolError,
  131. Timeout,
  132. NoAuthenticationKey,
  133. InternalError,
  134. ConnectionTerminated,
  135. EntityKilled,
  136. InvalidServer,
  137. NoduleInitFailed,
  138. BadState,
  139. NoData,
  140. IncompatibleProtocolVersion,
  141. DataTooLarge,
  142. NotSupported,
  143. Unknown,
  144. NoExtension,
  145. Obsolete,
  146. NotImplemented,
  147. CalledFromFork,
  148. IOError,
  149. Busy,
  150. Sentinel
  151. };
  152. StringView pulse_audio_error_to_string(PulseAudioErrorCode code);
  153. }