mirror of https://github.com/auygun/kaliber.git
Audio code refactoring.
This commit is contained in:
parent
c6501323cf
commit
44aa8b2a33
|
@ -66,7 +66,6 @@ add_library(kaliber SHARED
|
||||||
../../../src/engine/animator.cc
|
../../../src/engine/animator.cc
|
||||||
../../../src/engine/audio/audio_base.cc
|
../../../src/engine/audio/audio_base.cc
|
||||||
../../../src/engine/audio/audio_oboe.cc
|
../../../src/engine/audio/audio_oboe.cc
|
||||||
../../../src/engine/audio/audio_resource.cc
|
|
||||||
../../../src/engine/drawable.cc
|
../../../src/engine/drawable.cc
|
||||||
../../../src/engine/engine.cc
|
../../../src/engine/engine.cc
|
||||||
../../../src/engine/font.cc
|
../../../src/engine/font.cc
|
||||||
|
|
|
@ -91,7 +91,6 @@ GLTEST_SRC := \
|
||||||
$(SRC_ROOT)/engine/animator.cc \
|
$(SRC_ROOT)/engine/animator.cc \
|
||||||
$(SRC_ROOT)/engine/audio/audio_alsa.cc \
|
$(SRC_ROOT)/engine/audio/audio_alsa.cc \
|
||||||
$(SRC_ROOT)/engine/audio/audio_base.cc \
|
$(SRC_ROOT)/engine/audio/audio_base.cc \
|
||||||
$(SRC_ROOT)/engine/audio/audio_resource.cc \
|
|
||||||
$(SRC_ROOT)/engine/drawable.cc \
|
$(SRC_ROOT)/engine/drawable.cc \
|
||||||
$(SRC_ROOT)/engine/engine.cc \
|
$(SRC_ROOT)/engine/engine.cc \
|
||||||
$(SRC_ROOT)/engine/font.cc \
|
$(SRC_ROOT)/engine/font.cc \
|
||||||
|
|
|
@ -46,6 +46,11 @@ inline void AlignedFree(void* mem) {
|
||||||
free(mem);
|
free(mem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <int kAlignment>
|
||||||
|
inline bool IsAligned(void* ptr) {
|
||||||
|
return (reinterpret_cast<uintptr_t>(ptr) & (kAlignment - 1)) == 0U;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace base
|
} // namespace base
|
||||||
|
|
||||||
#endif // MEM_H
|
#endif // MEM_H
|
||||||
|
|
|
@ -8,8 +8,6 @@ source_set("engine") {
|
||||||
"audio/audio_base.cc",
|
"audio/audio_base.cc",
|
||||||
"audio/audio_base.h",
|
"audio/audio_base.h",
|
||||||
"audio/audio_forward.h",
|
"audio/audio_forward.h",
|
||||||
"audio/audio_resource.cc",
|
|
||||||
"audio/audio_resource.h",
|
|
||||||
"audio/audio_sample.h",
|
"audio/audio_sample.h",
|
||||||
"audio/audio.h",
|
"audio/audio.h",
|
||||||
"drawable.cc",
|
"drawable.cc",
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "../../base/log.h"
|
#include "../../base/log.h"
|
||||||
#include "audio_resource.h"
|
|
||||||
|
|
||||||
using namespace base;
|
using namespace base;
|
||||||
|
|
||||||
|
|
|
@ -10,8 +10,6 @@ typedef struct _snd_pcm snd_pcm_t;
|
||||||
|
|
||||||
namespace eng {
|
namespace eng {
|
||||||
|
|
||||||
class AudioResource;
|
|
||||||
|
|
||||||
class AudioAlsa : public AudioBase {
|
class AudioAlsa : public AudioBase {
|
||||||
public:
|
public:
|
||||||
AudioAlsa();
|
AudioAlsa();
|
||||||
|
|
|
@ -16,50 +16,161 @@ AudioBase::AudioBase()
|
||||||
|
|
||||||
AudioBase::~AudioBase() = default;
|
AudioBase::~AudioBase() = default;
|
||||||
|
|
||||||
void AudioBase::Play(std::shared_ptr<AudioSample> sample) {
|
size_t AudioBase::CreateResource() {
|
||||||
if (audio_enabled_) {
|
size_t resource_id = ++last_resource_id_;
|
||||||
std::lock_guard<std::mutex> scoped_lock(lock_);
|
resources_[resource_id] = std::make_shared<Resource>();
|
||||||
samples_[0].push_back(sample);
|
return resource_id;
|
||||||
} else if ((sample->flags.load(std::memory_order_relaxed) &
|
}
|
||||||
AudioSample::kStopped) == 0) {
|
|
||||||
sample->active = false;
|
void AudioBase::DestroyResource(size_t resource_id) {
|
||||||
|
auto it = resources_.find(resource_id);
|
||||||
|
if (it == resources_.end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
it->second->flags.fetch_or(kStopped, std::memory_order_relaxed);
|
||||||
|
resources_.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioBase::Play(size_t resource_id,
|
||||||
|
std::shared_ptr<Sound> sound,
|
||||||
|
float amplitude,
|
||||||
|
bool reset_pos) {
|
||||||
|
if (!audio_enabled_)
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto it = resources_.find(resource_id);
|
||||||
|
if (it == resources_.end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (it->second->active) {
|
||||||
|
if (reset_pos)
|
||||||
|
it->second->flags.fetch_or(kStopped, std::memory_order_relaxed);
|
||||||
|
|
||||||
|
if (it->second->flags.load(std::memory_order_relaxed) & kStopped) {
|
||||||
|
Closure ocb = std::move(it->second->end_cb);
|
||||||
|
SetEndCallback(
|
||||||
|
resource_id,
|
||||||
|
[&, resource_id, sound, amplitude, reset_pos, ocb]() -> void {
|
||||||
|
Play(resource_id, sound, amplitude, reset_pos);
|
||||||
|
SetEndCallback(resource_id, std::move(ocb));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (reset_pos) {
|
||||||
|
it->second->src_index = 0;
|
||||||
|
it->second->accumulator = 0;
|
||||||
|
sound->ResetStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
it->second->active = true;
|
||||||
|
it->second->flags.fetch_and(~kStopped, std::memory_order_relaxed);
|
||||||
|
it->second->sound = sound;
|
||||||
|
if (amplitude >= 0)
|
||||||
|
it->second->amplitude = amplitude;
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> scoped_lock(lock_);
|
||||||
|
play_list_[0].push_back(it->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioBase::Stop(size_t resource_id) {
|
||||||
|
auto it = resources_.find(resource_id);
|
||||||
|
if (it == resources_.end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (it->second->active)
|
||||||
|
it->second->flags.fetch_or(kStopped, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioBase::SetLoop(size_t resource_id, bool loop) {
|
||||||
|
auto it = resources_.find(resource_id);
|
||||||
|
if (it == resources_.end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (loop)
|
||||||
|
it->second->flags.fetch_or(kLoop, std::memory_order_relaxed);
|
||||||
|
else
|
||||||
|
it->second->flags.fetch_and(~kLoop, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioBase::SetSimulateStereo(size_t resource_id, bool simulate) {
|
||||||
|
auto it = resources_.find(resource_id);
|
||||||
|
if (it == resources_.end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (simulate)
|
||||||
|
it->second->flags.fetch_or(kSimulateStereo, std::memory_order_relaxed);
|
||||||
|
else
|
||||||
|
it->second->flags.fetch_and(~kSimulateStereo, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioBase::SetResampleStep(size_t resource_id, size_t step) {
|
||||||
|
auto it = resources_.find(resource_id);
|
||||||
|
if (it == resources_.end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
it->second->step.store(step + 100, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioBase::SetMaxAmplitude(size_t resource_id, float max_amplitude) {
|
||||||
|
auto it = resources_.find(resource_id);
|
||||||
|
if (it == resources_.end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
it->second->max_amplitude.store(max_amplitude, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioBase::SetAmplitudeInc(size_t resource_id, float amplitude_inc) {
|
||||||
|
auto it = resources_.find(resource_id);
|
||||||
|
if (it == resources_.end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
it->second->amplitude_inc.store(amplitude_inc, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioBase::SetEndCallback(size_t resource_id, base::Closure cb) {
|
||||||
|
auto it = resources_.find(resource_id);
|
||||||
|
if (it == resources_.end())
|
||||||
|
return;
|
||||||
|
|
||||||
|
it->second->end_cb = std::move(cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioBase::RenderAudio(float* output_buffer, size_t num_frames) {
|
void AudioBase::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)
|
||||||
samples_[1].splice(samples_[1].end(), samples_[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 = samples_[1].begin(); it != samples_[1].end();) {
|
for (auto it = play_list_[1].begin(); it != play_list_[1].end();) {
|
||||||
AudioSample* sample = it->get();
|
auto sound = it->get()->sound.get();
|
||||||
|
unsigned flags = it->get()->flags.load(std::memory_order_relaxed);
|
||||||
|
bool marked_for_removal = false;
|
||||||
|
|
||||||
auto sound = sample->sound.get();
|
if (flags & kStopped) {
|
||||||
unsigned flags = sample->flags.load(std::memory_order_relaxed);
|
marked_for_removal = true;
|
||||||
|
} else {
|
||||||
if (flags & AudioSample::kStopped) {
|
|
||||||
sample->marked_for_removal = true;
|
|
||||||
} else if (!sample->marked_for_removal) {
|
|
||||||
const float* src[2] = {sound->GetBuffer(0), sound->GetBuffer(1)};
|
const float* src[2] = {sound->GetBuffer(0), sound->GetBuffer(1)};
|
||||||
if (!src[1])
|
if (!src[1])
|
||||||
src[1] = src[0]; // mono.
|
src[1] = src[0]; // mono.
|
||||||
|
|
||||||
size_t num_samples = sound->GetNumSamples();
|
size_t num_samples = sound->GetNumSamples();
|
||||||
size_t src_index = sample->src_index;
|
size_t src_index = it->get()->src_index;
|
||||||
size_t step = sample->step.load(std::memory_order_relaxed);
|
size_t step = it->get()->step.load(std::memory_order_relaxed);
|
||||||
size_t accumulator = sample->accumulator;
|
size_t accumulator = it->get()->accumulator;
|
||||||
float amplitude = sample->amplitude;
|
float amplitude = it->get()->amplitude;
|
||||||
float amplitude_inc =
|
float amplitude_inc =
|
||||||
sample->amplitude_inc.load(std::memory_order_relaxed);
|
it->get()->amplitude_inc.load(std::memory_order_relaxed);
|
||||||
float max_amplitude =
|
float max_amplitude =
|
||||||
sample->max_amplitude.load(std::memory_order_relaxed);
|
it->get()->max_amplitude.load(std::memory_order_relaxed);
|
||||||
|
|
||||||
size_t channel_offset =
|
size_t channel_offset =
|
||||||
(flags & AudioSample::kSimulateStereo) && !sound->is_streaming_sound()
|
(flags & kSimulateStereo) && !sound->is_streaming_sound()
|
||||||
? sound->sample_rate() / 10
|
? sound->sample_rate() / 10
|
||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
|
@ -74,7 +185,7 @@ void AudioBase::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 & AudioSample::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++;
|
||||||
|
@ -82,39 +193,40 @@ void AudioBase::RenderAudio(float* output_buffer, size_t num_frames) {
|
||||||
// Apply amplitude modification.
|
// Apply amplitude modification.
|
||||||
amplitude += amplitude_inc;
|
amplitude += amplitude_inc;
|
||||||
if (amplitude <= 0) {
|
if (amplitude <= 0) {
|
||||||
sample->marked_for_removal = true;
|
marked_for_removal = true;
|
||||||
break;
|
break;
|
||||||
} else if (amplitude > max_amplitude) {
|
} else if (amplitude > max_amplitude) {
|
||||||
amplitude = max_amplitude;
|
amplitude = max_amplitude;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Basic resampling for variations.
|
// Advance source index. Apply basic resampling for variations.
|
||||||
accumulator += step;
|
accumulator += step;
|
||||||
src_index += accumulator / 100;
|
src_index += accumulator / 100;
|
||||||
accumulator %= 100;
|
accumulator %= 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Advance source index.
|
// Remove, loop or stream if the source data is consumed
|
||||||
if (src_index >= num_samples) {
|
if (src_index >= num_samples) {
|
||||||
if (!sound->is_streaming_sound()) {
|
if (!sound->is_streaming_sound()) {
|
||||||
src_index %= num_samples;
|
src_index %= num_samples;
|
||||||
|
|
||||||
if (!(flags & AudioSample::kLoop)) {
|
if (!(flags & kLoop)) {
|
||||||
sample->marked_for_removal = true;
|
marked_for_removal = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else if (!sample->streaming_in_progress.load(
|
} else if (!it->get()->streaming_in_progress.load(
|
||||||
std::memory_order_acquire)) {
|
std::memory_order_acquire)) {
|
||||||
if (num_samples)
|
if (num_samples)
|
||||||
src_index %= num_samples;
|
src_index %= num_samples;
|
||||||
|
|
||||||
|
// Looping streaming sounds never return eof.
|
||||||
if (sound->eof()) {
|
if (sound->eof()) {
|
||||||
sample->marked_for_removal = true;
|
marked_for_removal = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
sample->streaming_in_progress.store(true,
|
it->get()->streaming_in_progress.store(true,
|
||||||
std::memory_order_relaxed);
|
std::memory_order_relaxed);
|
||||||
|
|
||||||
// Swap buffers and start streaming in background.
|
// Swap buffers and start streaming in background.
|
||||||
sound->SwapBuffers();
|
sound->SwapBuffers();
|
||||||
|
@ -124,9 +236,8 @@ void AudioBase::RenderAudio(float* output_buffer, size_t num_frames) {
|
||||||
src[1] = src[0]; // mono.
|
src[1] = src[0]; // mono.
|
||||||
num_samples = sound->GetNumSamples();
|
num_samples = sound->GetNumSamples();
|
||||||
|
|
||||||
Worker::Get().PostTask(HERE,
|
Worker::Get().PostTask(HERE, std::bind(&AudioBase::DoStream, this,
|
||||||
std::bind(&AudioBase::DoStream, this, *it,
|
*it, flags & kLoop));
|
||||||
flags & AudioSample::kLoop));
|
|
||||||
} else if (num_samples) {
|
} else if (num_samples) {
|
||||||
DLOG << "Buffer underrun!";
|
DLOG << "Buffer underrun!";
|
||||||
src_index %= num_samples;
|
src_index %= num_samples;
|
||||||
|
@ -134,37 +245,44 @@ void AudioBase::RenderAudio(float* output_buffer, size_t num_frames) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sample->src_index = src_index;
|
it->get()->src_index = src_index;
|
||||||
sample->accumulator = accumulator;
|
it->get()->accumulator = accumulator;
|
||||||
sample->amplitude = amplitude;
|
it->get()->amplitude = amplitude;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sample->marked_for_removal &&
|
if (marked_for_removal) {
|
||||||
(!sound->is_streaming_sound() ||
|
end_list_.push_back(*it);
|
||||||
!sample->streaming_in_progress.load(std::memory_order_relaxed))) {
|
it = play_list_[1].erase(it);
|
||||||
sample->marked_for_removal = false;
|
} else {
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto it = end_list_.begin(); it != end_list_.end();) {
|
||||||
|
if ((!it->get()->sound->is_streaming_sound() ||
|
||||||
|
!it->get()->streaming_in_progress.load(std::memory_order_relaxed))) {
|
||||||
main_thread_task_runner_->PostTask(
|
main_thread_task_runner_->PostTask(
|
||||||
HERE, std::bind(&AudioBase::EndCallback, this, *it));
|
HERE, std::bind(&AudioBase::EndCallback, this, *it));
|
||||||
it = samples_[1].erase(it);
|
it = end_list_.erase(it);
|
||||||
} else {
|
} else {
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioBase::DoStream(std::shared_ptr<AudioSample> sample, bool loop) {
|
void AudioBase::DoStream(std::shared_ptr<Resource> resource, bool loop) {
|
||||||
sample->sound->Stream(loop);
|
resource->sound->Stream(loop);
|
||||||
|
|
||||||
// Memory barrier to ensure all memory writes become visible to the audio
|
// Memory barrier to ensure all memory writes become visible to the audio
|
||||||
// thread.
|
// thread.
|
||||||
sample->streaming_in_progress.store(false, std::memory_order_release);
|
resource->streaming_in_progress.store(false, std::memory_order_release);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioBase::EndCallback(std::shared_ptr<AudioSample> sample) {
|
void AudioBase::EndCallback(std::shared_ptr<Resource> resource) {
|
||||||
sample->active = false;
|
resource->active = false;
|
||||||
|
|
||||||
if (sample->end_cb)
|
if (resource->end_cb)
|
||||||
sample->end_cb();
|
resource->end_cb();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace eng
|
} // namespace eng
|
||||||
|
|
|
@ -4,9 +4,9 @@
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "../../base/closure.h"
|
#include "../../base/closure.h"
|
||||||
#include "audio_sample.h"
|
|
||||||
|
|
||||||
namespace base {
|
namespace base {
|
||||||
class TaskRunner;
|
class TaskRunner;
|
||||||
|
@ -16,9 +16,26 @@ namespace eng {
|
||||||
|
|
||||||
class Sound;
|
class Sound;
|
||||||
|
|
||||||
|
// Class representing the end-point for rendered audio. A platform specific
|
||||||
|
// implementation is expected to periodically call RenderAudio() in a background
|
||||||
|
// thread.
|
||||||
class AudioBase {
|
class AudioBase {
|
||||||
public:
|
public:
|
||||||
void Play(std::shared_ptr<AudioSample> sample);
|
size_t CreateResource();
|
||||||
|
void DestroyResource(size_t resource_id);
|
||||||
|
|
||||||
|
void Play(size_t resource_id,
|
||||||
|
std::shared_ptr<Sound> sound,
|
||||||
|
float amplitude,
|
||||||
|
bool reset_pos);
|
||||||
|
void Stop(size_t resource_id);
|
||||||
|
|
||||||
|
void SetLoop(size_t resource_id, bool loop);
|
||||||
|
void SetSimulateStereo(size_t resource_id, bool simulate);
|
||||||
|
void SetResampleStep(size_t resource_id, size_t step);
|
||||||
|
void SetMaxAmplitude(size_t resource_id, float max_amplitude);
|
||||||
|
void SetAmplitudeInc(size_t resource_id, float amplitude_inc);
|
||||||
|
void SetEndCallback(size_t resource_id, base::Closure cb);
|
||||||
|
|
||||||
void SetEnableAudio(bool enable) { audio_enabled_ = enable; }
|
void SetEnableAudio(bool enable) { audio_enabled_ = enable; }
|
||||||
|
|
||||||
|
@ -31,16 +48,44 @@ class AudioBase {
|
||||||
void RenderAudio(float* output_buffer, size_t num_frames);
|
void RenderAudio(float* output_buffer, size_t num_frames);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::list<std::shared_ptr<AudioSample>> samples_[2];
|
enum SampleFlags { kLoop = 1, kStopped = 2, kSimulateStereo = 4 };
|
||||||
|
|
||||||
|
struct Resource {
|
||||||
|
// Accessed by main thread only.
|
||||||
|
bool active = false;
|
||||||
|
base::Closure end_cb;
|
||||||
|
|
||||||
|
// Initialized by main thread, used by audio thread.
|
||||||
|
std::shared_ptr<Sound> sound;
|
||||||
|
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};
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unordered_map<size_t, std::shared_ptr<Resource>> resources_;
|
||||||
|
size_t last_resource_id_ = 0;
|
||||||
|
|
||||||
|
std::list<std::shared_ptr<Resource>> play_list_[2];
|
||||||
std::mutex lock_;
|
std::mutex lock_;
|
||||||
|
|
||||||
|
std::list<std::shared_ptr<Resource>> end_list_;
|
||||||
|
|
||||||
base::TaskRunner* main_thread_task_runner_;
|
base::TaskRunner* main_thread_task_runner_;
|
||||||
|
|
||||||
bool audio_enabled_ = true;
|
bool audio_enabled_ = true;
|
||||||
|
|
||||||
void DoStream(std::shared_ptr<AudioSample> sample, bool loop);
|
void DoStream(std::shared_ptr<Resource> sample, bool loop);
|
||||||
|
|
||||||
void EndCallback(std::shared_ptr<AudioSample> sample);
|
void EndCallback(std::shared_ptr<Resource> sample);
|
||||||
|
|
||||||
AudioBase(const AudioBase&) = delete;
|
AudioBase(const AudioBase&) = delete;
|
||||||
AudioBase& operator=(const AudioBase&) = delete;
|
AudioBase& operator=(const AudioBase&) = delete;
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
#include "../../base/log.h"
|
#include "../../base/log.h"
|
||||||
#include "../../third_party/oboe/include/oboe/Oboe.h"
|
#include "../../third_party/oboe/include/oboe/Oboe.h"
|
||||||
#include "audio_resource.h"
|
|
||||||
|
|
||||||
using namespace base;
|
using namespace base;
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,6 @@
|
||||||
|
|
||||||
namespace eng {
|
namespace eng {
|
||||||
|
|
||||||
class AudioResource;
|
|
||||||
|
|
||||||
class AudioOboe : public AudioBase {
|
class AudioOboe : public AudioBase {
|
||||||
public:
|
public:
|
||||||
AudioOboe();
|
AudioOboe();
|
||||||
|
|
|
@ -1,92 +0,0 @@
|
||||||
#include "audio_resource.h"
|
|
||||||
|
|
||||||
#include "../../base/log.h"
|
|
||||||
#include "../sound.h"
|
|
||||||
#include "audio.h"
|
|
||||||
#include "audio_sample.h"
|
|
||||||
|
|
||||||
using namespace base;
|
|
||||||
|
|
||||||
namespace eng {
|
|
||||||
|
|
||||||
AudioResource::AudioResource(Audio* audio)
|
|
||||||
: sample_(std::make_shared<AudioSample>()), audio_(audio) {}
|
|
||||||
|
|
||||||
AudioResource::~AudioResource() {
|
|
||||||
sample_->flags.fetch_or(AudioSample::kStopped, std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioResource::Play(std::shared_ptr<Sound> sound,
|
|
||||||
float amplitude,
|
|
||||||
bool reset_pos) {
|
|
||||||
AudioSample* sample = sample_.get();
|
|
||||||
|
|
||||||
if (sample->active) {
|
|
||||||
if (reset_pos)
|
|
||||||
sample_->flags.fetch_or(AudioSample::kStopped, std::memory_order_relaxed);
|
|
||||||
|
|
||||||
if (reset_pos ||
|
|
||||||
sample->flags.load(std::memory_order_relaxed) & AudioSample::kStopped) {
|
|
||||||
Closure ocb = sample_->end_cb;
|
|
||||||
SetEndCallback([&, sound, amplitude, reset_pos, ocb]() -> void {
|
|
||||||
Play(sound, amplitude, reset_pos);
|
|
||||||
SetEndCallback(ocb);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reset_pos) {
|
|
||||||
sample->src_index = 0;
|
|
||||||
sample->accumulator = 0;
|
|
||||||
sound->ResetStream();
|
|
||||||
}
|
|
||||||
|
|
||||||
sample->active = true;
|
|
||||||
sample_->flags.fetch_and(~AudioSample::kStopped, std::memory_order_relaxed);
|
|
||||||
sample->sound = sound;
|
|
||||||
if (amplitude >= 0)
|
|
||||||
sample->amplitude = amplitude;
|
|
||||||
|
|
||||||
audio_->Play(sample_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioResource::Stop() {
|
|
||||||
if (sample_->active)
|
|
||||||
sample_->flags.fetch_or(AudioSample::kStopped, std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioResource::SetLoop(bool loop) {
|
|
||||||
if (loop)
|
|
||||||
sample_->flags.fetch_or(AudioSample::kLoop, std::memory_order_relaxed);
|
|
||||||
else
|
|
||||||
sample_->flags.fetch_and(~AudioSample::kLoop, std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioResource::SetSimulateStereo(bool simulate) {
|
|
||||||
if (simulate)
|
|
||||||
sample_->flags.fetch_or(AudioSample::kSimulateStereo,
|
|
||||||
std::memory_order_relaxed);
|
|
||||||
else
|
|
||||||
sample_->flags.fetch_and(~AudioSample::kSimulateStereo,
|
|
||||||
std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioResource::SetResampleStep(size_t step) {
|
|
||||||
sample_->step.store(step + 100, std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioResource::SetMaxAmplitude(float max_amplitude) {
|
|
||||||
sample_->max_amplitude.store(max_amplitude, std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioResource::SetAmplitudeInc(float amplitude_inc) {
|
|
||||||
sample_->amplitude_inc.store(amplitude_inc, std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioResource::SetEndCallback(base::Closure cb) {
|
|
||||||
sample_->end_cb = cb;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace eng
|
|
|
@ -1,41 +0,0 @@
|
||||||
#ifndef AUDIO_RESOURCE_H
|
|
||||||
#define AUDIO_RESOURCE_H
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include "../../base/closure.h"
|
|
||||||
#include "audio_forward.h"
|
|
||||||
|
|
||||||
namespace eng {
|
|
||||||
|
|
||||||
struct AudioSample;
|
|
||||||
class Sound;
|
|
||||||
|
|
||||||
class AudioResource {
|
|
||||||
public:
|
|
||||||
AudioResource(Audio* audio);
|
|
||||||
~AudioResource();
|
|
||||||
|
|
||||||
void Play(std::shared_ptr<Sound> sound, float amplitude, bool reset_pos);
|
|
||||||
|
|
||||||
void Stop();
|
|
||||||
|
|
||||||
void SetLoop(bool loop);
|
|
||||||
void SetSimulateStereo(bool simulate);
|
|
||||||
void SetResampleStep(size_t step);
|
|
||||||
void SetMaxAmplitude(float max_amplitude);
|
|
||||||
void SetAmplitudeInc(float amplitude_inc);
|
|
||||||
void SetEndCallback(base::Closure cb);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::shared_ptr<AudioSample> sample_;
|
|
||||||
|
|
||||||
Audio* audio_ = nullptr;
|
|
||||||
|
|
||||||
AudioResource(const AudioResource&) = delete;
|
|
||||||
AudioResource& operator=(const AudioResource&) = delete;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace eng
|
|
||||||
|
|
||||||
#endif // AUDIO_RESOURCE_H
|
|
|
@ -1,41 +0,0 @@
|
||||||
#ifndef AUDIO_SAMPLE_H
|
|
||||||
#define AUDIO_SAMPLE_H
|
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include "../../base/closure.h"
|
|
||||||
|
|
||||||
namespace eng {
|
|
||||||
|
|
||||||
class Sound;
|
|
||||||
|
|
||||||
struct AudioSample {
|
|
||||||
enum SampleFlags { kLoop = 1, kStopped = 2, kSimulateStereo = 4 };
|
|
||||||
|
|
||||||
// Accessed by main thread only.
|
|
||||||
bool active = false;
|
|
||||||
base::Closure end_cb;
|
|
||||||
|
|
||||||
// Accessed by audio thread only.
|
|
||||||
bool marked_for_removal = false;
|
|
||||||
|
|
||||||
// Initialized by main thread, used by audio thread.
|
|
||||||
std::shared_ptr<Sound> sound;
|
|
||||||
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};
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace eng
|
|
||||||
|
|
||||||
#endif // AUDIO_SAMPLE_H
|
|
|
@ -4,7 +4,6 @@
|
||||||
#include "../third_party/texture_compressor/texture_compressor.h"
|
#include "../third_party/texture_compressor/texture_compressor.h"
|
||||||
#include "animator.h"
|
#include "animator.h"
|
||||||
#include "audio/audio.h"
|
#include "audio/audio.h"
|
||||||
#include "audio/audio_resource.h"
|
|
||||||
#include "drawable.h"
|
#include "drawable.h"
|
||||||
#include "font.h"
|
#include "font.h"
|
||||||
#include "game.h"
|
#include "game.h"
|
||||||
|
@ -274,10 +273,6 @@ std::shared_ptr<Texture> Engine::GetTexture(const std::string& asset_name) {
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<AudioResource> Engine::CreateAudioResource() {
|
|
||||||
return std::make_unique<AudioResource>(audio_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Engine::AddInputEvent(std::unique_ptr<InputEvent> event) {
|
void Engine::AddInputEvent(std::unique_ptr<InputEvent> event) {
|
||||||
if (replaying_)
|
if (replaying_)
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -19,7 +19,6 @@ class TextureCompressor;
|
||||||
namespace eng {
|
namespace eng {
|
||||||
|
|
||||||
class Animator;
|
class Animator;
|
||||||
class AudioResource;
|
|
||||||
class Font;
|
class Font;
|
||||||
class Game;
|
class Game;
|
||||||
class Drawable;
|
class Drawable;
|
||||||
|
@ -83,8 +82,6 @@ class Engine {
|
||||||
|
|
||||||
std::shared_ptr<Texture> GetTexture(const std::string& asset_name);
|
std::shared_ptr<Texture> GetTexture(const std::string& asset_name);
|
||||||
|
|
||||||
std::unique_ptr<AudioResource> CreateAudioResource();
|
|
||||||
|
|
||||||
void AddInputEvent(std::unique_ptr<InputEvent> event);
|
void AddInputEvent(std::unique_ptr<InputEvent> event);
|
||||||
std::unique_ptr<InputEvent> GetNextInputEvent();
|
std::unique_ptr<InputEvent> GetNextInputEvent();
|
||||||
|
|
||||||
|
@ -108,6 +105,8 @@ class Engine {
|
||||||
|
|
||||||
void SetEnableVibration(bool enable) { vibration_enabled_ = enable; }
|
void SetEnableVibration(bool enable) { vibration_enabled_ = enable; }
|
||||||
|
|
||||||
|
Audio* GetAudio() { return audio_; }
|
||||||
|
|
||||||
// Access to the render resources.
|
// Access to the render resources.
|
||||||
Geometry* GetQuad() { return quad_.get(); }
|
Geometry* GetQuad() { return quad_.get(); }
|
||||||
Shader* GetPassThroughShader() { return pass_through_shader_.get(); }
|
Shader* GetPassThroughShader() { return pass_through_shader_.get(); }
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
#include "../base/interpolation.h"
|
#include "../base/interpolation.h"
|
||||||
#include "../base/log.h"
|
#include "../base/log.h"
|
||||||
#include "audio/audio_resource.h"
|
#include "audio/audio.h"
|
||||||
#include "engine.h"
|
#include "engine.h"
|
||||||
#include "sound.h"
|
#include "sound.h"
|
||||||
|
|
||||||
|
@ -10,9 +10,12 @@ using namespace base;
|
||||||
|
|
||||||
namespace eng {
|
namespace eng {
|
||||||
|
|
||||||
SoundPlayer::SoundPlayer() : resource_(Engine::Get().CreateAudioResource()) {}
|
SoundPlayer::SoundPlayer()
|
||||||
|
: resource_id_(Engine::Get().GetAudio()->CreateResource()) {}
|
||||||
|
|
||||||
SoundPlayer::~SoundPlayer() = default;
|
SoundPlayer::~SoundPlayer() {
|
||||||
|
Engine::Get().GetAudio()->DestroyResource(resource_id_);
|
||||||
|
}
|
||||||
|
|
||||||
void SoundPlayer::SetSound(std::shared_ptr<Sound> sound) {
|
void SoundPlayer::SetSound(std::shared_ptr<Sound> sound) {
|
||||||
CHECK(!sound->is_streaming_sound()) << "Streaming sound cannot be shared.";
|
CHECK(!sound->is_streaming_sound()) << "Streaming sound cannot be shared.";
|
||||||
|
@ -29,14 +32,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;
|
||||||
resource_->SetResampleStep(step * 12);
|
Engine::Get().GetAudio()->SetResampleStep(resource_id_, step * 12);
|
||||||
resource_->SetLoop(loop);
|
Engine::Get().GetAudio()->SetLoop(resource_id_, loop);
|
||||||
if (fade_in_duration > 0)
|
if (fade_in_duration > 0)
|
||||||
resource_->SetAmplitudeInc(1.0f /
|
Engine::Get().GetAudio()->SetAmplitudeInc(
|
||||||
(sound_->sample_rate() * fade_in_duration));
|
resource_id_, 1.0f / (sound_->sample_rate() * fade_in_duration));
|
||||||
else
|
else
|
||||||
resource_->SetAmplitudeInc(0);
|
Engine::Get().GetAudio()->SetAmplitudeInc(resource_id_, 0);
|
||||||
resource_->Play(sound_, fade_in_duration > 0 ? 0 : max_amplitude_, true);
|
Engine::Get().GetAudio()->Play(
|
||||||
|
resource_id_, sound_, fade_in_duration > 0 ? 0 : max_amplitude_, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoundPlayer::Resume(float fade_in_duration) {
|
void SoundPlayer::Resume(float fade_in_duration) {
|
||||||
|
@ -44,9 +48,10 @@ void SoundPlayer::Resume(float fade_in_duration) {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (fade_in_duration > 0)
|
if (fade_in_duration > 0)
|
||||||
resource_->SetAmplitudeInc(1.0f /
|
Engine::Get().GetAudio()->SetAmplitudeInc(
|
||||||
(sound_->sample_rate() * fade_in_duration));
|
resource_id_, 1.0f / (sound_->sample_rate() * fade_in_duration));
|
||||||
resource_->Play(sound_, fade_in_duration > 0 ? 0 : -1, false);
|
Engine::Get().GetAudio()->Play(resource_id_, sound_,
|
||||||
|
fade_in_duration > 0 ? 0 : -1, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoundPlayer::Stop(float fade_out_duration) {
|
void SoundPlayer::Stop(float fade_out_duration) {
|
||||||
|
@ -54,10 +59,10 @@ void SoundPlayer::Stop(float fade_out_duration) {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (fade_out_duration > 0)
|
if (fade_out_duration > 0)
|
||||||
resource_->SetAmplitudeInc(-1.0f /
|
Engine::Get().GetAudio()->SetAmplitudeInc(
|
||||||
(sound_->sample_rate() * fade_out_duration));
|
resource_id_, -1.0f / (sound_->sample_rate() * fade_out_duration));
|
||||||
else
|
else
|
||||||
resource_->Stop();
|
Engine::Get().GetAudio()->Stop(resource_id_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoundPlayer::SetVariate(bool variate) {
|
void SoundPlayer::SetVariate(bool variate) {
|
||||||
|
@ -65,16 +70,16 @@ void SoundPlayer::SetVariate(bool variate) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoundPlayer::SetSimulateStereo(bool simulate) {
|
void SoundPlayer::SetSimulateStereo(bool simulate) {
|
||||||
resource_->SetSimulateStereo(simulate);
|
Engine::Get().GetAudio()->SetSimulateStereo(resource_id_, simulate);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoundPlayer::SetMaxAplitude(float max_amplitude) {
|
void SoundPlayer::SetMaxAplitude(float max_amplitude) {
|
||||||
max_amplitude_ = max_amplitude;
|
max_amplitude_ = max_amplitude;
|
||||||
resource_->SetMaxAmplitude(max_amplitude);
|
Engine::Get().GetAudio()->SetMaxAmplitude(resource_id_, max_amplitude);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoundPlayer::SetEndCallback(base::Closure cb) {
|
void SoundPlayer::SetEndCallback(base::Closure cb) {
|
||||||
resource_->SetEndCallback(cb);
|
Engine::Get().GetAudio()->SetEndCallback(resource_id_, cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace eng
|
} // namespace eng
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
|
|
||||||
namespace eng {
|
namespace eng {
|
||||||
|
|
||||||
class AudioResource;
|
|
||||||
class Sound;
|
class Sound;
|
||||||
|
|
||||||
class SoundPlayer {
|
class SoundPlayer {
|
||||||
|
@ -37,7 +36,7 @@ class SoundPlayer {
|
||||||
void SetEndCallback(base::Closure cb);
|
void SetEndCallback(base::Closure cb);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<AudioResource> resource_;
|
size_t resource_id_ = 0;
|
||||||
std::shared_ptr<Sound> sound_;
|
std::shared_ptr<Sound> sound_;
|
||||||
|
|
||||||
float max_amplitude_ = 1.0f;
|
float max_amplitude_ = 1.0f;
|
||||||
|
|
Loading…
Reference in New Issue