mirror of https://github.com/auygun/kaliber.git
Compare commits
No commits in common. "34a73082a6a971a8fd38636754b727f50b2e13ac" and "c5171ffc038f8c433b8ea344e9f6f8d94d1b562a" have entirely different histories.
34a73082a6
...
c5171ffc03
|
@ -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
|
||||||
|
|
|
@ -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 \
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
|
|
@ -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
|
|
|
@ -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,9 +46,10 @@ 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));
|
||||||
fade_in_duration > 0 ? 0 : -1, false);
|
Engine::Get().GetAudioMixer()->Play(resource_, sound_,
|
||||||
|
fade_in_duration > 0 ? 0 : -1, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoundPlayer::Stop(float fade_out_duration) {
|
void SoundPlayer::Stop(float fade_out_duration) {
|
||||||
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue