Compare commits

...

2 Commits

Author SHA1 Message Date
Attila Uygun 34a73082a6 Fix typo 2023-07-13 10:56:51 +02:00
Attila Uygun 38813f98b4 Move AudioMixer::Resource to MixerInput 2023-07-13 10:49:42 +02:00
12 changed files with 237 additions and 198 deletions

View File

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

View File

@ -102,6 +102,7 @@ ENGINE_SRC := \
$(SRC_ROOT)/engine/audio/audio_bus.cc \
$(SRC_ROOT)/engine/audio/audio_mixer.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/drawable.cc \
$(SRC_ROOT)/engine/engine.cc \

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,6 @@
#ifndef ENGINE_AUDIO_AUDIO_MIXER_H
#define ENGINE_AUDIO_AUDIO_MIXER_H
#include <atomic>
#include <list>
#include <memory>
#include <mutex>
@ -16,31 +15,24 @@ class TaskRunner;
namespace eng {
class AudioBus;
struct MixerInput;
// Mix and render audio with low overhead. A platform specific AudioSink
// implementation is expected to periodically call RenderAudio() in a background
// thread.
// 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
// when it needs more data. Input source will be removed once end-of-stream is
// 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 {
public:
AudioMixer();
~AudioMixer();
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 AddInput(std::shared_ptr<MixerInput> input);
void SetEnableAudio(bool enable) { audio_enabled_ = enable; }
bool IsAudioEnabled() const { return audio_enabled_; }
void Suspend();
void Resume();
@ -48,38 +40,12 @@ class AudioMixer : public AudioSink::Delegate {
size_t GetHardwareSampleRate();
private:
enum SampleFlags { kLoop = 1, kStopped = 2, kSimulateStereo = 4 };
static constexpr int kChannelCount = 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::list<std::shared_ptr<MixerInput>> inputs_[2];
std::mutex lock_;
std::list<std::shared_ptr<Resource>> end_list_;
std::list<std::shared_ptr<MixerInput>> removed_inputs_;
std::shared_ptr<base::TaskRunner> main_thread_task_runner_;
@ -91,9 +57,9 @@ 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<Resource> sample, bool loop);
void DoStream(std::shared_ptr<MixerInput> sample, bool loop);
void EndCallback(std::shared_ptr<Resource> sample);
void EndCallback(std::shared_ptr<MixerInput> sample);
AudioMixer(const AudioMixer&) = delete;
AudioMixer& operator=(const AudioMixer&) = delete;

View File

@ -0,0 +1,98 @@
#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

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

View File

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