mirror of https://github.com/auygun/kaliber.git
Compare commits
2 Commits
c5171ffc03
...
34a73082a6
Author | SHA1 | Date |
---|---|---|
Attila Uygun | 34a73082a6 | |
Attila Uygun | 38813f98b4 |
|
@ -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
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue