Make member variables in MixerInput private

This commit is contained in:
Attila Uygun 2023-07-15 11:01:32 +02:00
parent 19910be27e
commit e65d37e846
6 changed files with 158 additions and 117 deletions

View File

@ -4,7 +4,6 @@
#include "base/log.h" #include "base/log.h"
#include "base/task_runner.h" #include "base/task_runner.h"
#include "base/thread_pool.h"
#include "engine/audio/audio_bus.h" #include "engine/audio/audio_bus.h"
#include "engine/audio/mixer_input.h" #include "engine/audio/mixer_input.h"
@ -33,11 +32,11 @@ AudioMixer::~AudioMixer() {
audio_sink_.reset(); audio_sink_.reset();
} }
void AudioMixer::AddInput(std::shared_ptr<MixerInput> input) { void AudioMixer::AddInput(std::shared_ptr<MixerInput> mixer_input) {
DCHECK(audio_enabled_); DCHECK(audio_enabled_);
std::lock_guard<std::mutex> scoped_lock(lock_); std::lock_guard<std::mutex> scoped_lock(lock_);
inputs_[0].push_back(input); inputs_[0].push_back(mixer_input);
} }
void AudioMixer::Suspend() { 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); memset(output_buffer, 0, sizeof(float) * num_frames * kChannelCount);
for (auto it = inputs_[1].begin(); it != inputs_[1].end();) { for (auto it = inputs_[1].begin(); it != inputs_[1].end();) {
auto audio_bus = (*it)->audio_bus.get(); auto* audio_bus = (*it)->GetAudioBus().get();
unsigned flags = (*it)->flags.load(std::memory_order_relaxed); unsigned flags = (*it)->GetFlags();
bool marked_for_removal = false; bool marked_for_removal = false;
if (flags & MixerInput::kStopped) { if (flags & MixerInput::kStopped) {
@ -75,14 +74,12 @@ void AudioMixer::RenderAudio(float* output_buffer, size_t num_frames) {
src[1] = src[0]; // mono. src[1] = src[0]; // mono.
size_t num_samples = audio_bus->samples_per_channel(); size_t num_samples = audio_bus->samples_per_channel();
size_t src_index = (*it)->src_index; size_t src_index = (*it)->GetSrcIndex();
size_t step = (*it)->step.load(std::memory_order_relaxed); size_t step = (*it)->GetStep();
size_t accumulator = (*it)->accumulator; size_t accumulator = (*it)->GetAccumulator();
float amplitude = (*it)->amplitude; float amplitude = (*it)->GetAmplitude();
float amplitude_inc = float amplitude_inc = (*it)->GetAmplitudeInc();
(*it)->amplitude_inc.load(std::memory_order_relaxed); float max_amplitude = (*it)->GetMaxAmplitude();
float max_amplitude =
(*it)->max_amplitude.load(std::memory_order_relaxed);
size_t channel_offset = (flags & MixerInput::kSimulateStereo) size_t channel_offset = (flags & MixerInput::kSimulateStereo)
? audio_bus->sample_rate() / 10 ? audio_bus->sample_rate() / 10
: 0; : 0;
@ -118,37 +115,29 @@ void AudioMixer::RenderAudio(float* output_buffer, size_t num_frames) {
accumulator %= 100; accumulator %= 100;
} else { } else {
if (audio_bus->EndOfStream()) { if (audio_bus->EndOfStream()) {
src_index %= num_samples; if (!(flags & MixerInput::kLoop))
marked_for_removal = !(flags & MixerInput::kLoop); marked_for_removal = true;
else
src_index %= num_samples;
break; break;
} }
if (!(*it)->streaming_in_progress.load(std::memory_order_acquire)) { if ((*it)->OnMoreData(!!(flags & MixerInput::kLoop))) {
src_index %= num_samples; 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[0] = audio_bus->GetChannelData(0);
src[1] = audio_bus->GetChannelData(1); src[1] = audio_bus->GetChannelData(1);
if (!src[1]) if (!src[1])
src[1] = src[0]; // mono. src[1] = src[0]; // mono.
num_samples = audio_bus->samples_per_channel(); num_samples = audio_bus->samples_per_channel();
ThreadPool::Get().PostTask(
HERE,
std::bind(&AudioMixer::DoStream, this, *it,
flags & MixerInput::kLoop),
true);
} else { } else {
DLOG(0) << "Mixer buffer underrun!"; DLOG(0) << "Mixer buffer underrun!";
} }
} }
} }
(*it)->src_index = src_index; // Remember last sample position and volume.
(*it)->accumulator = accumulator; (*it)->SetPosition(src_index, accumulator);
(*it)->amplitude = amplitude; (*it)->SetAmplitude(amplitude);
} }
if (marked_for_removal) { 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();) { 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( main_thread_task_runner_->PostTask(
HERE, std::bind(&AudioMixer::EndCallback, this, *it)); HERE, std::bind(&MixerInput::OnRemovedFromMixer, *it));
it = removed_inputs_.erase(it); it = removed_inputs_.erase(it);
} else { } else {
++it; ++it;
@ -170,25 +159,4 @@ void AudioMixer::RenderAudio(float* output_buffer, size_t num_frames) {
} }
} }
void AudioMixer::DoStream(std::shared_ptr<MixerInput> input, bool loop) {
input->audio_bus->Stream(loop);
input->streaming_in_progress.store(false, std::memory_order_release);
}
void AudioMixer::EndCallback(std::shared_ptr<MixerInput> 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 } // namespace eng

View File

@ -14,8 +14,7 @@ class TaskRunner;
namespace eng { namespace eng {
class AudioBus; class MixerInput;
struct MixerInput;
// Mix and render audio with low overhead. The mixer has zero or more inputs // 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 // 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();
~AudioMixer(); ~AudioMixer();
void AddInput(std::shared_ptr<MixerInput> input); void AddInput(std::shared_ptr<MixerInput> mixer_input);
void SetEnableAudio(bool enable) { audio_enabled_ = enable; } void SetEnableAudio(bool enable) { audio_enabled_ = enable; }
bool IsAudioEnabled() const { return audio_enabled_; } bool IsAudioEnabled() const { return audio_enabled_; }
@ -57,10 +56,6 @@ class AudioMixer : public AudioSink::Delegate {
int GetChannelCount() final { return kChannelCount; } int GetChannelCount() final { return kChannelCount; }
void RenderAudio(float* output_buffer, size_t num_frames) final; void RenderAudio(float* output_buffer, size_t num_frames) final;
void DoStream(std::shared_ptr<MixerInput> sample, bool loop);
void EndCallback(std::shared_ptr<MixerInput> sample);
AudioMixer(const AudioMixer&) = delete; AudioMixer(const AudioMixer&) = delete;
AudioMixer& operator=(const AudioMixer&) = delete; AudioMixer& operator=(const AudioMixer&) = delete;
}; };

View File

@ -1,6 +1,7 @@
#include "engine/audio/mixer_input.h" #include "engine/audio/mixer_input.h"
#include "base/log.h" #include "base/log.h"
#include "base/thread_pool.h"
#include "engine/audio/audio_bus.h" #include "engine/audio/audio_bus.h"
#include "engine/audio/audio_mixer.h" #include "engine/audio/audio_mixer.h"
@ -21,78 +22,124 @@ std::shared_ptr<MixerInput> MixerInput::Create() {
return std::shared_ptr<MixerInput>(new MixerInput()); return std::shared_ptr<MixerInput>(new MixerInput());
} }
void MixerInput::SetAudioBus(std::shared_ptr<AudioBus> bus) { void MixerInput::SetAudioBus(std::shared_ptr<AudioBus> audio_bus) {
if (streaming) if (playing_)
pending_audio_bus = bus; pending_audio_bus_ = audio_bus;
else else
audio_bus = bus; audio_bus_ = audio_bus;
} }
void MixerInput::Play(AudioMixer* mixer, bool restart) { void MixerInput::Play(AudioMixer* mixer, bool restart) {
if (!mixer->IsAudioEnabled()) if (!mixer->IsAudioEnabled()) {
if (!playing_ && end_cb_)
end_cb_();
return; return;
}
// If already streaming check if stream position needs to be reset. // If already playing check if stream position needs to be reset.
if (streaming) { if (playing_) {
if (restart) 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) if (flags_.load(std::memory_order_relaxed) & kStopped)
restart_cb = [&, mixer, restart]() -> void { Play(mixer, restart); }; restart_cb_ = [&, mixer, restart]() -> void { Play(mixer, restart); };
return; return;
} }
if (restart || audio_bus->EndOfStream()) { if (restart || audio_bus_->EndOfStream()) {
src_index = 0; src_index_ = 0;
accumulator = 0; accumulator_ = 0;
audio_bus->ResetStream(); audio_bus_->ResetStream();
} }
streaming = true; playing_ = true;
flags.fetch_and(~kStopped, std::memory_order_relaxed); flags_.fetch_and(~kStopped, std::memory_order_relaxed);
mixer->AddInput(shared_from_this()); mixer->AddInput(shared_from_this());
} }
void MixerInput::Stop() { void MixerInput::Stop() {
if (streaming) { if (playing_) {
restart_cb = nullptr; restart_cb_ = nullptr;
flags.fetch_or(kStopped, std::memory_order_relaxed); flags_.fetch_or(kStopped, std::memory_order_relaxed);
} }
} }
void MixerInput::SetLoop(bool loop) { void MixerInput::SetLoop(bool loop) {
if (loop) if (loop)
flags.fetch_or(kLoop, std::memory_order_relaxed); flags_.fetch_or(kLoop, std::memory_order_relaxed);
else else
flags.fetch_and(~kLoop, std::memory_order_relaxed); flags_.fetch_and(~kLoop, std::memory_order_relaxed);
} }
void MixerInput::SetSimulateStereo(bool simulate) { void MixerInput::SetSimulateStereo(bool simulate) {
if (simulate) if (simulate)
flags.fetch_or(kSimulateStereo, std::memory_order_relaxed); flags_.fetch_or(kSimulateStereo, std::memory_order_relaxed);
else else
flags.fetch_and(~kSimulateStereo, std::memory_order_relaxed); flags_.fetch_and(~kSimulateStereo, std::memory_order_relaxed);
} }
void MixerInput::SetResampleStep(size_t value) { 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) { void MixerInput::SetAmplitude(float value) {
amplitude.store(value, std::memory_order_relaxed); amplitude_.store(value, std::memory_order_relaxed);
} }
void MixerInput::SetMaxAmplitude(float value) { 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) { 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) { 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 } // namespace eng

View File

@ -13,16 +13,19 @@ class AudioMixer;
// An audio input stream that gets mixed and rendered to the audio sink. Handles // An audio input stream that gets mixed and rendered to the audio sink. Handles
// playback and volume control. // playback and volume control.
struct MixerInput : public std::enable_shared_from_this<MixerInput> { class MixerInput : public std::enable_shared_from_this<MixerInput> {
public:
enum Flags { kLoop = 1, kStopped = 2, kSimulateStereo = 4 }; enum Flags { kLoop = 1, kStopped = 2, kSimulateStereo = 4 };
~MixerInput(); ~MixerInput();
static std::shared_ptr<MixerInput> Create(); static std::shared_ptr<MixerInput> Create();
// Set AudioBus for playback. If this MixerInput is already streaming, keeps bool IsValid() const { return !!audio_bus_; }
// it as pending and sets once playback ends.
void SetAudioBus(std::shared_ptr<AudioBus> 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<AudioBus> audio_bus);
// Starts playback. Remembers last stream position. Resets the stream position // Starts playback. Remembers last stream position. Resets the stream position
// if restart is true. // if restart is true.
@ -31,6 +34,7 @@ struct MixerInput : public std::enable_shared_from_this<MixerInput> {
// Stops playback. Does not reset stream position. // Stops playback. Does not reset stream position.
void Stop(); void Stop();
// Set whether the playback should loop or not.
void SetLoop(bool loop); void SetLoop(bool loop);
// Simulate stereo effect slightly delays one channel. // Simulate stereo effect slightly delays one channel.
@ -47,30 +51,57 @@ struct MixerInput : public std::enable_shared_from_this<MixerInput> {
// Set a callback to be called once playback ends. // Set a callback to be called once playback ends.
void SetEndCallback(base::Closure cb); void SetEndCallback(base::Closure cb);
// Active AudioBus. // Getters
std::shared_ptr<AudioBus> audio_bus; std::shared_ptr<AudioBus> GetAudioBus() const { return audio_bus_; }
// AudioBus to be set as active once playback ends. unsigned GetFlags() const { return flags_.load(std::memory_order_relaxed); }
std::shared_ptr<AudioBus> pending_audio_bus; size_t GetStep() const { return step_.load(std::memory_order_relaxed); }
// Stream position. float GetAmplitude() const {
size_t src_index = 0; return amplitude_.load(std::memory_order_relaxed);
size_t accumulator = 0; }
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 IsStreamingInProgress() const;
bool streaming = false;
base::Closure end_cb;
base::Closure restart_cb;
// Accessed by main thread and audio thread. // Called by the mixer to save the last sample position.
std::atomic<unsigned> flags{0}; void SetPosition(size_t index, int accumulator);
std::atomic<size_t> step{100};
std::atomic<float> amplitude{1.0f};
std::atomic<float> amplitude_inc{0};
std::atomic<float> max_amplitude{1.0f};
// Accessed by audio thread and decoding worker thread. // Called by the mixer when more data is needed.
std::atomic<bool> streaming_in_progress{false}; bool OnMoreData(bool loop);
// Called by the mixer when playback ends.
void OnRemovedFromMixer();
private: private:
// Active AudioBus.
std::shared_ptr<AudioBus> audio_bus_;
// AudioBus to be set as active once playback ends.
std::shared_ptr<AudioBus> 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<unsigned> flags_{0};
std::atomic<size_t> step_{100};
std::atomic<float> amplitude_{1.0f};
std::atomic<float> amplitude_inc_{0};
std::atomic<float> max_amplitude_{1.0f};
// Accessed by audio thread and decoding worker thread.
std::atomic<bool> streaming_in_progress_{false};
MixerInput(); MixerInput();
}; };

View File

@ -26,7 +26,7 @@ void SoundPlayer::SetSound(std::shared_ptr<AudioBus> sound) {
} }
void SoundPlayer::Play(bool loop, float fade_in_duration) { void SoundPlayer::Play(bool loop, float fade_in_duration) {
if (!input_->audio_bus) if (!input_->IsValid())
return; return;
int step = variate_ ? Engine::Get().GetRandomGenerator().Roll(3) - 2 : 0; 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) { if (fade_in_duration > 0) {
input_->SetAmplitude(0); input_->SetAmplitude(0);
input_->SetAmplitudeInc( input_->SetAmplitudeInc(
1.0f / (input_->audio_bus->sample_rate() * fade_in_duration)); 1.0f / (input_->GetAudioBus()->sample_rate() * fade_in_duration));
} else { } else {
input_->SetAmplitude(max_amplitude_); input_->SetAmplitude(max_amplitude_);
input_->SetAmplitudeInc(0); input_->SetAmplitudeInc(0);
@ -44,24 +44,24 @@ void SoundPlayer::Play(bool loop, float fade_in_duration) {
} }
void SoundPlayer::Resume(float fade_in_duration) { void SoundPlayer::Resume(float fade_in_duration) {
if (!input_->audio_bus) if (!input_->IsValid())
return; return;
if (fade_in_duration > 0) { if (fade_in_duration > 0) {
input_->SetAmplitude(0); input_->SetAmplitude(0);
input_->SetAmplitudeInc( 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); input_->Play(Engine::Get().GetAudioMixer(), false);
} }
void SoundPlayer::Stop(float fade_out_duration) { void SoundPlayer::Stop(float fade_out_duration) {
if (!input_->audio_bus) if (!input_->IsValid())
return; return;
if (fade_out_duration > 0) if (fade_out_duration > 0)
input_->SetAmplitudeInc( input_->SetAmplitudeInc(
-1.0f / (input_->audio_bus->sample_rate() * fade_out_duration)); -1.0f / (input_->GetAudioBus()->sample_rate() * fade_out_duration));
else else
input_->Stop(); input_->Stop();
} }

View File

@ -9,7 +9,7 @@
namespace eng { namespace eng {
class AudioBus; class AudioBus;
struct MixerInput; class MixerInput;
class SoundPlayer { class SoundPlayer {
public: public: