PlaybackStreamAudioUnit.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. /*
  2. * Copyright (c) 2023, Andrew Kaster <akaster@serenityos.org>
  3. * Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <AK/Atomic.h>
  8. #include <AK/SourceLocation.h>
  9. #include <LibAudio/PlaybackStreamAudioUnit.h>
  10. #include <LibCore/SharedCircularQueue.h>
  11. #include <LibCore/ThreadedPromise.h>
  12. // Several AK types conflict with MacOS types.
  13. #define FixedPoint FixedPointMacOS
  14. #define Duration DurationMacOS
  15. #include <AudioUnit/AudioUnit.h>
  16. #undef FixedPoint
  17. #undef Duration
  18. namespace Audio {
  19. static constexpr AudioUnitElement AUDIO_UNIT_OUTPUT_BUS = 0;
  20. static void log_os_error_code(OSStatus error_code, SourceLocation location = SourceLocation::current());
  21. #define AU_TRY(expression) \
  22. ({ \
  23. /* Ignore -Wshadow to allow nesting the macro. */ \
  24. AK_IGNORE_DIAGNOSTIC("-Wshadow", auto&& _temporary_result = (expression)); \
  25. if (_temporary_result != noErr) [[unlikely]] { \
  26. log_os_error_code(_temporary_result); \
  27. return Error::from_errno(_temporary_result); \
  28. } \
  29. })
  30. struct AudioTask {
  31. enum class Type {
  32. Play,
  33. Pause,
  34. PauseAndDiscard,
  35. Volume,
  36. };
  37. void resolve(Duration time)
  38. {
  39. promise.visit(
  40. [](Empty) { VERIFY_NOT_REACHED(); },
  41. [&](NonnullRefPtr<Core::ThreadedPromise<void>>& promise) {
  42. promise->resolve();
  43. },
  44. [&](NonnullRefPtr<Core::ThreadedPromise<Duration>>& promise) {
  45. promise->resolve(move(time));
  46. });
  47. }
  48. void reject(OSStatus error)
  49. {
  50. log_os_error_code(error);
  51. promise.visit(
  52. [](Empty) { VERIFY_NOT_REACHED(); },
  53. [error](auto& promise) {
  54. promise->reject(Error::from_errno(error));
  55. });
  56. }
  57. Type type;
  58. Variant<Empty, NonnullRefPtr<Core::ThreadedPromise<void>>, NonnullRefPtr<Core::ThreadedPromise<Duration>>> promise;
  59. Optional<double> data {};
  60. };
  61. class AudioState : public RefCounted<AudioState> {
  62. public:
  63. using AudioTaskQueue = Core::SharedSingleProducerCircularQueue<AudioTask>;
  64. static ErrorOr<NonnullRefPtr<AudioState>> create(AudioStreamBasicDescription description, PlaybackStream::AudioDataRequestCallback data_request_callback, OutputState initial_output_state)
  65. {
  66. auto task_queue = TRY(AudioTaskQueue::create());
  67. auto state = TRY(adopt_nonnull_ref_or_enomem(new (nothrow) AudioState(description, move(task_queue), move(data_request_callback), initial_output_state)));
  68. AudioComponentDescription component_description;
  69. component_description.componentType = kAudioUnitType_Output;
  70. component_description.componentSubType = kAudioUnitSubType_DefaultOutput;
  71. component_description.componentManufacturer = kAudioUnitManufacturer_Apple;
  72. component_description.componentFlags = 0;
  73. component_description.componentFlagsMask = 0;
  74. auto* component = AudioComponentFindNext(NULL, &component_description);
  75. AU_TRY(AudioComponentInstanceNew(component, &state->m_audio_unit));
  76. AU_TRY(AudioUnitSetProperty(
  77. state->m_audio_unit,
  78. kAudioUnitProperty_StreamFormat,
  79. kAudioUnitScope_Input,
  80. AUDIO_UNIT_OUTPUT_BUS,
  81. &description,
  82. sizeof(description)));
  83. AURenderCallbackStruct callbackStruct;
  84. callbackStruct.inputProc = &AudioState::on_audio_unit_buffer_request;
  85. callbackStruct.inputProcRefCon = state.ptr();
  86. AU_TRY(AudioUnitSetProperty(
  87. state->m_audio_unit,
  88. kAudioUnitProperty_SetRenderCallback,
  89. kAudioUnitScope_Global,
  90. AUDIO_UNIT_OUTPUT_BUS,
  91. &callbackStruct,
  92. sizeof(callbackStruct)));
  93. AU_TRY(AudioUnitInitialize(state->m_audio_unit));
  94. AU_TRY(AudioOutputUnitStart(state->m_audio_unit));
  95. return state;
  96. }
  97. ~AudioState()
  98. {
  99. if (m_audio_unit != nullptr)
  100. AudioOutputUnitStop(m_audio_unit);
  101. }
  102. ErrorOr<void> queue_task(AudioTask task)
  103. {
  104. return m_task_queue.blocking_enqueue(move(task), []() {
  105. usleep(10'000);
  106. });
  107. }
  108. Duration last_sample_time() const
  109. {
  110. return Duration::from_milliseconds(m_last_sample_time.load());
  111. }
  112. private:
  113. AudioState(AudioStreamBasicDescription description, AudioTaskQueue task_queue, PlaybackStream::AudioDataRequestCallback data_request_callback, OutputState initial_output_state)
  114. : m_description(description)
  115. , m_task_queue(move(task_queue))
  116. , m_paused(initial_output_state == OutputState::Playing ? Paused::No : Paused::Yes)
  117. , m_data_request_callback(move(data_request_callback))
  118. {
  119. }
  120. static OSStatus on_audio_unit_buffer_request(void* user_data, AudioUnitRenderActionFlags*, AudioTimeStamp const* time_stamp, UInt32 element, UInt32 frames_to_render, AudioBufferList* output_buffer_list)
  121. {
  122. VERIFY(element == AUDIO_UNIT_OUTPUT_BUS);
  123. VERIFY(output_buffer_list->mNumberBuffers == 1);
  124. auto& state = *static_cast<AudioState*>(user_data);
  125. VERIFY(time_stamp->mFlags & kAudioTimeStampSampleTimeValid);
  126. auto sample_time_seconds = time_stamp->mSampleTime / state.m_description.mSampleRate;
  127. auto last_sample_time = static_cast<i64>(sample_time_seconds * 1000.0);
  128. state.m_last_sample_time.store(last_sample_time);
  129. if (auto result = state.m_task_queue.dequeue(); result.is_error()) {
  130. VERIFY(result.error() == AudioTaskQueue::QueueStatus::Empty);
  131. } else {
  132. auto task = result.release_value();
  133. OSStatus error = noErr;
  134. switch (task.type) {
  135. case AudioTask::Type::Play:
  136. state.m_paused = Paused::No;
  137. break;
  138. case AudioTask::Type::Pause:
  139. state.m_paused = Paused::Yes;
  140. break;
  141. case AudioTask::Type::PauseAndDiscard:
  142. error = AudioUnitReset(state.m_audio_unit, kAudioUnitScope_Global, AUDIO_UNIT_OUTPUT_BUS);
  143. state.m_paused = Paused::Yes;
  144. break;
  145. case AudioTask::Type::Volume:
  146. VERIFY(task.data.has_value());
  147. error = AudioUnitSetParameter(state.m_audio_unit, kHALOutputParam_Volume, kAudioUnitScope_Global, 0, static_cast<float>(*task.data), 0);
  148. break;
  149. }
  150. if (error == noErr)
  151. task.resolve(Duration::from_milliseconds(last_sample_time));
  152. else
  153. task.reject(error);
  154. }
  155. Bytes output_buffer {
  156. reinterpret_cast<u8*>(output_buffer_list->mBuffers[0].mData),
  157. output_buffer_list->mBuffers[0].mDataByteSize
  158. };
  159. if (state.m_paused == Paused::No) {
  160. auto written_bytes = state.m_data_request_callback(output_buffer, PcmSampleFormat::Float32, frames_to_render);
  161. if (written_bytes.is_empty())
  162. state.m_paused = Paused::Yes;
  163. }
  164. if (state.m_paused == Paused::Yes)
  165. output_buffer.fill(0);
  166. return noErr;
  167. }
  168. AudioComponentInstance m_audio_unit { nullptr };
  169. AudioStreamBasicDescription m_description {};
  170. AudioTaskQueue m_task_queue;
  171. enum class Paused {
  172. Yes,
  173. No,
  174. };
  175. Paused m_paused { Paused::Yes };
  176. PlaybackStream::AudioDataRequestCallback m_data_request_callback;
  177. Atomic<i64> m_last_sample_time { 0 };
  178. };
  179. ErrorOr<NonnullRefPtr<PlaybackStream>> PlaybackStreamAudioUnit::create(OutputState initial_output_state, u32 sample_rate, u8 channels, u32, AudioDataRequestCallback&& data_request_callback)
  180. {
  181. AudioStreamBasicDescription description {};
  182. description.mFormatID = kAudioFormatLinearPCM;
  183. description.mFormatFlags = kLinearPCMFormatFlagIsFloat | kLinearPCMFormatFlagIsPacked;
  184. description.mSampleRate = sample_rate;
  185. description.mChannelsPerFrame = channels;
  186. description.mBitsPerChannel = sizeof(float) * 8;
  187. description.mBytesPerFrame = sizeof(float) * channels;
  188. description.mBytesPerPacket = description.mBytesPerFrame;
  189. description.mFramesPerPacket = 1;
  190. auto state = TRY(AudioState::create(description, move(data_request_callback), initial_output_state));
  191. return TRY(adopt_nonnull_ref_or_enomem(new (nothrow) PlaybackStreamAudioUnit(move(state))));
  192. }
  193. PlaybackStreamAudioUnit::PlaybackStreamAudioUnit(NonnullRefPtr<AudioState> impl)
  194. : m_state(move(impl))
  195. {
  196. }
  197. PlaybackStreamAudioUnit::~PlaybackStreamAudioUnit() = default;
  198. void PlaybackStreamAudioUnit::set_underrun_callback(Function<void()>)
  199. {
  200. // FIXME: Implement this.
  201. }
  202. NonnullRefPtr<Core::ThreadedPromise<Duration>> PlaybackStreamAudioUnit::resume()
  203. {
  204. auto promise = Core::ThreadedPromise<Duration>::create();
  205. AudioTask task { AudioTask::Type::Play, promise };
  206. if (auto result = m_state->queue_task(move(task)); result.is_error())
  207. promise->reject(result.release_error());
  208. return promise;
  209. }
  210. NonnullRefPtr<Core::ThreadedPromise<void>> PlaybackStreamAudioUnit::drain_buffer_and_suspend()
  211. {
  212. auto promise = Core::ThreadedPromise<void>::create();
  213. AudioTask task { AudioTask::Type::Pause, promise };
  214. if (auto result = m_state->queue_task(move(task)); result.is_error())
  215. promise->reject(result.release_error());
  216. return promise;
  217. }
  218. NonnullRefPtr<Core::ThreadedPromise<void>> PlaybackStreamAudioUnit::discard_buffer_and_suspend()
  219. {
  220. auto promise = Core::ThreadedPromise<void>::create();
  221. AudioTask task { AudioTask::Type::PauseAndDiscard, promise };
  222. if (auto result = m_state->queue_task(move(task)); result.is_error())
  223. promise->reject(result.release_error());
  224. return promise;
  225. }
  226. ErrorOr<Duration> PlaybackStreamAudioUnit::total_time_played()
  227. {
  228. return m_state->last_sample_time();
  229. }
  230. NonnullRefPtr<Core::ThreadedPromise<void>> PlaybackStreamAudioUnit::set_volume(double volume)
  231. {
  232. auto promise = Core::ThreadedPromise<void>::create();
  233. AudioTask task { AudioTask::Type::Volume, promise, volume };
  234. if (auto result = m_state->queue_task(move(task)); result.is_error())
  235. promise->reject(result.release_error());
  236. return promise;
  237. }
  238. void log_os_error_code([[maybe_unused]] OSStatus error_code, [[maybe_unused]] SourceLocation location)
  239. {
  240. #if AUDIO_DEBUG
  241. auto error_string = "Unknown error"sv;
  242. // Errors listed in AUComponent.h
  243. switch (error_code) {
  244. case kAudioUnitErr_InvalidProperty:
  245. error_string = "InvalidProperty"sv;
  246. break;
  247. case kAudioUnitErr_InvalidParameter:
  248. error_string = "InvalidParameter"sv;
  249. break;
  250. case kAudioUnitErr_InvalidElement:
  251. error_string = "InvalidElement"sv;
  252. break;
  253. case kAudioUnitErr_NoConnection:
  254. error_string = "NoConnection"sv;
  255. break;
  256. case kAudioUnitErr_FailedInitialization:
  257. error_string = "FailedInitialization"sv;
  258. break;
  259. case kAudioUnitErr_TooManyFramesToProcess:
  260. error_string = "TooManyFramesToProcess"sv;
  261. break;
  262. case kAudioUnitErr_InvalidFile:
  263. error_string = "InvalidFile"sv;
  264. break;
  265. case kAudioUnitErr_UnknownFileType:
  266. error_string = "UnknownFileType"sv;
  267. break;
  268. case kAudioUnitErr_FileNotSpecified:
  269. error_string = "FileNotSpecified"sv;
  270. break;
  271. case kAudioUnitErr_FormatNotSupported:
  272. error_string = "FormatNotSupported"sv;
  273. break;
  274. case kAudioUnitErr_Uninitialized:
  275. error_string = "Uninitialized"sv;
  276. break;
  277. case kAudioUnitErr_InvalidScope:
  278. error_string = "InvalidScope"sv;
  279. break;
  280. case kAudioUnitErr_PropertyNotWritable:
  281. error_string = "PropertyNotWritable"sv;
  282. break;
  283. case kAudioUnitErr_CannotDoInCurrentContext:
  284. error_string = "CannotDoInCurrentContext"sv;
  285. break;
  286. case kAudioUnitErr_InvalidPropertyValue:
  287. error_string = "InvalidPropertyValue"sv;
  288. break;
  289. case kAudioUnitErr_PropertyNotInUse:
  290. error_string = "PropertyNotInUse"sv;
  291. break;
  292. case kAudioUnitErr_Initialized:
  293. error_string = "Initialized"sv;
  294. break;
  295. case kAudioUnitErr_InvalidOfflineRender:
  296. error_string = "InvalidOfflineRender"sv;
  297. break;
  298. case kAudioUnitErr_Unauthorized:
  299. error_string = "Unauthorized"sv;
  300. break;
  301. case kAudioUnitErr_MIDIOutputBufferFull:
  302. error_string = "MIDIOutputBufferFull"sv;
  303. break;
  304. case kAudioComponentErr_InstanceTimedOut:
  305. error_string = "InstanceTimedOut"sv;
  306. break;
  307. case kAudioComponentErr_InstanceInvalidated:
  308. error_string = "InstanceInvalidated"sv;
  309. break;
  310. case kAudioUnitErr_RenderTimeout:
  311. error_string = "RenderTimeout"sv;
  312. break;
  313. case kAudioUnitErr_ExtensionNotFound:
  314. error_string = "ExtensionNotFound"sv;
  315. break;
  316. case kAudioUnitErr_InvalidParameterValue:
  317. error_string = "InvalidParameterValue"sv;
  318. break;
  319. case kAudioUnitErr_InvalidFilePath:
  320. error_string = "InvalidFilePath"sv;
  321. break;
  322. case kAudioUnitErr_MissingKey:
  323. error_string = "MissingKey"sv;
  324. break;
  325. default:
  326. break;
  327. }
  328. warnln("{}: Audio Unit error {}: {}", location, error_code, error_string);
  329. #endif
  330. }
  331. }