/* * Copyright (c) 2024, Olekoop * * SPDX-License-Identifier: BSD-2-Clause */ #define AK_DONT_REPLACE_STD #include #include #include #include #include #include #include namespace Audio { class OboeCallback : public oboe::AudioStreamDataCallback { public: virtual oboe::DataCallbackResult onAudioReady(oboe::AudioStream* oboeStream, void* audioData, int32_t numFrames) { Bytes output_buffer { reinterpret_cast(audioData), static_cast(numFrames * oboeStream->getChannelCount() * sizeof(float)) }; auto written_bytes = m_data_request_callback(output_buffer, PcmSampleFormat::Float32, numFrames); if (written_bytes.is_empty()) return oboe::DataCallbackResult::Stop; auto timestamp = oboeStream->getTimestamp(CLOCK_MONOTONIC); if (timestamp == oboe::Result::OK) { m_number_of_samples_enqueued = timestamp.value().position; } else { // Fallback for OpenSLES m_number_of_samples_enqueued += numFrames; } auto last_sample_time = static_cast(m_number_of_samples_enqueued / oboeStream->getSampleRate()); m_last_sample_time.store(last_sample_time); float* output = (float*)audioData; for (int frames = 0; frames < numFrames; frames++) { for (int channels = 0; channels < oboeStream->getChannelCount(); channels++) { *output++ *= m_volume.load(); } } return oboe::DataCallbackResult::Continue; } OboeCallback(PlaybackStream::AudioDataRequestCallback data_request_callback) : m_data_request_callback(move(data_request_callback)) { } Duration last_sample_time() const { return Duration::from_seconds(m_last_sample_time.load()); } void set_volume(float volume) { m_volume.store(volume); } private: PlaybackStream::AudioDataRequestCallback m_data_request_callback; Atomic m_last_sample_time { 0 }; size_t m_number_of_samples_enqueued { 0 }; Atomic m_volume { 1.0 }; }; class PlaybackStreamOboe::Storage : public RefCounted { public: Storage(std::shared_ptr stream, std::shared_ptr oboe_callback) : m_stream(move(stream)) , m_oboe_callback(move(oboe_callback)) { } std::shared_ptr stream() const { return m_stream; } std::shared_ptr oboe_callback() const { return m_oboe_callback; } private: std::shared_ptr m_stream; std::shared_ptr m_oboe_callback; }; PlaybackStreamOboe::PlaybackStreamOboe(NonnullRefPtr storage) : m_storage(move(storage)) { } ErrorOr> PlaybackStreamOboe::create(OutputState initial_output_state, u32 sample_rate, u8 channels, u32, AudioDataRequestCallback&& data_request_callback) { std::shared_ptr stream; auto oboe_callback = std::make_shared(move(data_request_callback)); oboe::AudioStreamBuilder builder; auto result = builder.setSharingMode(oboe::SharingMode::Shared) ->setPerformanceMode(oboe::PerformanceMode::LowLatency) ->setFormat(oboe::AudioFormat::Float) ->setDataCallback(oboe_callback) ->setChannelCount(channels) ->setSampleRate(sample_rate) ->openStream(stream); if (result != oboe::Result::OK) return Error::from_string_literal("Oboe failed to start"); if (initial_output_state == OutputState::Playing) stream->requestStart(); auto storage = TRY(adopt_nonnull_ref_or_enomem(new PlaybackStreamOboe::Storage(move(stream), move(oboe_callback)))); return TRY(adopt_nonnull_ref_or_enomem(new (nothrow) PlaybackStreamOboe(move(storage)))); } PlaybackStreamOboe::~PlaybackStreamOboe() = default; void PlaybackStreamOboe::set_underrun_callback(Function) { // FIXME: Implement this. } NonnullRefPtr> PlaybackStreamOboe::resume() { auto promise = Core::ThreadedPromise::create(); auto time = MUST(total_time_played()); m_storage->stream()->start(); promise->resolve(move(time)); return promise; } NonnullRefPtr> PlaybackStreamOboe::drain_buffer_and_suspend() { auto promise = Core::ThreadedPromise::create(); m_storage->stream()->stop(); promise->resolve(); return promise; } NonnullRefPtr> PlaybackStreamOboe::discard_buffer_and_suspend() { auto promise = Core::ThreadedPromise::create(); m_storage->stream()->pause(); m_storage->stream()->flush(); promise->resolve(); return promise; } ErrorOr PlaybackStreamOboe::total_time_played() { return m_storage->oboe_callback()->last_sample_time(); } NonnullRefPtr> PlaybackStreamOboe::set_volume(double volume) { auto promise = Core::ThreadedPromise::create(); m_storage->oboe_callback()->set_volume(volume); promise->resolve(); return promise; } }