From c90d039944e553e41bee4a1d547067c6d576f0f6 Mon Sep 17 00:00:00 2001 From: Attila Uygun Date: Thu, 18 May 2023 01:17:31 +0200 Subject: [PATCH] Refactoring audio code --- build/android/app/CMakeLists.txt | 4 +- build/linux/Makefile | 4 +- src/engine/audio/audio.h | 20 ----- src/engine/audio/audio_alsa.h | 47 ----------- src/engine/audio/audio_driver.h | 35 ++++++++ .../{audio_alsa.cc => audio_driver_alsa.cc} | 46 +++++----- src/engine/audio/audio_driver_alsa.h | 51 ++++++++++++ src/engine/audio/audio_driver_delegate.h | 18 ++++ .../{audio_oboe.cc => audio_driver_oboe.cc} | 47 ++++++----- .../{audio_oboe.h => audio_driver_oboe.h} | 36 ++++---- src/engine/audio/audio_forward.h | 16 ---- .../audio/{audio_base.cc => audio_mixer.cc} | 83 +++++++++---------- .../audio/{audio_base.h => audio_mixer.h} | 34 ++++---- src/engine/engine.cc | 21 +++-- src/engine/engine.h | 12 +-- src/engine/platform/platform.cc | 22 +++-- src/engine/platform/platform.h | 4 +- src/engine/platform/platform_android.cc | 1 - src/engine/platform/platform_linux.cc | 1 - src/engine/sound_player.cc | 32 +++---- 20 files changed, 292 insertions(+), 242 deletions(-) delete mode 100644 src/engine/audio/audio.h delete mode 100644 src/engine/audio/audio_alsa.h create mode 100644 src/engine/audio/audio_driver.h rename src/engine/audio/{audio_alsa.cc => audio_driver_alsa.cc} (85%) create mode 100644 src/engine/audio/audio_driver_alsa.h create mode 100644 src/engine/audio/audio_driver_delegate.h rename src/engine/audio/{audio_oboe.cc => audio_driver_oboe.cc} (60%) rename src/engine/audio/{audio_oboe.h => audio_driver_oboe.h} (52%) delete mode 100644 src/engine/audio/audio_forward.h rename src/engine/audio/{audio_base.cc => audio_mixer.cc} (78%) rename src/engine/audio/{audio_base.h => audio_mixer.h} (76%) diff --git a/build/android/app/CMakeLists.txt b/build/android/app/CMakeLists.txt index 2f92f82..24e59bd 100644 --- a/build/android/app/CMakeLists.txt +++ b/build/android/app/CMakeLists.txt @@ -62,8 +62,8 @@ add_library(kaliber SHARED ../../../src/demo/sky_quad.cc ../../../src/engine/animatable.cc ../../../src/engine/animator.cc - ../../../src/engine/audio/audio_base.cc - ../../../src/engine/audio/audio_oboe.cc + ../../../src/engine/audio/audio_driver_oboe.cc + ../../../src/engine/audio/audio_mixer.cc ../../../src/engine/drawable.cc ../../../src/engine/engine.cc ../../../src/engine/font.cc diff --git a/build/linux/Makefile b/build/linux/Makefile index 90eaec2..e68c211 100644 --- a/build/linux/Makefile +++ b/build/linux/Makefile @@ -95,8 +95,8 @@ $(BASE_LIB): $(BASE_OBJS) ENGINE_SRC := \ $(SRC_ROOT)/engine/animatable.cc \ $(SRC_ROOT)/engine/animator.cc \ - $(SRC_ROOT)/engine/audio/audio_alsa.cc \ - $(SRC_ROOT)/engine/audio/audio_base.cc \ + $(SRC_ROOT)/engine/audio/audio_driver_alsa.cc \ + $(SRC_ROOT)/engine/audio/audio_mixer.cc \ $(SRC_ROOT)/engine/drawable.cc \ $(SRC_ROOT)/engine/engine.cc \ $(SRC_ROOT)/engine/font.cc \ diff --git a/src/engine/audio/audio.h b/src/engine/audio/audio.h deleted file mode 100644 index 28535a2..0000000 --- a/src/engine/audio/audio.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef ENGINE_AUDIO_AUDIO_H -#define ENGINE_AUDIO_AUDIO_H - -#if defined(__ANDROID__) -#include "engine/audio/audio_oboe.h" -#elif defined(__linux__) -#include "engine/audio/audio_alsa.h" -#endif - -namespace eng { - -#if defined(__ANDROID__) -using Audio = AudioOboe; -#elif defined(__linux__) -using Audio = AudioAlsa; -#endif - -} // namespace eng - -#endif // ENGINE_AUDIO_AUDIO_H diff --git a/src/engine/audio/audio_alsa.h b/src/engine/audio/audio_alsa.h deleted file mode 100644 index b69b4b0..0000000 --- a/src/engine/audio/audio_alsa.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef ENGINE_AUDIO_AUDIO_ALSA_H -#define ENGINE_AUDIO_AUDIO_ALSA_H - -#include -#include - -#include "engine/audio/audio_base.h" - -typedef struct _snd_pcm snd_pcm_t; - -namespace eng { - -class AudioAlsa : public AudioBase { - public: - AudioAlsa(); - ~AudioAlsa(); - - bool Initialize(); - - void Shutdown(); - - void Suspend(); - void Resume(); - - int GetHardwareSampleRate(); - - private: - // Handle for the PCM device. - snd_pcm_t* device_; - - std::thread audio_thread_; - std::atomic terminate_audio_thread_{false}; - std::atomic suspend_audio_thread_{false}; - - size_t num_channels_ = 0; - int sample_rate_ = 0; - size_t period_size_ = 0; - - void StartAudioThread(); - void TerminateAudioThread(); - - void AudioThreadMain(); -}; - -} // namespace eng - -#endif // ENGINE_AUDIO_AUDIO_ALSA_H diff --git a/src/engine/audio/audio_driver.h b/src/engine/audio/audio_driver.h new file mode 100644 index 0000000..865ad0c --- /dev/null +++ b/src/engine/audio/audio_driver.h @@ -0,0 +1,35 @@ +#ifndef ENGINE_AUDIO_AUDIO_DRIVER_H +#define ENGINE_AUDIO_AUDIO_DRIVER_H + +#include "engine/audio/audio_driver.h" + +namespace eng { + +class AudioDriverDelegate; + +// Models an audio sink sending mixed audio to the audio driver. Audio data from +// the mixer source is delivered on a pull model using AudioDriverDelegate. +class AudioDriver { + public: + AudioDriver() = default; + virtual ~AudioDriver() = default; + + virtual void SetDelegate(AudioDriverDelegate* delegate) = 0; + + virtual bool Initialize() = 0; + + virtual void Shutdown() = 0; + + virtual void Suspend() = 0; + virtual void Resume() = 0; + + virtual int GetHardwareSampleRate() = 0; + + private: + AudioDriver(const AudioDriver&) = delete; + AudioDriver& operator=(const AudioDriver&) = delete; +}; + +} // namespace eng + +#endif // ENGINE_AUDIO_AUDIO_DRIVER_H diff --git a/src/engine/audio/audio_alsa.cc b/src/engine/audio/audio_driver_alsa.cc similarity index 85% rename from src/engine/audio/audio_alsa.cc rename to src/engine/audio/audio_driver_alsa.cc index fdefd90..75d41b1 100644 --- a/src/engine/audio/audio_alsa.cc +++ b/src/engine/audio/audio_driver_alsa.cc @@ -1,20 +1,26 @@ -#include "engine/audio/audio_alsa.h" +#include "engine/audio/audio_driver_alsa.h" #include #include #include "base/log.h" +#include "engine/audio/audio_driver_delegate.h" using namespace base; namespace eng { -AudioAlsa::AudioAlsa() = default; +AudioDriverAlsa::AudioDriverAlsa() = default; -AudioAlsa::~AudioAlsa() = default; +AudioDriverAlsa::~AudioDriverAlsa() = default; -bool AudioAlsa::Initialize() { +void AudioDriverAlsa::SetDelegate(AudioDriverDelegate* delegate) { + delegate_ = delegate; + Resume(); +} + +bool AudioDriverAlsa::Initialize() { LOG << "Initializing audio system."; int err; @@ -136,51 +142,43 @@ bool AudioAlsa::Initialize() { return false; } -void AudioAlsa::Shutdown() { +void AudioDriverAlsa::Shutdown() { LOG << "Shutting down audio system."; - TerminateAudioThread(); snd_pcm_drop(device_); snd_pcm_close(device_); } -void AudioAlsa::Suspend() { - DCHECK(!terminate_audio_thread_.load(std::memory_order_relaxed)); - +void AudioDriverAlsa::Suspend() { suspend_audio_thread_.store(true, std::memory_order_relaxed); } -void AudioAlsa::Resume() { - DCHECK(!terminate_audio_thread_.load(std::memory_order_relaxed)); - +void AudioDriverAlsa::Resume() { suspend_audio_thread_.store(false, std::memory_order_relaxed); } -int AudioAlsa::GetHardwareSampleRate() { +int AudioDriverAlsa::GetHardwareSampleRate() { return sample_rate_; } -void AudioAlsa::StartAudioThread() { +void AudioDriverAlsa::StartAudioThread() { LOG << "Starting audio thread."; - - DCHECK(!terminate_audio_thread_.load(std::memory_order_relaxed)); - - audio_thread_ = std::thread(&AudioAlsa::AudioThreadMain, this); + terminate_audio_thread_.store(false, std::memory_order_relaxed); + suspend_audio_thread_.store(true, std::memory_order_relaxed); + audio_thread_ = std::thread(&AudioDriverAlsa::AudioThreadMain, this); } -void AudioAlsa::TerminateAudioThread() { +void AudioDriverAlsa::TerminateAudioThread() { if (terminate_audio_thread_.load(std::memory_order_relaxed)) return; LOG << "Terminating audio thread"; - - // Notify worker thread and wait for it to terminate. terminate_audio_thread_.store(true, std::memory_order_relaxed); suspend_audio_thread_.store(true, std::memory_order_relaxed); audio_thread_.join(); } -void AudioAlsa::AudioThreadMain() { +void AudioDriverAlsa::AudioThreadMain() { size_t num_frames = period_size_ / (num_channels_ * sizeof(float)); auto buffer = std::make_unique(num_frames * 2); @@ -192,11 +190,11 @@ void AudioAlsa::AudioThreadMain() { std::this_thread::sleep_for(std::chrono::milliseconds(1)); } - RenderAudio(buffer.get(), num_frames); + delegate_->RenderAudio(buffer.get(), num_frames); while (snd_pcm_writei(device_, buffer.get(), num_frames) < 0) { snd_pcm_prepare(device_); - DLOG << "Audio buffer underrun!"; + DLOG << "Alsa buffer underrun!"; } } } diff --git a/src/engine/audio/audio_driver_alsa.h b/src/engine/audio/audio_driver_alsa.h new file mode 100644 index 0000000..f3d56d4 --- /dev/null +++ b/src/engine/audio/audio_driver_alsa.h @@ -0,0 +1,51 @@ +#ifndef ENGINE_AUDIO_AUDIO_DRIVER_ALSA_H +#define ENGINE_AUDIO_AUDIO_DRIVER_ALSA_H + +#include +#include + +#include "engine/audio/audio_driver.h" + +typedef struct _snd_pcm snd_pcm_t; + +namespace eng { + +class AudioDriverAlsa final : public AudioDriver { + public: + AudioDriverAlsa(); + ~AudioDriverAlsa() final; + + void SetDelegate(AudioDriverDelegate* delegate) final; + + bool Initialize() final; + + void Shutdown() final; + + void Suspend() final; + void Resume() final; + + int GetHardwareSampleRate() final; + + private: + // Handle for the PCM device. + snd_pcm_t* device_; + + std::thread audio_thread_; + std::atomic terminate_audio_thread_{false}; + std::atomic suspend_audio_thread_{false}; + + size_t num_channels_ = 0; + int sample_rate_ = 0; + size_t period_size_ = 0; + + AudioDriverDelegate* delegate_ = nullptr; + + void StartAudioThread(); + void TerminateAudioThread(); + + void AudioThreadMain(); +}; + +} // namespace eng + +#endif // ENGINE_AUDIO_AUDIO_DRIVER_ALSA_H diff --git a/src/engine/audio/audio_driver_delegate.h b/src/engine/audio/audio_driver_delegate.h new file mode 100644 index 0000000..e2fee3f --- /dev/null +++ b/src/engine/audio/audio_driver_delegate.h @@ -0,0 +1,18 @@ +#ifndef ENGINE_AUDIO_AUDIO_DRIVER_DELEGATE_H +#define ENGINE_AUDIO_AUDIO_DRIVER_DELEGATE_H + +#include + +namespace eng { + +class AudioDriverDelegate { + public: + AudioDriverDelegate() = default; + virtual ~AudioDriverDelegate() = default; + + virtual void RenderAudio(float* output_buffer, size_t num_frames) = 0; +}; + +} // namespace eng + +#endif // ENGINE_AUDIO_AUDIO_DRIVER_DELEGATE_H diff --git a/src/engine/audio/audio_oboe.cc b/src/engine/audio/audio_driver_oboe.cc similarity index 60% rename from src/engine/audio/audio_oboe.cc rename to src/engine/audio/audio_driver_oboe.cc index d686215..01e269f 100644 --- a/src/engine/audio/audio_oboe.cc +++ b/src/engine/audio/audio_driver_oboe.cc @@ -1,62 +1,70 @@ -#include "engine/audio/audio_oboe.h" +#include "engine/audio/audio_driver_oboe.h" #include "base/log.h" +#include "engine/audio/audio_driver_delegate.h" #include "third_party/oboe/include/oboe/Oboe.h" using namespace base; namespace eng { -AudioOboe::AudioOboe() : callback_(std::make_unique(this)) {} +AudioDriverOboe::AudioDriverOboe() + : callback_(std::make_unique(this)) {} -AudioOboe::~AudioOboe() = default; +AudioDriverOboe::~AudioDriverOboe() = default; -bool AudioOboe::Initialize() { - LOG << "Initializing audio system."; - - return RestartStream(); +void AudioDriverOboe::SetDelegate(AudioDriverDelegate* delegate) { + delegate_ = delegate; + Resume(); } -void AudioOboe::Shutdown() { +bool AudioDriverOboe::Initialize() { + LOG << "Initializing audio system."; + + return RestartStream(true); +} + +void AudioDriverOboe::Shutdown() { LOG << "Shutting down audio system."; stream_->stop(); } -void AudioOboe::Suspend() { +void AudioDriverOboe::Suspend() { stream_->stop(); } -void AudioOboe::Resume() { +void AudioDriverOboe::Resume() { RestartStream(); } -int AudioOboe::GetHardwareSampleRate() { +int AudioDriverOboe::GetHardwareSampleRate() { return stream_->getSampleRate(); } -AudioOboe::StreamCallback::StreamCallback(AudioOboe* audio) : audio_(audio) {} +AudioDriverOboe::StreamCallback::StreamCallback(AudioDriverOboe* driver) + : driver_(driver) {} -AudioOboe::StreamCallback::~StreamCallback() = default; +AudioDriverOboe::StreamCallback::~StreamCallback() = default; -oboe::DataCallbackResult AudioOboe::StreamCallback::onAudioReady( +oboe::DataCallbackResult AudioDriverOboe::StreamCallback::onAudioReady( oboe::AudioStream* oboe_stream, void* audio_data, int32_t num_frames) { float* output_buffer = static_cast(audio_data); - audio_->RenderAudio(output_buffer, num_frames); + driver_->delegate_->RenderAudio(output_buffer, num_frames); return oboe::DataCallbackResult::Continue; } -void AudioOboe::StreamCallback::onErrorAfterClose( +void AudioDriverOboe::StreamCallback::onErrorAfterClose( oboe::AudioStream* oboe_stream, oboe::Result error) { LOG << "Error after close. Error: " << oboe::convertToText(error); - audio_->RestartStream(); + driver_->RestartStream(); } -bool AudioOboe::RestartStream() { +bool AudioDriverOboe::RestartStream(bool suspended) { oboe::AudioStreamBuilder builder; oboe::Result result = builder.setSharingMode(oboe::SharingMode::Exclusive) @@ -80,7 +88,8 @@ bool AudioOboe::RestartStream() { return false; } - stream_->start(); + if (!suspended) + stream_->start(); return true; } diff --git a/src/engine/audio/audio_oboe.h b/src/engine/audio/audio_driver_oboe.h similarity index 52% rename from src/engine/audio/audio_oboe.h rename to src/engine/audio/audio_driver_oboe.h index 0470365..63c477d 100644 --- a/src/engine/audio/audio_oboe.h +++ b/src/engine/audio/audio_driver_oboe.h @@ -1,33 +1,37 @@ -#ifndef ENGINE_AUDIO_AUDIO_OBOE_H -#define ENGINE_AUDIO_AUDIO_OBOE_H +#ifndef ENGINE_AUDIO_AUDIO_DRIVER_OBOE_H +#define ENGINE_AUDIO_AUDIO_DRIVER_OBOE_H #include #include "third_party/oboe/include/oboe/AudioStream.h" #include "third_party/oboe/include/oboe/AudioStreamCallback.h" -#include "engine/audio/audio_base.h" +#include "engine/audio/audio_driver.h" namespace eng { -class AudioOboe : public AudioBase { +class AudioDriverOboe final : public AudioDriver { public: - AudioOboe(); - ~AudioOboe(); + AudioDriverOboe(); + ~AudioDriverOboe() final; - bool Initialize(); + void SetDelegate(AudioDriverDelegate* delegate) final; - void Shutdown(); + bool Initialize() final; - void Suspend(); - void Resume(); + void Shutdown() final; - int GetHardwareSampleRate(); + void Suspend() final; + void Resume() final; + + int GetHardwareSampleRate() final; private: + static constexpr int kChannelCount = 2; + class StreamCallback final : public oboe::AudioStreamCallback { public: - StreamCallback(AudioOboe* audio); + StreamCallback(AudioDriverOboe* audio); ~StreamCallback() final; oboe::DataCallbackResult onAudioReady(oboe::AudioStream* oboe_stream, @@ -38,15 +42,17 @@ class AudioOboe : public AudioBase { oboe::Result error) final; private: - AudioOboe* audio_; + AudioDriverOboe* driver_; }; oboe::ManagedStream stream_; std::unique_ptr callback_; - bool RestartStream(); + AudioDriverDelegate* delegate_ = nullptr; + + bool RestartStream(bool suspended = false); }; } // namespace eng -#endif // ENGINE_AUDIO_AUDIO_OBOE_H +#endif // ENGINE_AUDIO_AUDIO_DRIVER_OBOE_H diff --git a/src/engine/audio/audio_forward.h b/src/engine/audio/audio_forward.h deleted file mode 100644 index 59acf90..0000000 --- a/src/engine/audio/audio_forward.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef ENGINE_AUDIO_AUDIO_FORWARD_H -#define ENGINE_AUDIO_AUDIO_FORWARD_H - -namespace eng { - -#if defined(__ANDROID__) -class AudioOboe; -using Audio = AudioOboe; -#elif defined(__linux__) -class AudioAlsa; -using Audio = AudioAlsa; -#endif - -} // namespace eng - -#endif // ENGINE_AUDIO_AUDIO_FORWARD_H diff --git a/src/engine/audio/audio_base.cc b/src/engine/audio/audio_mixer.cc similarity index 78% rename from src/engine/audio/audio_base.cc rename to src/engine/audio/audio_mixer.cc index f694117..db53b0e 100644 --- a/src/engine/audio/audio_base.cc +++ b/src/engine/audio/audio_mixer.cc @@ -1,4 +1,4 @@ -#include "engine/audio/audio.h" +#include "engine/audio/audio_mixer.h" #include @@ -11,30 +11,33 @@ using namespace base; namespace eng { -AudioBase::AudioBase() +AudioMixer::AudioMixer() : main_thread_task_runner_(TaskRunner::GetThreadLocalTaskRunner()) {} -AudioBase::~AudioBase() = default; +AudioMixer::~AudioMixer() = default; -uint64_t AudioBase::CreateResource() { +uint64_t AudioMixer::CreateResource() { uint64_t resource_id = ++last_resource_id_; resources_[resource_id] = std::make_shared(); return resource_id; } -void AudioBase::DestroyResource(uint64_t resource_id) { +void AudioMixer::DestroyResource(uint64_t resource_id) { auto it = resources_.find(resource_id); if (it == resources_.end()) return; - it->second->flags.fetch_or(kStopped, std::memory_order_relaxed); + if (it->second->active) { + it->second->restart_cb = nullptr; + it->second->flags.fetch_or(kStopped, std::memory_order_relaxed); + } resources_.erase(it); } -void AudioBase::Play(uint64_t resource_id, - std::shared_ptr sound, - float amplitude, - bool reset_pos) { +void AudioMixer::Play(uint64_t resource_id, + std::shared_ptr sound, + float amplitude, + bool reset_pos) { if (!audio_enabled_) return; @@ -46,15 +49,11 @@ void AudioBase::Play(uint64_t resource_id, if (reset_pos) it->second->flags.fetch_or(kStopped, std::memory_order_relaxed); - if (it->second->flags.load(std::memory_order_relaxed) & kStopped) { - Closure ocb = std::move(it->second->end_cb); - SetEndCallback( - resource_id, - [&, resource_id, sound, amplitude, reset_pos, ocb]() -> void { - Play(resource_id, sound, amplitude, reset_pos); - SetEndCallback(resource_id, std::move(ocb)); - }); - } + if (it->second->flags.load(std::memory_order_relaxed) & kStopped) + it->second->restart_cb = [&, resource_id, sound, amplitude, + reset_pos]() -> void { + Play(resource_id, sound, amplitude, reset_pos); + }; return; } @@ -75,16 +74,18 @@ void AudioBase::Play(uint64_t resource_id, play_list_[0].push_back(it->second); } -void AudioBase::Stop(uint64_t resource_id) { +void AudioMixer::Stop(uint64_t resource_id) { auto it = resources_.find(resource_id); if (it == resources_.end()) return; - if (it->second->active) + if (it->second->active) { + it->second->restart_cb = nullptr; it->second->flags.fetch_or(kStopped, std::memory_order_relaxed); + } } -void AudioBase::SetLoop(uint64_t resource_id, bool loop) { +void AudioMixer::SetLoop(uint64_t resource_id, bool loop) { auto it = resources_.find(resource_id); if (it == resources_.end()) return; @@ -95,7 +96,7 @@ void AudioBase::SetLoop(uint64_t resource_id, bool loop) { it->second->flags.fetch_and(~kLoop, std::memory_order_relaxed); } -void AudioBase::SetSimulateStereo(uint64_t resource_id, bool simulate) { +void AudioMixer::SetSimulateStereo(uint64_t resource_id, bool simulate) { auto it = resources_.find(resource_id); if (it == resources_.end()) return; @@ -106,7 +107,7 @@ void AudioBase::SetSimulateStereo(uint64_t resource_id, bool simulate) { it->second->flags.fetch_and(~kSimulateStereo, std::memory_order_relaxed); } -void AudioBase::SetResampleStep(uint64_t resource_id, size_t step) { +void AudioMixer::SetResampleStep(uint64_t resource_id, size_t step) { auto it = resources_.find(resource_id); if (it == resources_.end()) return; @@ -114,7 +115,7 @@ void AudioBase::SetResampleStep(uint64_t resource_id, size_t step) { it->second->step.store(step + 100, std::memory_order_relaxed); } -void AudioBase::SetMaxAmplitude(uint64_t resource_id, float max_amplitude) { +void AudioMixer::SetMaxAmplitude(uint64_t resource_id, float max_amplitude) { auto it = resources_.find(resource_id); if (it == resources_.end()) return; @@ -122,7 +123,7 @@ void AudioBase::SetMaxAmplitude(uint64_t resource_id, float max_amplitude) { it->second->max_amplitude.store(max_amplitude, std::memory_order_relaxed); } -void AudioBase::SetAmplitudeInc(uint64_t resource_id, float amplitude_inc) { +void AudioMixer::SetAmplitudeInc(uint64_t resource_id, float amplitude_inc) { auto it = resources_.find(resource_id); if (it == resources_.end()) return; @@ -130,7 +131,7 @@ void AudioBase::SetAmplitudeInc(uint64_t resource_id, float amplitude_inc) { it->second->amplitude_inc.store(amplitude_inc, std::memory_order_relaxed); } -void AudioBase::SetEndCallback(uint64_t resource_id, base::Closure cb) { +void AudioMixer::SetEndCallback(uint64_t resource_id, base::Closure cb) { auto it = resources_.find(resource_id); if (it == resources_.end()) return; @@ -138,7 +139,7 @@ void AudioBase::SetEndCallback(uint64_t resource_id, base::Closure cb) { it->second->end_cb = std::move(cb); } -void AudioBase::RenderAudio(float* output_buffer, size_t num_frames) { +void AudioMixer::RenderAudio(float* output_buffer, size_t num_frames) { { std::unique_lock scoped_lock(lock_, std::try_to_lock); if (scoped_lock) @@ -207,20 +208,18 @@ void AudioBase::RenderAudio(float* output_buffer, size_t num_frames) { // Remove, loop or stream if the source data is consumed if (src_index >= num_samples) { - if (!sound->is_streaming_sound()) { + if (num_samples) src_index %= num_samples; + if (!sound->is_streaming_sound()) { if (!(flags & kLoop)) { marked_for_removal = true; break; } } else if (!it->get()->streaming_in_progress.load( std::memory_order_acquire)) { - if (num_samples) - src_index %= num_samples; - - // Looping streaming sounds never return eof. if (sound->eof()) { + DCHECK((flags & kLoop) == 0); marked_for_removal = true; break; } @@ -238,10 +237,9 @@ void AudioBase::RenderAudio(float* output_buffer, size_t num_frames) { ThreadPool::Get().PostTask( HERE, - std::bind(&AudioBase::DoStream, this, *it, flags & kLoop)); + std::bind(&AudioMixer::DoStream, this, *it, flags & kLoop)); } else if (num_samples) { - DLOG << "Buffer underrun!"; - src_index %= num_samples; + DLOG << "Mixer buffer underrun!"; } } } @@ -263,7 +261,7 @@ void AudioBase::RenderAudio(float* output_buffer, size_t num_frames) { if ((!it->get()->sound->is_streaming_sound() || !it->get()->streaming_in_progress.load(std::memory_order_relaxed))) { main_thread_task_runner_->PostTask( - HERE, std::bind(&AudioBase::EndCallback, this, *it)); + HERE, std::bind(&AudioMixer::EndCallback, this, *it)); it = end_list_.erase(it); } else { ++it; @@ -271,19 +269,20 @@ void AudioBase::RenderAudio(float* output_buffer, size_t num_frames) { } } -void AudioBase::DoStream(std::shared_ptr resource, bool loop) { +void AudioMixer::DoStream(std::shared_ptr resource, bool loop) { resource->sound->Stream(loop); - - // Memory barrier to ensure all memory writes become visible to the audio - // thread. resource->streaming_in_progress.store(false, std::memory_order_release); } -void AudioBase::EndCallback(std::shared_ptr resource) { +void AudioMixer::EndCallback(std::shared_ptr resource) { resource->active = false; if (resource->end_cb) resource->end_cb(); + if (resource->restart_cb) { + resource->restart_cb(); + resource->restart_cb = nullptr; + } } } // namespace eng diff --git a/src/engine/audio/audio_base.h b/src/engine/audio/audio_mixer.h similarity index 76% rename from src/engine/audio/audio_base.h rename to src/engine/audio/audio_mixer.h index d89e2fd..b0fff9f 100644 --- a/src/engine/audio/audio_base.h +++ b/src/engine/audio/audio_mixer.h @@ -1,12 +1,14 @@ -#ifndef ENGINE_AUDIO_AUDIO_BASE_H -#define ENGINE_AUDIO_AUDIO_BASE_H +#ifndef ENGINE_AUDIO_AUDIO_MIXER_H +#define ENGINE_AUDIO_AUDIO_MIXER_H +#include #include #include #include #include #include "base/closure.h" +#include "engine/audio/audio_driver_delegate.h" namespace base { class TaskRunner; @@ -16,11 +18,14 @@ namespace eng { class Sound; -// Class representing the end-point for rendered audio. A platform specific -// implementation is expected to periodically call RenderAudio() in a background +// Mix and render audio with low overhead. A platform specific AudioDriver +// implementation is expected to periodically call Render() in a background // thread. -class AudioBase { +class AudioMixer : public AudioDriverDelegate { public: + AudioMixer(); + ~AudioMixer(); + uint64_t CreateResource(); void DestroyResource(uint64_t resource_id); @@ -39,21 +44,15 @@ class AudioBase { void SetEnableAudio(bool enable) { audio_enabled_ = enable; } - protected: - static constexpr int kChannelCount = 2; - - AudioBase(); - ~AudioBase(); - - void RenderAudio(float* output_buffer, size_t num_frames); - private: enum SampleFlags { kLoop = 1, kStopped = 2, kSimulateStereo = 4 }; + static constexpr int kChannelCount = 2; struct Resource { // Accessed by main thread only. bool active = false; base::Closure end_cb; + base::Closure restart_cb; // Initialized by main thread, used by audio thread. std::shared_ptr sound; @@ -83,14 +82,17 @@ class AudioBase { bool audio_enabled_ = true; + // AudioDriverDelegate implementation + void RenderAudio(float* output_buffer, size_t num_frames) final; + void DoStream(std::shared_ptr sample, bool loop); void EndCallback(std::shared_ptr sample); - AudioBase(const AudioBase&) = delete; - AudioBase& operator=(const AudioBase&) = delete; + AudioMixer(const AudioMixer&) = delete; + AudioMixer& operator=(const AudioMixer&) = delete; }; } // namespace eng -#endif // ENGINE_AUDIO_AUDIO_BASE_H +#endif // ENGINE_AUDIO_AUDIO_MIXER_H diff --git a/src/engine/engine.cc b/src/engine/engine.cc index 945a0ee..270c1fc 100644 --- a/src/engine/engine.cc +++ b/src/engine/engine.cc @@ -2,7 +2,8 @@ #include "base/log.h" #include "engine/animator.h" -#include "engine/audio/audio.h" +#include "engine/audio/audio_driver.h" +#include "engine/audio/audio_mixer.h" #include "engine/drawable.h" #include "engine/font.h" #include "engine/game.h" @@ -25,11 +26,17 @@ namespace eng { Engine* Engine::singleton = nullptr; -Engine::Engine(Platform* platform, Renderer* renderer, Audio* audio) - : platform_(platform), renderer_(renderer), audio_(audio) { +Engine::Engine(Platform* platform, + Renderer* renderer, + AudioDriver* audio_driver) + : platform_(platform), + renderer_(renderer), + audio_driver_(audio_driver), + audio_mixer_{std::make_unique()} { DCHECK(!singleton); singleton = this; + audio_driver_->SetDelegate(audio_mixer_.get()); renderer_->SetContextLostCB(std::bind(&Engine::ContextLost, this)); quad_ = CreateRenderResource(); @@ -151,14 +158,14 @@ void Engine::Draw(float frame_frac) { } void Engine::LostFocus() { - audio_->Suspend(); + audio_driver_->Suspend(); if (game_) game_->LostFocus(); } void Engine::GainedFocus(bool from_interstitial_ad) { - audio_->Resume(); + audio_driver_->Resume(); if (game_) game_->GainedFocus(from_interstitial_ad); @@ -436,7 +443,7 @@ void Engine::SetKeepScreenOn(bool keep_screen_on) { } void Engine::SetEnableAudio(bool enable) { - audio_->SetEnableAudio(enable); + audio_mixer_->SetEnableAudio(enable); } TextureCompressor* Engine::GetTextureCompressor(bool opacity) { @@ -474,7 +481,7 @@ const std::string& Engine::GetSharedDataPath() const { } int Engine::GetAudioHardwareSampleRate() { - return audio_->GetHardwareSampleRate(); + return audio_driver_->GetHardwareSampleRate(); } bool Engine::IsMobile() const { diff --git a/src/engine/engine.h b/src/engine/engine.h index 024c1a5..fbd7cda 100644 --- a/src/engine/engine.h +++ b/src/engine/engine.h @@ -9,7 +9,6 @@ #include "base/random.h" #include "base/vecmath.h" -#include "engine/audio/audio_forward.h" #include "engine/persistent_data.h" class TextureCompressor; @@ -17,6 +16,8 @@ class TextureCompressor; namespace eng { class Animator; +class AudioDriver; +class AudioMixer; class Font; class Game; class Drawable; @@ -33,7 +34,7 @@ class Engine { public: using CreateImageCB = std::function()>; - Engine(Platform* platform, Renderer* renderer, Audio* audio); + Engine(Platform* platform, Renderer* renderer, AudioDriver* audio_driver); ~Engine(); static Engine& Get(); @@ -107,7 +108,7 @@ class Engine { void SetEnableVibration(bool enable) { vibration_enabled_ = enable; } - Audio* GetAudio() { return audio_; } + AudioMixer* GetAudioMixer() { return audio_mixer_.get(); } // Access to the render resources. Geometry* GetQuad() { return quad_.get(); } @@ -172,11 +173,10 @@ class Engine { static Engine* singleton; Platform* platform_ = nullptr; - Renderer* renderer_ = nullptr; + AudioDriver* audio_driver_ = nullptr; - Audio* audio_ = nullptr; - + std::unique_ptr audio_mixer_; std::unique_ptr game_; std::unique_ptr quad_; diff --git a/src/engine/platform/platform.cc b/src/engine/platform/platform.cc index 2093b40..5357225 100644 --- a/src/engine/platform/platform.cc +++ b/src/engine/platform/platform.cc @@ -2,11 +2,16 @@ #include "base/log.h" #include "base/task_runner.h" -#include "engine/audio/audio.h" #include "engine/engine.h" #include "engine/renderer/opengl/renderer_opengl.h" #include "engine/renderer/vulkan/renderer_vulkan.h" +#if defined(__ANDROID__) +#include "engine/audio/audio_driver_oboe.h" +#elif defined(__linux__) +#include "engine/audio/audio_driver_alsa.h" +#endif + using namespace base; namespace eng { @@ -23,9 +28,13 @@ void Platform::InitializeCommon() { thread_pool_.Initialize(); TaskRunner::CreateThreadLocalTaskRunner(); - audio_ = std::make_unique