diff --git a/src/engine/audio/audio_mixer.cc b/src/engine/audio/audio_mixer.cc index f9a7ff3..ac8c27b 100644 --- a/src/engine/audio/audio_mixer.cc +++ b/src/engine/audio/audio_mixer.cc @@ -4,7 +4,6 @@ #include "base/log.h" #include "base/task_runner.h" -#include "base/thread_pool.h" #include "engine/audio/audio_bus.h" #include "engine/audio/mixer_input.h" @@ -33,11 +32,11 @@ AudioMixer::~AudioMixer() { audio_sink_.reset(); } -void AudioMixer::AddInput(std::shared_ptr input) { +void AudioMixer::AddInput(std::shared_ptr mixer_input) { DCHECK(audio_enabled_); std::lock_guard scoped_lock(lock_); - inputs_[0].push_back(input); + inputs_[0].push_back(mixer_input); } void AudioMixer::Suspend() { @@ -62,8 +61,8 @@ void AudioMixer::RenderAudio(float* output_buffer, size_t num_frames) { memset(output_buffer, 0, sizeof(float) * num_frames * kChannelCount); for (auto it = inputs_[1].begin(); it != inputs_[1].end();) { - auto audio_bus = (*it)->audio_bus.get(); - unsigned flags = (*it)->flags.load(std::memory_order_relaxed); + auto* audio_bus = (*it)->GetAudioBus().get(); + unsigned flags = (*it)->GetFlags(); bool marked_for_removal = false; if (flags & MixerInput::kStopped) { @@ -75,14 +74,12 @@ void AudioMixer::RenderAudio(float* output_buffer, size_t num_frames) { src[1] = src[0]; // mono. size_t num_samples = audio_bus->samples_per_channel(); - size_t src_index = (*it)->src_index; - size_t step = (*it)->step.load(std::memory_order_relaxed); - size_t accumulator = (*it)->accumulator; - float amplitude = (*it)->amplitude; - float amplitude_inc = - (*it)->amplitude_inc.load(std::memory_order_relaxed); - float max_amplitude = - (*it)->max_amplitude.load(std::memory_order_relaxed); + size_t src_index = (*it)->GetSrcIndex(); + size_t step = (*it)->GetStep(); + size_t accumulator = (*it)->GetAccumulator(); + float amplitude = (*it)->GetAmplitude(); + float amplitude_inc = (*it)->GetAmplitudeInc(); + float max_amplitude = (*it)->GetMaxAmplitude(); size_t channel_offset = (flags & MixerInput::kSimulateStereo) ? audio_bus->sample_rate() / 10 : 0; @@ -118,37 +115,29 @@ void AudioMixer::RenderAudio(float* output_buffer, size_t num_frames) { accumulator %= 100; } else { if (audio_bus->EndOfStream()) { - src_index %= num_samples; - marked_for_removal = !(flags & MixerInput::kLoop); + if (!(flags & MixerInput::kLoop)) + marked_for_removal = true; + else + src_index %= num_samples; break; } - if (!(*it)->streaming_in_progress.load(std::memory_order_acquire)) { + if ((*it)->OnMoreData(!!(flags & MixerInput::kLoop))) { src_index %= num_samples; - (*it)->streaming_in_progress.store(true, std::memory_order_relaxed); - - // Swap buffers and start streaming in background. - audio_bus->SwapBuffers(); src[0] = audio_bus->GetChannelData(0); src[1] = audio_bus->GetChannelData(1); if (!src[1]) src[1] = src[0]; // mono. num_samples = audio_bus->samples_per_channel(); - - ThreadPool::Get().PostTask( - HERE, - std::bind(&AudioMixer::DoStream, this, *it, - flags & MixerInput::kLoop), - true); } else { DLOG(0) << "Mixer buffer underrun!"; } } } - (*it)->src_index = src_index; - (*it)->accumulator = accumulator; - (*it)->amplitude = amplitude; + // Remember last sample position and volume. + (*it)->SetPosition(src_index, accumulator); + (*it)->SetAmplitude(amplitude); } if (marked_for_removal) { @@ -160,9 +149,9 @@ void AudioMixer::RenderAudio(float* output_buffer, size_t num_frames) { } for (auto it = removed_inputs_.begin(); it != removed_inputs_.end();) { - if (!(*it)->streaming_in_progress.load(std::memory_order_relaxed)) { + if (!(*it)->IsStreamingInProgress()) { main_thread_task_runner_->PostTask( - HERE, std::bind(&AudioMixer::EndCallback, this, *it)); + HERE, std::bind(&MixerInput::OnRemovedFromMixer, *it)); it = removed_inputs_.erase(it); } else { ++it; @@ -170,25 +159,4 @@ void AudioMixer::RenderAudio(float* output_buffer, size_t num_frames) { } } -void AudioMixer::DoStream(std::shared_ptr input, bool loop) { - input->audio_bus->Stream(loop); - input->streaming_in_progress.store(false, std::memory_order_release); -} - -void AudioMixer::EndCallback(std::shared_ptr input) { - input->streaming = false; - - if (input->pending_audio_bus) { - input->audio_bus = input->pending_audio_bus; - input->pending_audio_bus.reset(); - } - - if (input->end_cb) - input->end_cb(); - if (input->restart_cb) { - input->restart_cb(); - input->restart_cb = nullptr; - } -} - } // namespace eng diff --git a/src/engine/audio/audio_mixer.h b/src/engine/audio/audio_mixer.h index f86a721..a2f424a 100644 --- a/src/engine/audio/audio_mixer.h +++ b/src/engine/audio/audio_mixer.h @@ -14,8 +14,7 @@ class TaskRunner; namespace eng { -class AudioBus; -struct MixerInput; +class MixerInput; // Mix and render audio with low overhead. The mixer has zero or more inputs // which can be added at any time. The mixer will pull from each input source @@ -29,7 +28,7 @@ class AudioMixer : public AudioSink::Delegate { AudioMixer(); ~AudioMixer(); - void AddInput(std::shared_ptr input); + void AddInput(std::shared_ptr mixer_input); void SetEnableAudio(bool enable) { audio_enabled_ = enable; } bool IsAudioEnabled() const { return audio_enabled_; } @@ -57,10 +56,6 @@ class AudioMixer : public AudioSink::Delegate { int GetChannelCount() final { return kChannelCount; } void RenderAudio(float* output_buffer, size_t num_frames) final; - void DoStream(std::shared_ptr sample, bool loop); - - void EndCallback(std::shared_ptr sample); - AudioMixer(const AudioMixer&) = delete; AudioMixer& operator=(const AudioMixer&) = delete; }; diff --git a/src/engine/audio/mixer_input.cc b/src/engine/audio/mixer_input.cc index 98c940c..7873bad 100644 --- a/src/engine/audio/mixer_input.cc +++ b/src/engine/audio/mixer_input.cc @@ -1,6 +1,7 @@ #include "engine/audio/mixer_input.h" #include "base/log.h" +#include "base/thread_pool.h" #include "engine/audio/audio_bus.h" #include "engine/audio/audio_mixer.h" @@ -21,78 +22,124 @@ std::shared_ptr MixerInput::Create() { return std::shared_ptr(new MixerInput()); } -void MixerInput::SetAudioBus(std::shared_ptr bus) { - if (streaming) - pending_audio_bus = bus; +void MixerInput::SetAudioBus(std::shared_ptr audio_bus) { + if (playing_) + pending_audio_bus_ = audio_bus; else - audio_bus = bus; + audio_bus_ = audio_bus; } void MixerInput::Play(AudioMixer* mixer, bool restart) { - if (!mixer->IsAudioEnabled()) + if (!mixer->IsAudioEnabled()) { + if (!playing_ && end_cb_) + end_cb_(); return; + } - // If already streaming check if stream position needs to be reset. - if (streaming) { + // If already playing check if stream position needs to be reset. + if (playing_) { if (restart) - flags.fetch_or(kStopped, std::memory_order_relaxed); + flags_.fetch_or(kStopped, std::memory_order_relaxed); - if (flags.load(std::memory_order_relaxed) & kStopped) - restart_cb = [&, mixer, restart]() -> void { Play(mixer, restart); }; + if (flags_.load(std::memory_order_relaxed) & kStopped) + restart_cb_ = [&, mixer, restart]() -> void { Play(mixer, restart); }; return; } - if (restart || audio_bus->EndOfStream()) { - src_index = 0; - accumulator = 0; - audio_bus->ResetStream(); + if (restart || audio_bus_->EndOfStream()) { + src_index_ = 0; + accumulator_ = 0; + audio_bus_->ResetStream(); } - streaming = true; - flags.fetch_and(~kStopped, std::memory_order_relaxed); + playing_ = true; + flags_.fetch_and(~kStopped, std::memory_order_relaxed); mixer->AddInput(shared_from_this()); } void MixerInput::Stop() { - if (streaming) { - restart_cb = nullptr; - flags.fetch_or(kStopped, std::memory_order_relaxed); + if (playing_) { + restart_cb_ = nullptr; + flags_.fetch_or(kStopped, std::memory_order_relaxed); } } void MixerInput::SetLoop(bool loop) { if (loop) - flags.fetch_or(kLoop, std::memory_order_relaxed); + flags_.fetch_or(kLoop, std::memory_order_relaxed); else - flags.fetch_and(~kLoop, std::memory_order_relaxed); + flags_.fetch_and(~kLoop, std::memory_order_relaxed); } void MixerInput::SetSimulateStereo(bool simulate) { if (simulate) - flags.fetch_or(kSimulateStereo, std::memory_order_relaxed); + flags_.fetch_or(kSimulateStereo, std::memory_order_relaxed); else - flags.fetch_and(~kSimulateStereo, std::memory_order_relaxed); + flags_.fetch_and(~kSimulateStereo, std::memory_order_relaxed); } void MixerInput::SetResampleStep(size_t value) { - step.store(value + 100, std::memory_order_relaxed); + step_.store(value + 100, std::memory_order_relaxed); } void MixerInput::SetAmplitude(float value) { - amplitude.store(value, std::memory_order_relaxed); + amplitude_.store(value, std::memory_order_relaxed); } void MixerInput::SetMaxAmplitude(float value) { - max_amplitude.store(value, std::memory_order_relaxed); + max_amplitude_.store(value, std::memory_order_relaxed); } void MixerInput::SetAmplitudeInc(float value) { - amplitude_inc.store(value, std::memory_order_relaxed); + amplitude_inc_.store(value, std::memory_order_relaxed); } void MixerInput::SetEndCallback(base::Closure cb) { - end_cb = std::move(cb); + end_cb_ = std::move(cb); +} + +bool MixerInput::IsStreamingInProgress() const { + return streaming_in_progress_.load(std::memory_order_relaxed); +} + +void MixerInput::SetPosition(size_t index, int accumulator) { + src_index_ = index; + accumulator_ = accumulator; +} + +bool MixerInput::OnMoreData(bool loop) { + if (streaming_in_progress_.load(std::memory_order_acquire)) + return false; + + streaming_in_progress_.store(true, std::memory_order_relaxed); + audio_bus_->SwapBuffers(); + ThreadPool::Get().PostTask( + HERE, + [&, loop]() { + audio_bus_->Stream(loop); + streaming_in_progress_.store(false, std::memory_order_release); + }, + true); + return true; +} + +void MixerInput::OnRemovedFromMixer() { + DCHECK(!streaming_in_progress_.load(std::memory_order_relaxed)); + DCHECK(playing_); + playing_ = false; + + if (pending_audio_bus_) { + audio_bus_ = pending_audio_bus_; + pending_audio_bus_.reset(); + } + + if (end_cb_) + end_cb_(); + if (restart_cb_) { + restart_cb_(); + restart_cb_ = nullptr; + } } } // namespace eng diff --git a/src/engine/audio/mixer_input.h b/src/engine/audio/mixer_input.h index 8920fde..22b7a6a 100644 --- a/src/engine/audio/mixer_input.h +++ b/src/engine/audio/mixer_input.h @@ -13,16 +13,19 @@ class AudioMixer; // An audio input stream that gets mixed and rendered to the audio sink. Handles // playback and volume control. -struct MixerInput : public std::enable_shared_from_this { +class MixerInput : public std::enable_shared_from_this { + public: enum Flags { kLoop = 1, kStopped = 2, kSimulateStereo = 4 }; ~MixerInput(); static std::shared_ptr Create(); - // Set AudioBus for playback. If this MixerInput is already streaming, keeps - // it as pending and sets once playback ends. - void SetAudioBus(std::shared_ptr bus); + bool IsValid() const { return !!audio_bus_; } + + // Set AudioBus for playback. If this MixerInput is already playing, keeps it + // as pending and sets once playback ends. + void SetAudioBus(std::shared_ptr audio_bus); // Starts playback. Remembers last stream position. Resets the stream position // if restart is true. @@ -31,6 +34,7 @@ struct MixerInput : public std::enable_shared_from_this { // Stops playback. Does not reset stream position. void Stop(); + // Set whether the playback should loop or not. void SetLoop(bool loop); // Simulate stereo effect slightly delays one channel. @@ -47,30 +51,57 @@ struct MixerInput : public std::enable_shared_from_this { // Set a callback to be called once playback ends. void SetEndCallback(base::Closure cb); - // Active AudioBus. - std::shared_ptr audio_bus; - // AudioBus to be set as active once playback ends. - std::shared_ptr pending_audio_bus; - // Stream position. - size_t src_index = 0; - size_t accumulator = 0; + // Getters + std::shared_ptr GetAudioBus() const { return audio_bus_; } + unsigned GetFlags() const { return flags_.load(std::memory_order_relaxed); } + size_t GetStep() const { return step_.load(std::memory_order_relaxed); } + float GetAmplitude() const { + return amplitude_.load(std::memory_order_relaxed); + } + float GetAmplitudeInc() const { + return amplitude_inc_.load(std::memory_order_relaxed); + } + float GetMaxAmplitude() const { + return max_amplitude_.load(std::memory_order_relaxed); + } + size_t GetSrcIndex() const { return src_index_; } + size_t GetAccumulator() const { return accumulator_; } - // Accessed by main thread only. - bool streaming = false; - base::Closure end_cb; - base::Closure restart_cb; + bool IsStreamingInProgress() const; - // Accessed by main thread and audio thread. - std::atomic flags{0}; - std::atomic step{100}; - std::atomic amplitude{1.0f}; - std::atomic amplitude_inc{0}; - std::atomic max_amplitude{1.0f}; + // Called by the mixer to save the last sample position. + void SetPosition(size_t index, int accumulator); - // Accessed by audio thread and decoding worker thread. - std::atomic streaming_in_progress{false}; + // Called by the mixer when more data is needed. + bool OnMoreData(bool loop); + + // Called by the mixer when playback ends. + void OnRemovedFromMixer(); private: + // Active AudioBus. + std::shared_ptr audio_bus_; + // AudioBus to be set as active once playback ends. + std::shared_ptr pending_audio_bus_; + // Stream position. + size_t src_index_ = 0; + size_t accumulator_ = 0; + + // Accessed by main thread only. + bool playing_ = false; + base::Closure end_cb_; + base::Closure restart_cb_; + + // Accessed by main thread and audio thread. + std::atomic flags_{0}; + std::atomic step_{100}; + std::atomic amplitude_{1.0f}; + std::atomic amplitude_inc_{0}; + std::atomic max_amplitude_{1.0f}; + + // Accessed by audio thread and decoding worker thread. + std::atomic streaming_in_progress_{false}; + MixerInput(); }; diff --git a/src/engine/sound_player.cc b/src/engine/sound_player.cc index 51b536b..ad6a18b 100644 --- a/src/engine/sound_player.cc +++ b/src/engine/sound_player.cc @@ -26,7 +26,7 @@ void SoundPlayer::SetSound(std::shared_ptr sound) { } void SoundPlayer::Play(bool loop, float fade_in_duration) { - if (!input_->audio_bus) + if (!input_->IsValid()) return; int step = variate_ ? Engine::Get().GetRandomGenerator().Roll(3) - 2 : 0; @@ -35,7 +35,7 @@ void SoundPlayer::Play(bool loop, float fade_in_duration) { if (fade_in_duration > 0) { input_->SetAmplitude(0); input_->SetAmplitudeInc( - 1.0f / (input_->audio_bus->sample_rate() * fade_in_duration)); + 1.0f / (input_->GetAudioBus()->sample_rate() * fade_in_duration)); } else { input_->SetAmplitude(max_amplitude_); input_->SetAmplitudeInc(0); @@ -44,24 +44,24 @@ void SoundPlayer::Play(bool loop, float fade_in_duration) { } void SoundPlayer::Resume(float fade_in_duration) { - if (!input_->audio_bus) + if (!input_->IsValid()) return; if (fade_in_duration > 0) { input_->SetAmplitude(0); input_->SetAmplitudeInc( - 1.0f / (input_->audio_bus->sample_rate() * fade_in_duration)); + 1.0f / (input_->GetAudioBus()->sample_rate() * fade_in_duration)); } input_->Play(Engine::Get().GetAudioMixer(), false); } void SoundPlayer::Stop(float fade_out_duration) { - if (!input_->audio_bus) + if (!input_->IsValid()) return; if (fade_out_duration > 0) input_->SetAmplitudeInc( - -1.0f / (input_->audio_bus->sample_rate() * fade_out_duration)); + -1.0f / (input_->GetAudioBus()->sample_rate() * fade_out_duration)); else input_->Stop(); } diff --git a/src/engine/sound_player.h b/src/engine/sound_player.h index 9b692b2..7b0cb6b 100644 --- a/src/engine/sound_player.h +++ b/src/engine/sound_player.h @@ -9,7 +9,7 @@ namespace eng { class AudioBus; -struct MixerInput; +class MixerInput; class SoundPlayer { public: