Compare commits

..

No commits in common. "34a73082a6a971a8fd38636754b727f50b2e13ac" and "c5171ffc038f8c433b8ea344e9f6f8d94d1b562a" have entirely different histories.

12 changed files with 198 additions and 237 deletions

View File

@ -69,7 +69,6 @@ add_library(kaliber SHARED
../../../src/engine/audio/audio_bus.cc ../../../src/engine/audio/audio_bus.cc
../../../src/engine/audio/audio_mixer.cc ../../../src/engine/audio/audio_mixer.cc
../../../src/engine/audio/audio_sink_oboe.cc ../../../src/engine/audio/audio_sink_oboe.cc
../../../src/engine/audio/mixer_input.cc
../../../src/engine/audio/sinc_resampler.cc ../../../src/engine/audio/sinc_resampler.cc
../../../src/engine/drawable.cc ../../../src/engine/drawable.cc
../../../src/engine/engine.cc ../../../src/engine/engine.cc

View File

@ -102,7 +102,6 @@ ENGINE_SRC := \
$(SRC_ROOT)/engine/audio/audio_bus.cc \ $(SRC_ROOT)/engine/audio/audio_bus.cc \
$(SRC_ROOT)/engine/audio/audio_mixer.cc \ $(SRC_ROOT)/engine/audio/audio_mixer.cc \
$(SRC_ROOT)/engine/audio/audio_sink_alsa.cc \ $(SRC_ROOT)/engine/audio/audio_sink_alsa.cc \
$(SRC_ROOT)/engine/audio/mixer_input.cc \
$(SRC_ROOT)/engine/audio/sinc_resampler.cc \ $(SRC_ROOT)/engine/audio/sinc_resampler.cc \
$(SRC_ROOT)/engine/drawable.cc \ $(SRC_ROOT)/engine/drawable.cc \
$(SRC_ROOT)/engine/engine.cc \ $(SRC_ROOT)/engine/engine.cc \

View File

@ -109,10 +109,10 @@ bool Demo::Initialize() {
} }
music_.SetSound("music"); music_.SetSound("music");
music_.SetMaxAmplitude(0.5f); music_.SetMaxAplitude(0.5f);
boss_music_.SetSound("boss_music"); boss_music_.SetSound("boss_music");
boss_music_.SetMaxAmplitude(0.5f); boss_music_.SetMaxAplitude(0.5f);
if (!saved_data_.root().get("audio", Json::Value(true)).asBool()) if (!saved_data_.root().get("audio", Json::Value(true)).asBool())
Engine::Get().SetEnableAudio(false); Engine::Get().SetEnableAudio(false);

View File

@ -734,29 +734,29 @@ void Enemy::SpawnUnit(EnemyType enemy_type,
e.explosion.SetSound("powerup-pick"); e.explosion.SetSound("powerup-pick");
e.spawn.SetSound("powerup-spawn"); e.spawn.SetSound("powerup-spawn");
e.spawn.SetMaxAmplitude(2.0f); e.spawn.SetMaxAplitude(2.0f);
e.spawn.Play(false); e.spawn.Play(false);
} else { } else {
e.explosion.SetSound("explosion"); e.explosion.SetSound("explosion");
e.explosion.SetVariate(true); e.explosion.SetVariate(true);
e.explosion.SetSimulateStereo(true); e.explosion.SetSimulateStereo(true);
e.explosion.SetMaxAmplitude(0.9f); e.explosion.SetMaxAplitude(0.9f);
} }
e.stealth.SetSound("stealth"); e.stealth.SetSound("stealth");
e.stealth.SetVariate(false); e.stealth.SetVariate(false);
e.stealth.SetSimulateStereo(false); e.stealth.SetSimulateStereo(false);
e.stealth.SetMaxAmplitude(0.7f); e.stealth.SetMaxAplitude(0.7f);
e.shield_on.SetSound("shield"); e.shield_on.SetSound("shield");
e.shield_on.SetVariate(false); e.shield_on.SetVariate(false);
e.shield_on.SetSimulateStereo(false); e.shield_on.SetSimulateStereo(false);
e.shield_on.SetMaxAmplitude(0.5f); e.shield_on.SetMaxAplitude(0.5f);
e.hit.SetSound("hit"); e.hit.SetSound("hit");
e.hit.SetVariate(true); e.hit.SetVariate(true);
e.hit.SetSimulateStereo(false); e.hit.SetSimulateStereo(false);
e.hit.SetMaxAmplitude(0.5f); e.hit.SetMaxAplitude(0.5f);
} }
void Enemy::SpawnBoss() { void Enemy::SpawnBoss() {
@ -834,7 +834,7 @@ void Enemy::SpawnBoss() {
e.hit.SetSound("hit"); e.hit.SetSound("hit");
e.hit.SetVariate(true); e.hit.SetVariate(true);
e.hit.SetSimulateStereo(false); e.hit.SetSimulateStereo(false);
e.hit.SetMaxAmplitude(0.5f); e.hit.SetMaxAplitude(0.5f);
}); });
boss_animator_.Play(Animator::kFrames, true); boss_animator_.Play(Animator::kFrames, true);
boss_animator_.Play(Animator::kMovement, false); boss_animator_.Play(Animator::kMovement, false);

View File

@ -149,7 +149,7 @@ bool Menu::Initialize() {
click_.SetSound(click_sound_); click_.SetSound(click_sound_);
click_.SetVariate(false); click_.SetVariate(false);
click_.SetSimulateStereo(false); click_.SetSimulateStereo(false);
click_.SetMaxAmplitude(1.5f); click_.SetMaxAplitude(1.5f);
logo_[0].Create("logo_tex0", {3, 8}); logo_[0].Create("logo_tex0", {3, 8});
logo_[0].SetZOrder(41); logo_[0].SetZOrder(41);

View File

@ -75,7 +75,7 @@ bool Player::Initialize() {
nuke_explosion_.SetSound("nuke"); nuke_explosion_.SetSound("nuke");
nuke_explosion_.SetVariate(false); nuke_explosion_.SetVariate(false);
nuke_explosion_.SetSimulateStereo(false); nuke_explosion_.SetSimulateStereo(false);
nuke_explosion_.SetMaxAmplitude(0.8f); nuke_explosion_.SetMaxAplitude(0.8f);
no_nuke_.SetSound("no_nuke"); no_nuke_.SetSound("no_nuke");
@ -306,7 +306,7 @@ void Player::SetupWeapons() {
laser_shot_[i].SetSound("laser"); laser_shot_[i].SetSound("laser");
laser_shot_[i].SetVariate(true); laser_shot_[i].SetVariate(true);
laser_shot_[i].SetSimulateStereo(false); laser_shot_[i].SetSimulateStereo(false);
laser_shot_[i].SetMaxAmplitude(0.4f); laser_shot_[i].SetMaxAplitude(0.4f);
} }
} }

View File

@ -6,7 +6,6 @@
#include "base/task_runner.h" #include "base/task_runner.h"
#include "base/thread_pool.h" #include "base/thread_pool.h"
#include "engine/audio/audio_bus.h" #include "engine/audio/audio_bus.h"
#include "engine/audio/mixer_input.h"
#if defined(__ANDROID__) #if defined(__ANDROID__)
#include "engine/audio/audio_sink_oboe.h" #include "engine/audio/audio_sink_oboe.h"
@ -18,6 +17,10 @@ using namespace base;
namespace eng { namespace eng {
AudioMixer::Resource::~Resource() {
DLOG(1) << "Destroying audio resource: " << uintptr_t(this);
}
AudioMixer::AudioMixer() AudioMixer::AudioMixer()
: main_thread_task_runner_(TaskRunner::GetThreadLocalTaskRunner()), : main_thread_task_runner_(TaskRunner::GetThreadLocalTaskRunner()),
#if defined(__ANDROID__) #if defined(__ANDROID__)
@ -33,11 +36,98 @@ AudioMixer::~AudioMixer() {
audio_sink_.reset(); audio_sink_.reset();
} }
void AudioMixer::AddInput(std::shared_ptr<MixerInput> input) { std::shared_ptr<void> AudioMixer::CreateResource() {
DCHECK(audio_enabled_); auto resource = std::make_shared<Resource>();
DLOG(1) << "Audio resource created: " << uintptr_t(resource.get());
return resource;
}
void AudioMixer::Play(std::shared_ptr<void> resource,
std::shared_ptr<AudioBus> audio_bus,
float amplitude,
bool reset_pos) {
if (!audio_enabled_)
return;
auto resource_ptr = std::static_pointer_cast<Resource>(resource);
if (resource_ptr->active) {
if (reset_pos)
resource_ptr->flags.fetch_or(kStopped, std::memory_order_relaxed);
if (resource_ptr->flags.load(std::memory_order_relaxed) & kStopped)
resource_ptr->restart_cb = [&, resource, audio_bus, amplitude,
reset_pos]() -> void {
Play(resource, audio_bus, amplitude, reset_pos);
};
return;
}
if (reset_pos) {
resource_ptr->src_index = 0;
resource_ptr->accumulator = 0;
audio_bus->ResetStream();
} else if (resource_ptr->src_index >= audio_bus->samples_per_channel()) {
return;
}
resource_ptr->active = true;
resource_ptr->flags.fetch_and(~kStopped, std::memory_order_relaxed);
resource_ptr->audio_bus = audio_bus;
if (amplitude >= 0)
resource_ptr->amplitude = amplitude;
std::lock_guard<std::mutex> scoped_lock(lock_); std::lock_guard<std::mutex> scoped_lock(lock_);
inputs_[0].push_back(input); play_list_[0].push_back(resource_ptr);
}
void AudioMixer::Stop(std::shared_ptr<void> resource) {
auto resource_ptr = std::static_pointer_cast<Resource>(resource);
if (resource_ptr->active) {
resource_ptr->restart_cb = nullptr;
resource_ptr->flags.fetch_or(kStopped, std::memory_order_relaxed);
}
}
void AudioMixer::SetLoop(std::shared_ptr<void> resource, bool loop) {
auto resource_ptr = std::static_pointer_cast<Resource>(resource);
if (loop)
resource_ptr->flags.fetch_or(kLoop, std::memory_order_relaxed);
else
resource_ptr->flags.fetch_and(~kLoop, std::memory_order_relaxed);
}
void AudioMixer::SetSimulateStereo(std::shared_ptr<void> resource,
bool simulate) {
auto resource_ptr = std::static_pointer_cast<Resource>(resource);
if (simulate)
resource_ptr->flags.fetch_or(kSimulateStereo, std::memory_order_relaxed);
else
resource_ptr->flags.fetch_and(~kSimulateStereo, std::memory_order_relaxed);
}
void AudioMixer::SetResampleStep(std::shared_ptr<void> resource, size_t step) {
auto resource_ptr = std::static_pointer_cast<Resource>(resource);
resource_ptr->step.store(step + 100, std::memory_order_relaxed);
}
void AudioMixer::SetMaxAmplitude(std::shared_ptr<void> resource,
float max_amplitude) {
auto resource_ptr = std::static_pointer_cast<Resource>(resource);
resource_ptr->max_amplitude.store(max_amplitude, std::memory_order_relaxed);
}
void AudioMixer::SetAmplitudeInc(std::shared_ptr<void> resource,
float amplitude_inc) {
auto resource_ptr = std::static_pointer_cast<Resource>(resource);
resource_ptr->amplitude_inc.store(amplitude_inc, std::memory_order_relaxed);
}
void AudioMixer::SetEndCallback(std::shared_ptr<void> resource,
base::Closure cb) {
auto resource_ptr = std::static_pointer_cast<Resource>(resource);
resource_ptr->end_cb = std::move(cb);
} }
void AudioMixer::Suspend() { void AudioMixer::Suspend() {
@ -56,17 +146,17 @@ void AudioMixer::RenderAudio(float* output_buffer, size_t num_frames) {
{ {
std::unique_lock<std::mutex> scoped_lock(lock_, std::try_to_lock); std::unique_lock<std::mutex> scoped_lock(lock_, std::try_to_lock);
if (scoped_lock) if (scoped_lock)
inputs_[1].splice(inputs_[1].end(), inputs_[0]); play_list_[1].splice(play_list_[1].end(), play_list_[0]);
} }
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 = play_list_[1].begin(); it != play_list_[1].end();) {
auto audio_bus = (*it)->audio_bus.get(); auto audio_bus = (*it)->audio_bus.get();
unsigned flags = (*it)->flags.load(std::memory_order_relaxed); unsigned flags = (*it)->flags.load(std::memory_order_relaxed);
bool marked_for_removal = false; bool marked_for_removal = false;
if (flags & MixerInput::kStopped) { if (flags & kStopped) {
marked_for_removal = true; marked_for_removal = true;
} else { } else {
const float* src[2] = {audio_bus->GetChannelData(0), const float* src[2] = {audio_bus->GetChannelData(0),
@ -83,9 +173,8 @@ void AudioMixer::RenderAudio(float* output_buffer, size_t num_frames) {
(*it)->amplitude_inc.load(std::memory_order_relaxed); (*it)->amplitude_inc.load(std::memory_order_relaxed);
float max_amplitude = float max_amplitude =
(*it)->max_amplitude.load(std::memory_order_relaxed); (*it)->max_amplitude.load(std::memory_order_relaxed);
size_t channel_offset = (flags & MixerInput::kSimulateStereo) size_t channel_offset =
? audio_bus->sample_rate() / 10 (flags & kSimulateStereo) ? audio_bus->sample_rate() / 10 : 0;
: 0;
DCHECK(num_samples > 0); DCHECK(num_samples > 0);
@ -98,7 +187,7 @@ void AudioMixer::RenderAudio(float* output_buffer, size_t num_frames) {
size_t ind = channel_offset + src_index; size_t ind = channel_offset + src_index;
if (ind < num_samples) if (ind < num_samples)
output_buffer[i++] += src[1][ind] * amplitude; output_buffer[i++] += src[1][ind] * amplitude;
else if (flags & MixerInput::kLoop) else if (flags & kLoop)
output_buffer[i++] += src[1][ind % num_samples] * amplitude; output_buffer[i++] += src[1][ind % num_samples] * amplitude;
else else
i++; i++;
@ -119,7 +208,7 @@ void AudioMixer::RenderAudio(float* output_buffer, size_t num_frames) {
} else { } else {
if (audio_bus->EndOfStream()) { if (audio_bus->EndOfStream()) {
src_index %= num_samples; src_index %= num_samples;
marked_for_removal = !(flags & MixerInput::kLoop); marked_for_removal = !(flags & kLoop);
break; break;
} }
@ -137,8 +226,7 @@ void AudioMixer::RenderAudio(float* output_buffer, size_t num_frames) {
ThreadPool::Get().PostTask( ThreadPool::Get().PostTask(
HERE, HERE,
std::bind(&AudioMixer::DoStream, this, *it, std::bind(&AudioMixer::DoStream, this, *it, flags & kLoop),
flags & MixerInput::kLoop),
true); true);
} else { } else {
DLOG(0) << "Mixer buffer underrun!"; DLOG(0) << "Mixer buffer underrun!";
@ -152,37 +240,37 @@ void AudioMixer::RenderAudio(float* output_buffer, size_t num_frames) {
} }
if (marked_for_removal) { if (marked_for_removal) {
removed_inputs_.push_back(*it); end_list_.push_back(*it);
it = inputs_[1].erase(it); it = play_list_[1].erase(it);
} else { } else {
++it; ++it;
} }
} }
for (auto it = removed_inputs_.begin(); it != removed_inputs_.end();) { for (auto it = end_list_.begin(); it != end_list_.end();) {
if (!(*it)->streaming_in_progress.load(std::memory_order_relaxed)) { if (!(*it)->streaming_in_progress.load(std::memory_order_relaxed)) {
main_thread_task_runner_->PostTask( main_thread_task_runner_->PostTask(
HERE, std::bind(&AudioMixer::EndCallback, this, *it)); HERE, std::bind(&AudioMixer::EndCallback, this, *it));
it = removed_inputs_.erase(it); it = end_list_.erase(it);
} else { } else {
++it; ++it;
} }
} }
} }
void AudioMixer::DoStream(std::shared_ptr<MixerInput> input, bool loop) { void AudioMixer::DoStream(std::shared_ptr<Resource> resource, bool loop) {
input->audio_bus->Stream(loop); resource->audio_bus->Stream(loop);
input->streaming_in_progress.store(false, std::memory_order_release); resource->streaming_in_progress.store(false, std::memory_order_release);
} }
void AudioMixer::EndCallback(std::shared_ptr<MixerInput> input) { void AudioMixer::EndCallback(std::shared_ptr<Resource> resource) {
input->active = false; resource->active = false;
if (input->end_cb) if (resource->end_cb)
input->end_cb(); resource->end_cb();
if (input->restart_cb) { if (resource->restart_cb) {
input->restart_cb(); resource->restart_cb();
input->restart_cb = nullptr; resource->restart_cb = nullptr;
} }
} }

View File

@ -1,6 +1,7 @@
#ifndef ENGINE_AUDIO_AUDIO_MIXER_H #ifndef ENGINE_AUDIO_AUDIO_MIXER_H
#define ENGINE_AUDIO_AUDIO_MIXER_H #define ENGINE_AUDIO_AUDIO_MIXER_H
#include <atomic>
#include <list> #include <list>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
@ -15,24 +16,31 @@ class TaskRunner;
namespace eng { namespace eng {
class AudioBus; class AudioBus;
struct MixerInput;
// Mix and render audio with low overhead. The mixer has zero or more inputs // Mix and render audio with low overhead. A platform specific AudioSink
// which can be added at any time. The mixer will pull from each input source // implementation is expected to periodically call RenderAudio() in a background
// when it needs more data. Input source will be removed once end-of-stream is // thread.
// reached. Any unfilled frames will be filled with silence. The mixer always
// outputs audio when active, even if input sources underflow. A platform
// specific AudioSink implementation is expected to periodically call
// RenderAudio() in a background thread.
class AudioMixer : public AudioSink::Delegate { class AudioMixer : public AudioSink::Delegate {
public: public:
AudioMixer(); AudioMixer();
~AudioMixer(); ~AudioMixer();
void AddInput(std::shared_ptr<MixerInput> input); std::shared_ptr<void> CreateResource();
void Play(std::shared_ptr<void> resource,
std::shared_ptr<AudioBus> audio_bus,
float amplitude,
bool reset_pos);
void Stop(std::shared_ptr<void> resource);
void SetLoop(std::shared_ptr<void> resource, bool loop);
void SetSimulateStereo(std::shared_ptr<void> resource, bool simulate);
void SetResampleStep(std::shared_ptr<void> resource, size_t step);
void SetMaxAmplitude(std::shared_ptr<void> resource, float max_amplitude);
void SetAmplitudeInc(std::shared_ptr<void> resource, float amplitude_inc);
void SetEndCallback(std::shared_ptr<void> resource, base::Closure cb);
void SetEnableAudio(bool enable) { audio_enabled_ = enable; } void SetEnableAudio(bool enable) { audio_enabled_ = enable; }
bool IsAudioEnabled() const { return audio_enabled_; }
void Suspend(); void Suspend();
void Resume(); void Resume();
@ -40,12 +48,38 @@ class AudioMixer : public AudioSink::Delegate {
size_t GetHardwareSampleRate(); size_t GetHardwareSampleRate();
private: private:
enum SampleFlags { kLoop = 1, kStopped = 2, kSimulateStereo = 4 };
static constexpr int kChannelCount = 2; static constexpr int kChannelCount = 2;
std::list<std::shared_ptr<MixerInput>> inputs_[2]; // An audio resource that gets mixed and rendered to the audio sink.
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<AudioBus> audio_bus;
size_t src_index = 0;
size_t accumulator = 0;
float amplitude = 1.0f;
// Write accessed by main thread, read-only accessed by audio thread.
std::atomic<unsigned> flags{0};
std::atomic<size_t> step{100};
std::atomic<float> amplitude_inc{0};
std::atomic<float> max_amplitude{1.0f};
// Accessed by audio thread and decoder thread.
std::atomic<bool> streaming_in_progress{false};
~Resource();
};
std::list<std::shared_ptr<Resource>> play_list_[2];
std::mutex lock_; std::mutex lock_;
std::list<std::shared_ptr<MixerInput>> removed_inputs_; std::list<std::shared_ptr<Resource>> end_list_;
std::shared_ptr<base::TaskRunner> main_thread_task_runner_; std::shared_ptr<base::TaskRunner> main_thread_task_runner_;
@ -57,9 +91,9 @@ 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 DoStream(std::shared_ptr<Resource> sample, bool loop);
void EndCallback(std::shared_ptr<MixerInput> sample); void EndCallback(std::shared_ptr<Resource> sample);
AudioMixer(const AudioMixer&) = delete; AudioMixer(const AudioMixer&) = delete;
AudioMixer& operator=(const AudioMixer&) = delete; AudioMixer& operator=(const AudioMixer&) = delete;

View File

@ -1,98 +0,0 @@
#include "engine/audio/mixer_input.h"
#include "base/log.h"
#include "engine/audio/audio_bus.h"
#include "engine/audio/audio_mixer.h"
using namespace base;
namespace eng {
MixerInput::MixerInput() {
DLOG(1) << "MixerInput created: " << uintptr_t(this);
}
MixerInput::~MixerInput() {
DLOG(1) << "Destroying MixerInput: " << uintptr_t(this);
}
// static
std::shared_ptr<MixerInput> MixerInput::Create() {
return std::shared_ptr<MixerInput>(new MixerInput());
}
void MixerInput::Play(AudioMixer* mixer,
std::shared_ptr<AudioBus> bus,
float amp,
bool restart) {
if (!mixer->IsAudioEnabled())
return;
if (bus != audio_bus || src_index >= bus->samples_per_channel())
restart = true;
if (active) {
if (restart)
flags.fetch_or(kStopped, std::memory_order_relaxed);
if (flags.load(std::memory_order_relaxed) & kStopped)
restart_cb = [&, mixer, bus, amp, restart]() -> void {
Play(mixer, bus, amp, restart);
};
return;
}
if (restart) {
src_index = 0;
accumulator = 0;
bus->ResetStream();
}
active = true;
flags.fetch_and(~kStopped, std::memory_order_relaxed);
audio_bus = bus;
if (amp >= 0)
amplitude = amp;
mixer->AddInput(shared_from_this());
}
void MixerInput::Stop() {
if (active) {
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);
else
flags.fetch_and(~kLoop, std::memory_order_relaxed);
}
void MixerInput::SetSimulateStereo(bool simulate) {
if (simulate)
flags.fetch_or(kSimulateStereo, std::memory_order_relaxed);
else
flags.fetch_and(~kSimulateStereo, std::memory_order_relaxed);
}
void MixerInput::SetResampleStep(size_t value) {
step.store(value + 100, std::memory_order_relaxed);
}
void MixerInput::SetMaxAmplitude(float value) {
max_amplitude.store(value, std::memory_order_relaxed);
}
void MixerInput::SetAmplitudeInc(float value) {
amplitude_inc.store(value, std::memory_order_relaxed);
}
void MixerInput::SetEndCallback(base::Closure cb) {
end_cb = std::move(cb);
}
} // namespace eng

View File

@ -1,62 +0,0 @@
#ifndef ENGINE_AUDIO_MIXER_INPUT_H
#define ENGINE_AUDIO_MIXER_INPUT_H
#include <atomic>
#include <memory>
#include "base/closure.h"
namespace eng {
class AudioBus;
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<MixerInput> {
enum Flags { kLoop = 1, kStopped = 2, kSimulateStereo = 4 };
~MixerInput();
static std::shared_ptr<MixerInput> Create();
void Play(AudioMixer* mixer,
std::shared_ptr<AudioBus> bus,
float amp,
bool restart);
void Stop();
void SetLoop(bool loop);
void SetSimulateStereo(bool simulate);
void SetResampleStep(size_t value);
void SetMaxAmplitude(float value);
void SetAmplitudeInc(float value);
void SetEndCallback(base::Closure cb);
// 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<AudioBus> audio_bus;
size_t src_index = 0;
size_t accumulator = 0;
float amplitude = 1.0f;
// Write accessed by main thread, read-only accessed by audio thread.
std::atomic<unsigned> flags{0};
std::atomic<size_t> step{100};
std::atomic<float> amplitude_inc{0};
std::atomic<float> max_amplitude{1.0f};
// Accessed by audio thread and decoder thread.
std::atomic<bool> streaming_in_progress{false};
private:
MixerInput();
};
} // namespace eng
#endif // ENGINE_AUDIO_MIXER_INPUT_H

View File

@ -4,17 +4,17 @@
#include "base/log.h" #include "base/log.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"
#include "engine/audio/mixer_input.h"
#include "engine/engine.h" #include "engine/engine.h"
using namespace base; using namespace base;
namespace eng { namespace eng {
SoundPlayer::SoundPlayer() : input_{MixerInput::Create()} {} SoundPlayer::SoundPlayer()
: resource_(Engine::Get().GetAudioMixer()->CreateResource()) {}
SoundPlayer::~SoundPlayer() { SoundPlayer::~SoundPlayer() {
input_->Stop(); Engine::Get().GetAudioMixer()->Stop(resource_);
} }
void SoundPlayer::SetSound(const std::string& asset_name) { void SoundPlayer::SetSound(const std::string& asset_name) {
@ -30,14 +30,15 @@ void SoundPlayer::Play(bool loop, float fade_in_duration) {
return; return;
int step = variate_ ? Engine::Get().GetRandomGenerator().Roll(3) - 2 : 0; int step = variate_ ? Engine::Get().GetRandomGenerator().Roll(3) - 2 : 0;
input_->SetResampleStep(step * 12); Engine::Get().GetAudioMixer()->SetResampleStep(resource_, step * 12);
input_->SetLoop(loop); Engine::Get().GetAudioMixer()->SetLoop(resource_, loop);
if (fade_in_duration > 0) if (fade_in_duration > 0)
input_->SetAmplitudeInc(1.0f / (sound_->sample_rate() * fade_in_duration)); Engine::Get().GetAudioMixer()->SetAmplitudeInc(
resource_, 1.0f / (sound_->sample_rate() * fade_in_duration));
else else
input_->SetAmplitudeInc(0); Engine::Get().GetAudioMixer()->SetAmplitudeInc(resource_, 0);
input_->Play(Engine::Get().GetAudioMixer(), sound_, Engine::Get().GetAudioMixer()->Play(
fade_in_duration > 0 ? 0 : max_amplitude_, true); resource_, sound_, fade_in_duration > 0 ? 0 : max_amplitude_, true);
} }
void SoundPlayer::Resume(float fade_in_duration) { void SoundPlayer::Resume(float fade_in_duration) {
@ -45,8 +46,9 @@ void SoundPlayer::Resume(float fade_in_duration) {
return; return;
if (fade_in_duration > 0) if (fade_in_duration > 0)
input_->SetAmplitudeInc(1.0f / (sound_->sample_rate() * fade_in_duration)); Engine::Get().GetAudioMixer()->SetAmplitudeInc(
input_->Play(Engine::Get().GetAudioMixer(), sound_, resource_, 1.0f / (sound_->sample_rate() * fade_in_duration));
Engine::Get().GetAudioMixer()->Play(resource_, sound_,
fade_in_duration > 0 ? 0 : -1, false); fade_in_duration > 0 ? 0 : -1, false);
} }
@ -55,10 +57,10 @@ void SoundPlayer::Stop(float fade_out_duration) {
return; return;
if (fade_out_duration > 0) if (fade_out_duration > 0)
input_->SetAmplitudeInc(-1.0f / Engine::Get().GetAudioMixer()->SetAmplitudeInc(
(sound_->sample_rate() * fade_out_duration)); resource_, -1.0f / (sound_->sample_rate() * fade_out_duration));
else else
input_->Stop(); Engine::Get().GetAudioMixer()->Stop(resource_);
} }
void SoundPlayer::SetVariate(bool variate) { void SoundPlayer::SetVariate(bool variate) {
@ -66,16 +68,16 @@ void SoundPlayer::SetVariate(bool variate) {
} }
void SoundPlayer::SetSimulateStereo(bool simulate) { void SoundPlayer::SetSimulateStereo(bool simulate) {
input_->SetSimulateStereo(simulate); Engine::Get().GetAudioMixer()->SetSimulateStereo(resource_, simulate);
} }
void SoundPlayer::SetMaxAmplitude(float max_amplitude) { void SoundPlayer::SetMaxAplitude(float max_amplitude) {
max_amplitude_ = max_amplitude; max_amplitude_ = max_amplitude;
input_->SetMaxAmplitude(max_amplitude); Engine::Get().GetAudioMixer()->SetMaxAmplitude(resource_, max_amplitude);
} }
void SoundPlayer::SetEndCallback(base::Closure cb) { void SoundPlayer::SetEndCallback(base::Closure cb) {
input_->SetEndCallback(cb); Engine::Get().GetAudioMixer()->SetEndCallback(resource_, cb);
} }
} // namespace eng } // namespace eng

View File

@ -9,7 +9,6 @@
namespace eng { namespace eng {
class AudioBus; class AudioBus;
struct MixerInput;
class SoundPlayer { class SoundPlayer {
public: public:
@ -32,13 +31,13 @@ class SoundPlayer {
// Enable or disable stereo simulation effect. Disabled by default. // Enable or disable stereo simulation effect. Disabled by default.
void SetSimulateStereo(bool simulate); void SetSimulateStereo(bool simulate);
void SetMaxAmplitude(float max_amplitude); void SetMaxAplitude(float max_amplitude);
// Set callback to be called once playback stops. // Set callback to be called once playback stops.
void SetEndCallback(base::Closure cb); void SetEndCallback(base::Closure cb);
private: private:
std::shared_ptr<MixerInput> input_; std::shared_ptr<void> resource_;
std::shared_ptr<AudioBus> sound_; std::shared_ptr<AudioBus> sound_;
float max_amplitude_ = 1.0f; float max_amplitude_ = 1.0f;