mirror of https://github.com/auygun/kaliber.git
Move AudioMixer::Resource to MixerInput
This commit is contained in:
parent
c5171ffc03
commit
38813f98b4
|
@ -69,6 +69,7 @@ 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,6 +102,7 @@ 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 \
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#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"
|
||||||
|
@ -17,10 +18,6 @@ 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__)
|
||||||
|
@ -36,98 +33,11 @@ AudioMixer::~AudioMixer() {
|
||||||
audio_sink_.reset();
|
audio_sink_.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<void> AudioMixer::CreateResource() {
|
void AudioMixer::AddInput(std::shared_ptr<MixerInput> input) {
|
||||||
auto resource = std::make_shared<Resource>();
|
DCHECK(audio_enabled_);
|
||||||
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_);
|
||||||
play_list_[0].push_back(resource_ptr);
|
inputs_[0].push_back(input);
|
||||||
}
|
|
||||||
|
|
||||||
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() {
|
||||||
|
@ -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);
|
std::unique_lock<std::mutex> scoped_lock(lock_, std::try_to_lock);
|
||||||
if (scoped_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);
|
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();
|
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 & kStopped) {
|
if (flags & MixerInput::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),
|
||||||
|
@ -173,8 +83,9 @@ 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 =
|
size_t channel_offset = (flags & MixerInput::kSimulateStereo)
|
||||||
(flags & kSimulateStereo) ? audio_bus->sample_rate() / 10 : 0;
|
? audio_bus->sample_rate() / 10
|
||||||
|
: 0;
|
||||||
|
|
||||||
DCHECK(num_samples > 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;
|
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 & kLoop)
|
else if (flags & MixerInput::kLoop)
|
||||||
output_buffer[i++] += src[1][ind % num_samples] * amplitude;
|
output_buffer[i++] += src[1][ind % num_samples] * amplitude;
|
||||||
else
|
else
|
||||||
i++;
|
i++;
|
||||||
|
@ -208,7 +119,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 & kLoop);
|
marked_for_removal = !(flags & MixerInput::kLoop);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -226,7 +137,8 @@ void AudioMixer::RenderAudio(float* output_buffer, size_t num_frames) {
|
||||||
|
|
||||||
ThreadPool::Get().PostTask(
|
ThreadPool::Get().PostTask(
|
||||||
HERE,
|
HERE,
|
||||||
std::bind(&AudioMixer::DoStream, this, *it, flags & kLoop),
|
std::bind(&AudioMixer::DoStream, this, *it,
|
||||||
|
flags & MixerInput::kLoop),
|
||||||
true);
|
true);
|
||||||
} else {
|
} else {
|
||||||
DLOG(0) << "Mixer buffer underrun!";
|
DLOG(0) << "Mixer buffer underrun!";
|
||||||
|
@ -240,37 +152,37 @@ void AudioMixer::RenderAudio(float* output_buffer, size_t num_frames) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (marked_for_removal) {
|
if (marked_for_removal) {
|
||||||
end_list_.push_back(*it);
|
removed_inputs_.push_back(*it);
|
||||||
it = play_list_[1].erase(it);
|
it = inputs_[1].erase(it);
|
||||||
} else {
|
} else {
|
||||||
++it;
|
++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)) {
|
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 = end_list_.erase(it);
|
it = removed_inputs_.erase(it);
|
||||||
} else {
|
} else {
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioMixer::DoStream(std::shared_ptr<Resource> resource, bool loop) {
|
void AudioMixer::DoStream(std::shared_ptr<MixerInput> input, bool loop) {
|
||||||
resource->audio_bus->Stream(loop);
|
input->audio_bus->Stream(loop);
|
||||||
resource->streaming_in_progress.store(false, std::memory_order_release);
|
input->streaming_in_progress.store(false, std::memory_order_release);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioMixer::EndCallback(std::shared_ptr<Resource> resource) {
|
void AudioMixer::EndCallback(std::shared_ptr<MixerInput> input) {
|
||||||
resource->active = false;
|
input->active = false;
|
||||||
|
|
||||||
if (resource->end_cb)
|
if (input->end_cb)
|
||||||
resource->end_cb();
|
input->end_cb();
|
||||||
if (resource->restart_cb) {
|
if (input->restart_cb) {
|
||||||
resource->restart_cb();
|
input->restart_cb();
|
||||||
resource->restart_cb = nullptr;
|
input->restart_cb = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
#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>
|
||||||
|
@ -16,31 +15,24 @@ class TaskRunner;
|
||||||
namespace eng {
|
namespace eng {
|
||||||
|
|
||||||
class AudioBus;
|
class AudioBus;
|
||||||
|
struct MixerInput;
|
||||||
|
|
||||||
// Mix and render audio with low overhead. A platform specific AudioSink
|
// Mix and render audio with low overhead. The mixer has zero or more inputs
|
||||||
// implementation is expected to periodically call RenderAudio() in a background
|
// which can be added at any time. The mixer will pull from each input source
|
||||||
// thread.
|
// 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 {
|
class AudioMixer : public AudioSink::Delegate {
|
||||||
public:
|
public:
|
||||||
AudioMixer();
|
AudioMixer();
|
||||||
~AudioMixer();
|
~AudioMixer();
|
||||||
|
|
||||||
std::shared_ptr<void> CreateResource();
|
void AddInput(std::shared_ptr<MixerInput> input);
|
||||||
|
|
||||||
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();
|
||||||
|
@ -48,38 +40,12 @@ 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;
|
||||||
|
|
||||||
// An audio resource that gets mixed and rendered to the audio sink.
|
std::list<std::shared_ptr<MixerInput>> inputs_[2];
|
||||||
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<Resource>> end_list_;
|
std::list<std::shared_ptr<MixerInput>> removed_inputs_;
|
||||||
|
|
||||||
std::shared_ptr<base::TaskRunner> main_thread_task_runner_;
|
std::shared_ptr<base::TaskRunner> main_thread_task_runner_;
|
||||||
|
|
||||||
|
@ -91,9 +57,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<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(const AudioMixer&) = delete;
|
||||||
AudioMixer& operator=(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 "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()
|
SoundPlayer::SoundPlayer() : input_{MixerInput::Create()} {}
|
||||||
: resource_(Engine::Get().GetAudioMixer()->CreateResource()) {}
|
|
||||||
|
|
||||||
SoundPlayer::~SoundPlayer() {
|
SoundPlayer::~SoundPlayer() {
|
||||||
Engine::Get().GetAudioMixer()->Stop(resource_);
|
input_->Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoundPlayer::SetSound(const std::string& asset_name) {
|
void SoundPlayer::SetSound(const std::string& asset_name) {
|
||||||
|
@ -30,15 +30,14 @@ 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;
|
||||||
Engine::Get().GetAudioMixer()->SetResampleStep(resource_, step * 12);
|
input_->SetResampleStep(step * 12);
|
||||||
Engine::Get().GetAudioMixer()->SetLoop(resource_, loop);
|
input_->SetLoop(loop);
|
||||||
if (fade_in_duration > 0)
|
if (fade_in_duration > 0)
|
||||||
Engine::Get().GetAudioMixer()->SetAmplitudeInc(
|
input_->SetAmplitudeInc(1.0f / (sound_->sample_rate() * fade_in_duration));
|
||||||
resource_, 1.0f / (sound_->sample_rate() * fade_in_duration));
|
|
||||||
else
|
else
|
||||||
Engine::Get().GetAudioMixer()->SetAmplitudeInc(resource_, 0);
|
input_->SetAmplitudeInc(0);
|
||||||
Engine::Get().GetAudioMixer()->Play(
|
input_->Play(Engine::Get().GetAudioMixer(), sound_,
|
||||||
resource_, sound_, fade_in_duration > 0 ? 0 : max_amplitude_, true);
|
fade_in_duration > 0 ? 0 : max_amplitude_, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoundPlayer::Resume(float fade_in_duration) {
|
void SoundPlayer::Resume(float fade_in_duration) {
|
||||||
|
@ -46,10 +45,9 @@ void SoundPlayer::Resume(float fade_in_duration) {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (fade_in_duration > 0)
|
if (fade_in_duration > 0)
|
||||||
Engine::Get().GetAudioMixer()->SetAmplitudeInc(
|
input_->SetAmplitudeInc(1.0f / (sound_->sample_rate() * fade_in_duration));
|
||||||
resource_, 1.0f / (sound_->sample_rate() * fade_in_duration));
|
input_->Play(Engine::Get().GetAudioMixer(), sound_,
|
||||||
Engine::Get().GetAudioMixer()->Play(resource_, sound_,
|
fade_in_duration > 0 ? 0 : -1, false);
|
||||||
fade_in_duration > 0 ? 0 : -1, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoundPlayer::Stop(float fade_out_duration) {
|
void SoundPlayer::Stop(float fade_out_duration) {
|
||||||
|
@ -57,10 +55,10 @@ void SoundPlayer::Stop(float fade_out_duration) {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (fade_out_duration > 0)
|
if (fade_out_duration > 0)
|
||||||
Engine::Get().GetAudioMixer()->SetAmplitudeInc(
|
input_->SetAmplitudeInc(-1.0f /
|
||||||
resource_, -1.0f / (sound_->sample_rate() * fade_out_duration));
|
(sound_->sample_rate() * fade_out_duration));
|
||||||
else
|
else
|
||||||
Engine::Get().GetAudioMixer()->Stop(resource_);
|
input_->Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoundPlayer::SetVariate(bool variate) {
|
void SoundPlayer::SetVariate(bool variate) {
|
||||||
|
@ -68,16 +66,16 @@ void SoundPlayer::SetVariate(bool variate) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoundPlayer::SetSimulateStereo(bool simulate) {
|
void SoundPlayer::SetSimulateStereo(bool simulate) {
|
||||||
Engine::Get().GetAudioMixer()->SetSimulateStereo(resource_, simulate);
|
input_->SetSimulateStereo(simulate);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoundPlayer::SetMaxAplitude(float max_amplitude) {
|
void SoundPlayer::SetMaxAplitude(float max_amplitude) {
|
||||||
max_amplitude_ = max_amplitude;
|
max_amplitude_ = max_amplitude;
|
||||||
Engine::Get().GetAudioMixer()->SetMaxAmplitude(resource_, max_amplitude);
|
input_->SetMaxAmplitude(max_amplitude);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoundPlayer::SetEndCallback(base::Closure cb) {
|
void SoundPlayer::SetEndCallback(base::Closure cb) {
|
||||||
Engine::Get().GetAudioMixer()->SetEndCallback(resource_, cb);
|
input_->SetEndCallback(cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace eng
|
} // namespace eng
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
namespace eng {
|
namespace eng {
|
||||||
|
|
||||||
class AudioBus;
|
class AudioBus;
|
||||||
|
struct MixerInput;
|
||||||
|
|
||||||
class SoundPlayer {
|
class SoundPlayer {
|
||||||
public:
|
public:
|
||||||
|
@ -31,13 +32,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 SetMaxAplitude(float max_amplitude);
|
void SetMaxAplitude(float max_amplitude); // TODO: typo
|
||||||
|
|
||||||
// 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<void> resource_;
|
std::shared_ptr<MixerInput> input_;
|
||||||
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