mirror of https://github.com/auygun/kaliber.git
Refactoring audio code
This commit is contained in:
parent
be1d562a07
commit
c90d039944
|
@ -62,8 +62,8 @@ add_library(kaliber SHARED
|
|||
../../../src/demo/sky_quad.cc
|
||||
../../../src/engine/animatable.cc
|
||||
../../../src/engine/animator.cc
|
||||
../../../src/engine/audio/audio_base.cc
|
||||
../../../src/engine/audio/audio_oboe.cc
|
||||
../../../src/engine/audio/audio_driver_oboe.cc
|
||||
../../../src/engine/audio/audio_mixer.cc
|
||||
../../../src/engine/drawable.cc
|
||||
../../../src/engine/engine.cc
|
||||
../../../src/engine/font.cc
|
||||
|
|
|
@ -95,8 +95,8 @@ $(BASE_LIB): $(BASE_OBJS)
|
|||
ENGINE_SRC := \
|
||||
$(SRC_ROOT)/engine/animatable.cc \
|
||||
$(SRC_ROOT)/engine/animator.cc \
|
||||
$(SRC_ROOT)/engine/audio/audio_alsa.cc \
|
||||
$(SRC_ROOT)/engine/audio/audio_base.cc \
|
||||
$(SRC_ROOT)/engine/audio/audio_driver_alsa.cc \
|
||||
$(SRC_ROOT)/engine/audio/audio_mixer.cc \
|
||||
$(SRC_ROOT)/engine/drawable.cc \
|
||||
$(SRC_ROOT)/engine/engine.cc \
|
||||
$(SRC_ROOT)/engine/font.cc \
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
#ifndef ENGINE_AUDIO_AUDIO_H
|
||||
#define ENGINE_AUDIO_AUDIO_H
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#include "engine/audio/audio_oboe.h"
|
||||
#elif defined(__linux__)
|
||||
#include "engine/audio/audio_alsa.h"
|
||||
#endif
|
||||
|
||||
namespace eng {
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
using Audio = AudioOboe;
|
||||
#elif defined(__linux__)
|
||||
using Audio = AudioAlsa;
|
||||
#endif
|
||||
|
||||
} // namespace eng
|
||||
|
||||
#endif // ENGINE_AUDIO_AUDIO_H
|
|
@ -1,47 +0,0 @@
|
|||
#ifndef ENGINE_AUDIO_AUDIO_ALSA_H
|
||||
#define ENGINE_AUDIO_AUDIO_ALSA_H
|
||||
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
|
||||
#include "engine/audio/audio_base.h"
|
||||
|
||||
typedef struct _snd_pcm snd_pcm_t;
|
||||
|
||||
namespace eng {
|
||||
|
||||
class AudioAlsa : public AudioBase {
|
||||
public:
|
||||
AudioAlsa();
|
||||
~AudioAlsa();
|
||||
|
||||
bool Initialize();
|
||||
|
||||
void Shutdown();
|
||||
|
||||
void Suspend();
|
||||
void Resume();
|
||||
|
||||
int GetHardwareSampleRate();
|
||||
|
||||
private:
|
||||
// Handle for the PCM device.
|
||||
snd_pcm_t* device_;
|
||||
|
||||
std::thread audio_thread_;
|
||||
std::atomic<bool> terminate_audio_thread_{false};
|
||||
std::atomic<bool> suspend_audio_thread_{false};
|
||||
|
||||
size_t num_channels_ = 0;
|
||||
int sample_rate_ = 0;
|
||||
size_t period_size_ = 0;
|
||||
|
||||
void StartAudioThread();
|
||||
void TerminateAudioThread();
|
||||
|
||||
void AudioThreadMain();
|
||||
};
|
||||
|
||||
} // namespace eng
|
||||
|
||||
#endif // ENGINE_AUDIO_AUDIO_ALSA_H
|
|
@ -0,0 +1,35 @@
|
|||
#ifndef ENGINE_AUDIO_AUDIO_DRIVER_H
|
||||
#define ENGINE_AUDIO_AUDIO_DRIVER_H
|
||||
|
||||
#include "engine/audio/audio_driver.h"
|
||||
|
||||
namespace eng {
|
||||
|
||||
class AudioDriverDelegate;
|
||||
|
||||
// Models an audio sink sending mixed audio to the audio driver. Audio data from
|
||||
// the mixer source is delivered on a pull model using AudioDriverDelegate.
|
||||
class AudioDriver {
|
||||
public:
|
||||
AudioDriver() = default;
|
||||
virtual ~AudioDriver() = default;
|
||||
|
||||
virtual void SetDelegate(AudioDriverDelegate* delegate) = 0;
|
||||
|
||||
virtual bool Initialize() = 0;
|
||||
|
||||
virtual void Shutdown() = 0;
|
||||
|
||||
virtual void Suspend() = 0;
|
||||
virtual void Resume() = 0;
|
||||
|
||||
virtual int GetHardwareSampleRate() = 0;
|
||||
|
||||
private:
|
||||
AudioDriver(const AudioDriver&) = delete;
|
||||
AudioDriver& operator=(const AudioDriver&) = delete;
|
||||
};
|
||||
|
||||
} // namespace eng
|
||||
|
||||
#endif // ENGINE_AUDIO_AUDIO_DRIVER_H
|
|
@ -1,20 +1,26 @@
|
|||
#include "engine/audio/audio_alsa.h"
|
||||
#include "engine/audio/audio_driver_alsa.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <alsa/asoundlib.h>
|
||||
|
||||
#include "base/log.h"
|
||||
#include "engine/audio/audio_driver_delegate.h"
|
||||
|
||||
using namespace base;
|
||||
|
||||
namespace eng {
|
||||
|
||||
AudioAlsa::AudioAlsa() = default;
|
||||
AudioDriverAlsa::AudioDriverAlsa() = default;
|
||||
|
||||
AudioAlsa::~AudioAlsa() = default;
|
||||
AudioDriverAlsa::~AudioDriverAlsa() = default;
|
||||
|
||||
bool AudioAlsa::Initialize() {
|
||||
void AudioDriverAlsa::SetDelegate(AudioDriverDelegate* delegate) {
|
||||
delegate_ = delegate;
|
||||
Resume();
|
||||
}
|
||||
|
||||
bool AudioDriverAlsa::Initialize() {
|
||||
LOG << "Initializing audio system.";
|
||||
|
||||
int err;
|
||||
|
@ -136,51 +142,43 @@ bool AudioAlsa::Initialize() {
|
|||
return false;
|
||||
}
|
||||
|
||||
void AudioAlsa::Shutdown() {
|
||||
void AudioDriverAlsa::Shutdown() {
|
||||
LOG << "Shutting down audio system.";
|
||||
|
||||
TerminateAudioThread();
|
||||
snd_pcm_drop(device_);
|
||||
snd_pcm_close(device_);
|
||||
}
|
||||
|
||||
void AudioAlsa::Suspend() {
|
||||
DCHECK(!terminate_audio_thread_.load(std::memory_order_relaxed));
|
||||
|
||||
void AudioDriverAlsa::Suspend() {
|
||||
suspend_audio_thread_.store(true, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void AudioAlsa::Resume() {
|
||||
DCHECK(!terminate_audio_thread_.load(std::memory_order_relaxed));
|
||||
|
||||
void AudioDriverAlsa::Resume() {
|
||||
suspend_audio_thread_.store(false, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
int AudioAlsa::GetHardwareSampleRate() {
|
||||
int AudioDriverAlsa::GetHardwareSampleRate() {
|
||||
return sample_rate_;
|
||||
}
|
||||
|
||||
void AudioAlsa::StartAudioThread() {
|
||||
void AudioDriverAlsa::StartAudioThread() {
|
||||
LOG << "Starting audio thread.";
|
||||
|
||||
DCHECK(!terminate_audio_thread_.load(std::memory_order_relaxed));
|
||||
|
||||
audio_thread_ = std::thread(&AudioAlsa::AudioThreadMain, this);
|
||||
terminate_audio_thread_.store(false, std::memory_order_relaxed);
|
||||
suspend_audio_thread_.store(true, std::memory_order_relaxed);
|
||||
audio_thread_ = std::thread(&AudioDriverAlsa::AudioThreadMain, this);
|
||||
}
|
||||
|
||||
void AudioAlsa::TerminateAudioThread() {
|
||||
void AudioDriverAlsa::TerminateAudioThread() {
|
||||
if (terminate_audio_thread_.load(std::memory_order_relaxed))
|
||||
return;
|
||||
|
||||
LOG << "Terminating audio thread";
|
||||
|
||||
// Notify worker thread and wait for it to terminate.
|
||||
terminate_audio_thread_.store(true, std::memory_order_relaxed);
|
||||
suspend_audio_thread_.store(true, std::memory_order_relaxed);
|
||||
audio_thread_.join();
|
||||
}
|
||||
|
||||
void AudioAlsa::AudioThreadMain() {
|
||||
void AudioDriverAlsa::AudioThreadMain() {
|
||||
size_t num_frames = period_size_ / (num_channels_ * sizeof(float));
|
||||
auto buffer = std::make_unique<float[]>(num_frames * 2);
|
||||
|
||||
|
@ -192,11 +190,11 @@ void AudioAlsa::AudioThreadMain() {
|
|||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
|
||||
RenderAudio(buffer.get(), num_frames);
|
||||
delegate_->RenderAudio(buffer.get(), num_frames);
|
||||
|
||||
while (snd_pcm_writei(device_, buffer.get(), num_frames) < 0) {
|
||||
snd_pcm_prepare(device_);
|
||||
DLOG << "Audio buffer underrun!";
|
||||
DLOG << "Alsa buffer underrun!";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
#ifndef ENGINE_AUDIO_AUDIO_DRIVER_ALSA_H
|
||||
#define ENGINE_AUDIO_AUDIO_DRIVER_ALSA_H
|
||||
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
|
||||
#include "engine/audio/audio_driver.h"
|
||||
|
||||
typedef struct _snd_pcm snd_pcm_t;
|
||||
|
||||
namespace eng {
|
||||
|
||||
class AudioDriverAlsa final : public AudioDriver {
|
||||
public:
|
||||
AudioDriverAlsa();
|
||||
~AudioDriverAlsa() final;
|
||||
|
||||
void SetDelegate(AudioDriverDelegate* delegate) final;
|
||||
|
||||
bool Initialize() final;
|
||||
|
||||
void Shutdown() final;
|
||||
|
||||
void Suspend() final;
|
||||
void Resume() final;
|
||||
|
||||
int GetHardwareSampleRate() final;
|
||||
|
||||
private:
|
||||
// Handle for the PCM device.
|
||||
snd_pcm_t* device_;
|
||||
|
||||
std::thread audio_thread_;
|
||||
std::atomic<bool> terminate_audio_thread_{false};
|
||||
std::atomic<bool> suspend_audio_thread_{false};
|
||||
|
||||
size_t num_channels_ = 0;
|
||||
int sample_rate_ = 0;
|
||||
size_t period_size_ = 0;
|
||||
|
||||
AudioDriverDelegate* delegate_ = nullptr;
|
||||
|
||||
void StartAudioThread();
|
||||
void TerminateAudioThread();
|
||||
|
||||
void AudioThreadMain();
|
||||
};
|
||||
|
||||
} // namespace eng
|
||||
|
||||
#endif // ENGINE_AUDIO_AUDIO_DRIVER_ALSA_H
|
|
@ -0,0 +1,18 @@
|
|||
#ifndef ENGINE_AUDIO_AUDIO_DRIVER_DELEGATE_H
|
||||
#define ENGINE_AUDIO_AUDIO_DRIVER_DELEGATE_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace eng {
|
||||
|
||||
class AudioDriverDelegate {
|
||||
public:
|
||||
AudioDriverDelegate() = default;
|
||||
virtual ~AudioDriverDelegate() = default;
|
||||
|
||||
virtual void RenderAudio(float* output_buffer, size_t num_frames) = 0;
|
||||
};
|
||||
|
||||
} // namespace eng
|
||||
|
||||
#endif // ENGINE_AUDIO_AUDIO_DRIVER_DELEGATE_H
|
|
@ -1,62 +1,70 @@
|
|||
#include "engine/audio/audio_oboe.h"
|
||||
#include "engine/audio/audio_driver_oboe.h"
|
||||
|
||||
#include "base/log.h"
|
||||
#include "engine/audio/audio_driver_delegate.h"
|
||||
#include "third_party/oboe/include/oboe/Oboe.h"
|
||||
|
||||
using namespace base;
|
||||
|
||||
namespace eng {
|
||||
|
||||
AudioOboe::AudioOboe() : callback_(std::make_unique<StreamCallback>(this)) {}
|
||||
AudioDriverOboe::AudioDriverOboe()
|
||||
: callback_(std::make_unique<StreamCallback>(this)) {}
|
||||
|
||||
AudioOboe::~AudioOboe() = default;
|
||||
AudioDriverOboe::~AudioDriverOboe() = default;
|
||||
|
||||
bool AudioOboe::Initialize() {
|
||||
LOG << "Initializing audio system.";
|
||||
|
||||
return RestartStream();
|
||||
void AudioDriverOboe::SetDelegate(AudioDriverDelegate* delegate) {
|
||||
delegate_ = delegate;
|
||||
Resume();
|
||||
}
|
||||
|
||||
void AudioOboe::Shutdown() {
|
||||
bool AudioDriverOboe::Initialize() {
|
||||
LOG << "Initializing audio system.";
|
||||
|
||||
return RestartStream(true);
|
||||
}
|
||||
|
||||
void AudioDriverOboe::Shutdown() {
|
||||
LOG << "Shutting down audio system.";
|
||||
|
||||
stream_->stop();
|
||||
}
|
||||
|
||||
void AudioOboe::Suspend() {
|
||||
void AudioDriverOboe::Suspend() {
|
||||
stream_->stop();
|
||||
}
|
||||
|
||||
void AudioOboe::Resume() {
|
||||
void AudioDriverOboe::Resume() {
|
||||
RestartStream();
|
||||
}
|
||||
|
||||
int AudioOboe::GetHardwareSampleRate() {
|
||||
int AudioDriverOboe::GetHardwareSampleRate() {
|
||||
return stream_->getSampleRate();
|
||||
}
|
||||
|
||||
AudioOboe::StreamCallback::StreamCallback(AudioOboe* audio) : audio_(audio) {}
|
||||
AudioDriverOboe::StreamCallback::StreamCallback(AudioDriverOboe* driver)
|
||||
: driver_(driver) {}
|
||||
|
||||
AudioOboe::StreamCallback::~StreamCallback() = default;
|
||||
AudioDriverOboe::StreamCallback::~StreamCallback() = default;
|
||||
|
||||
oboe::DataCallbackResult AudioOboe::StreamCallback::onAudioReady(
|
||||
oboe::DataCallbackResult AudioDriverOboe::StreamCallback::onAudioReady(
|
||||
oboe::AudioStream* oboe_stream,
|
||||
void* audio_data,
|
||||
int32_t num_frames) {
|
||||
float* output_buffer = static_cast<float*>(audio_data);
|
||||
audio_->RenderAudio(output_buffer, num_frames);
|
||||
driver_->delegate_->RenderAudio(output_buffer, num_frames);
|
||||
return oboe::DataCallbackResult::Continue;
|
||||
}
|
||||
|
||||
void AudioOboe::StreamCallback::onErrorAfterClose(
|
||||
void AudioDriverOboe::StreamCallback::onErrorAfterClose(
|
||||
oboe::AudioStream* oboe_stream,
|
||||
oboe::Result error) {
|
||||
LOG << "Error after close. Error: " << oboe::convertToText(error);
|
||||
|
||||
audio_->RestartStream();
|
||||
driver_->RestartStream();
|
||||
}
|
||||
|
||||
bool AudioOboe::RestartStream() {
|
||||
bool AudioDriverOboe::RestartStream(bool suspended) {
|
||||
oboe::AudioStreamBuilder builder;
|
||||
oboe::Result result =
|
||||
builder.setSharingMode(oboe::SharingMode::Exclusive)
|
||||
|
@ -80,6 +88,7 @@ bool AudioOboe::RestartStream() {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!suspended)
|
||||
stream_->start();
|
||||
return true;
|
||||
}
|
|
@ -1,33 +1,37 @@
|
|||
#ifndef ENGINE_AUDIO_AUDIO_OBOE_H
|
||||
#define ENGINE_AUDIO_AUDIO_OBOE_H
|
||||
#ifndef ENGINE_AUDIO_AUDIO_DRIVER_OBOE_H
|
||||
#define ENGINE_AUDIO_AUDIO_DRIVER_OBOE_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "third_party/oboe/include/oboe/AudioStream.h"
|
||||
#include "third_party/oboe/include/oboe/AudioStreamCallback.h"
|
||||
|
||||
#include "engine/audio/audio_base.h"
|
||||
#include "engine/audio/audio_driver.h"
|
||||
|
||||
namespace eng {
|
||||
|
||||
class AudioOboe : public AudioBase {
|
||||
class AudioDriverOboe final : public AudioDriver {
|
||||
public:
|
||||
AudioOboe();
|
||||
~AudioOboe();
|
||||
AudioDriverOboe();
|
||||
~AudioDriverOboe() final;
|
||||
|
||||
bool Initialize();
|
||||
void SetDelegate(AudioDriverDelegate* delegate) final;
|
||||
|
||||
void Shutdown();
|
||||
bool Initialize() final;
|
||||
|
||||
void Suspend();
|
||||
void Resume();
|
||||
void Shutdown() final;
|
||||
|
||||
int GetHardwareSampleRate();
|
||||
void Suspend() final;
|
||||
void Resume() final;
|
||||
|
||||
int GetHardwareSampleRate() final;
|
||||
|
||||
private:
|
||||
static constexpr int kChannelCount = 2;
|
||||
|
||||
class StreamCallback final : public oboe::AudioStreamCallback {
|
||||
public:
|
||||
StreamCallback(AudioOboe* audio);
|
||||
StreamCallback(AudioDriverOboe* audio);
|
||||
~StreamCallback() final;
|
||||
|
||||
oboe::DataCallbackResult onAudioReady(oboe::AudioStream* oboe_stream,
|
||||
|
@ -38,15 +42,17 @@ class AudioOboe : public AudioBase {
|
|||
oboe::Result error) final;
|
||||
|
||||
private:
|
||||
AudioOboe* audio_;
|
||||
AudioDriverOboe* driver_;
|
||||
};
|
||||
|
||||
oboe::ManagedStream stream_;
|
||||
std::unique_ptr<StreamCallback> callback_;
|
||||
|
||||
bool RestartStream();
|
||||
AudioDriverDelegate* delegate_ = nullptr;
|
||||
|
||||
bool RestartStream(bool suspended = false);
|
||||
};
|
||||
|
||||
} // namespace eng
|
||||
|
||||
#endif // ENGINE_AUDIO_AUDIO_OBOE_H
|
||||
#endif // ENGINE_AUDIO_AUDIO_DRIVER_OBOE_H
|
|
@ -1,16 +0,0 @@
|
|||
#ifndef ENGINE_AUDIO_AUDIO_FORWARD_H
|
||||
#define ENGINE_AUDIO_AUDIO_FORWARD_H
|
||||
|
||||
namespace eng {
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
class AudioOboe;
|
||||
using Audio = AudioOboe;
|
||||
#elif defined(__linux__)
|
||||
class AudioAlsa;
|
||||
using Audio = AudioAlsa;
|
||||
#endif
|
||||
|
||||
} // namespace eng
|
||||
|
||||
#endif // ENGINE_AUDIO_AUDIO_FORWARD_H
|
|
@ -1,4 +1,4 @@
|
|||
#include "engine/audio/audio.h"
|
||||
#include "engine/audio/audio_mixer.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
|
@ -11,27 +11,30 @@ using namespace base;
|
|||
|
||||
namespace eng {
|
||||
|
||||
AudioBase::AudioBase()
|
||||
AudioMixer::AudioMixer()
|
||||
: main_thread_task_runner_(TaskRunner::GetThreadLocalTaskRunner()) {}
|
||||
|
||||
AudioBase::~AudioBase() = default;
|
||||
AudioMixer::~AudioMixer() = default;
|
||||
|
||||
uint64_t AudioBase::CreateResource() {
|
||||
uint64_t AudioMixer::CreateResource() {
|
||||
uint64_t resource_id = ++last_resource_id_;
|
||||
resources_[resource_id] = std::make_shared<Resource>();
|
||||
return resource_id;
|
||||
}
|
||||
|
||||
void AudioBase::DestroyResource(uint64_t resource_id) {
|
||||
void AudioMixer::DestroyResource(uint64_t resource_id) {
|
||||
auto it = resources_.find(resource_id);
|
||||
if (it == resources_.end())
|
||||
return;
|
||||
|
||||
if (it->second->active) {
|
||||
it->second->restart_cb = nullptr;
|
||||
it->second->flags.fetch_or(kStopped, std::memory_order_relaxed);
|
||||
}
|
||||
resources_.erase(it);
|
||||
}
|
||||
|
||||
void AudioBase::Play(uint64_t resource_id,
|
||||
void AudioMixer::Play(uint64_t resource_id,
|
||||
std::shared_ptr<Sound> sound,
|
||||
float amplitude,
|
||||
bool reset_pos) {
|
||||
|
@ -46,15 +49,11 @@ void AudioBase::Play(uint64_t resource_id,
|
|||
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 {
|
||||
if (it->second->flags.load(std::memory_order_relaxed) & kStopped)
|
||||
it->second->restart_cb = [&, resource_id, sound, amplitude,
|
||||
reset_pos]() -> void {
|
||||
Play(resource_id, sound, amplitude, reset_pos);
|
||||
SetEndCallback(resource_id, std::move(ocb));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -75,16 +74,18 @@ void AudioBase::Play(uint64_t resource_id,
|
|||
play_list_[0].push_back(it->second);
|
||||
}
|
||||
|
||||
void AudioBase::Stop(uint64_t resource_id) {
|
||||
void AudioMixer::Stop(uint64_t resource_id) {
|
||||
auto it = resources_.find(resource_id);
|
||||
if (it == resources_.end())
|
||||
return;
|
||||
|
||||
if (it->second->active)
|
||||
if (it->second->active) {
|
||||
it->second->restart_cb = nullptr;
|
||||
it->second->flags.fetch_or(kStopped, std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioBase::SetLoop(uint64_t resource_id, bool loop) {
|
||||
void AudioMixer::SetLoop(uint64_t resource_id, bool loop) {
|
||||
auto it = resources_.find(resource_id);
|
||||
if (it == resources_.end())
|
||||
return;
|
||||
|
@ -95,7 +96,7 @@ void AudioBase::SetLoop(uint64_t resource_id, bool loop) {
|
|||
it->second->flags.fetch_and(~kLoop, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void AudioBase::SetSimulateStereo(uint64_t resource_id, bool simulate) {
|
||||
void AudioMixer::SetSimulateStereo(uint64_t resource_id, bool simulate) {
|
||||
auto it = resources_.find(resource_id);
|
||||
if (it == resources_.end())
|
||||
return;
|
||||
|
@ -106,7 +107,7 @@ void AudioBase::SetSimulateStereo(uint64_t resource_id, bool simulate) {
|
|||
it->second->flags.fetch_and(~kSimulateStereo, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void AudioBase::SetResampleStep(uint64_t resource_id, size_t step) {
|
||||
void AudioMixer::SetResampleStep(uint64_t resource_id, size_t step) {
|
||||
auto it = resources_.find(resource_id);
|
||||
if (it == resources_.end())
|
||||
return;
|
||||
|
@ -114,7 +115,7 @@ void AudioBase::SetResampleStep(uint64_t resource_id, size_t step) {
|
|||
it->second->step.store(step + 100, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void AudioBase::SetMaxAmplitude(uint64_t resource_id, float max_amplitude) {
|
||||
void AudioMixer::SetMaxAmplitude(uint64_t resource_id, float max_amplitude) {
|
||||
auto it = resources_.find(resource_id);
|
||||
if (it == resources_.end())
|
||||
return;
|
||||
|
@ -122,7 +123,7 @@ void AudioBase::SetMaxAmplitude(uint64_t resource_id, float max_amplitude) {
|
|||
it->second->max_amplitude.store(max_amplitude, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void AudioBase::SetAmplitudeInc(uint64_t resource_id, float amplitude_inc) {
|
||||
void AudioMixer::SetAmplitudeInc(uint64_t resource_id, float amplitude_inc) {
|
||||
auto it = resources_.find(resource_id);
|
||||
if (it == resources_.end())
|
||||
return;
|
||||
|
@ -130,7 +131,7 @@ void AudioBase::SetAmplitudeInc(uint64_t resource_id, float amplitude_inc) {
|
|||
it->second->amplitude_inc.store(amplitude_inc, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void AudioBase::SetEndCallback(uint64_t resource_id, base::Closure cb) {
|
||||
void AudioMixer::SetEndCallback(uint64_t resource_id, base::Closure cb) {
|
||||
auto it = resources_.find(resource_id);
|
||||
if (it == resources_.end())
|
||||
return;
|
||||
|
@ -138,7 +139,7 @@ void AudioBase::SetEndCallback(uint64_t resource_id, base::Closure cb) {
|
|||
it->second->end_cb = std::move(cb);
|
||||
}
|
||||
|
||||
void AudioBase::RenderAudio(float* output_buffer, size_t num_frames) {
|
||||
void AudioMixer::RenderAudio(float* output_buffer, size_t num_frames) {
|
||||
{
|
||||
std::unique_lock<std::mutex> scoped_lock(lock_, std::try_to_lock);
|
||||
if (scoped_lock)
|
||||
|
@ -207,20 +208,18 @@ void AudioBase::RenderAudio(float* output_buffer, size_t num_frames) {
|
|||
|
||||
// Remove, loop or stream if the source data is consumed
|
||||
if (src_index >= num_samples) {
|
||||
if (!sound->is_streaming_sound()) {
|
||||
if (num_samples)
|
||||
src_index %= num_samples;
|
||||
|
||||
if (!sound->is_streaming_sound()) {
|
||||
if (!(flags & kLoop)) {
|
||||
marked_for_removal = true;
|
||||
break;
|
||||
}
|
||||
} else if (!it->get()->streaming_in_progress.load(
|
||||
std::memory_order_acquire)) {
|
||||
if (num_samples)
|
||||
src_index %= num_samples;
|
||||
|
||||
// Looping streaming sounds never return eof.
|
||||
if (sound->eof()) {
|
||||
DCHECK((flags & kLoop) == 0);
|
||||
marked_for_removal = true;
|
||||
break;
|
||||
}
|
||||
|
@ -238,10 +237,9 @@ void AudioBase::RenderAudio(float* output_buffer, size_t num_frames) {
|
|||
|
||||
ThreadPool::Get().PostTask(
|
||||
HERE,
|
||||
std::bind(&AudioBase::DoStream, this, *it, flags & kLoop));
|
||||
std::bind(&AudioMixer::DoStream, this, *it, flags & kLoop));
|
||||
} else if (num_samples) {
|
||||
DLOG << "Buffer underrun!";
|
||||
src_index %= num_samples;
|
||||
DLOG << "Mixer buffer underrun!";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -263,7 +261,7 @@ void AudioBase::RenderAudio(float* output_buffer, size_t num_frames) {
|
|||
if ((!it->get()->sound->is_streaming_sound() ||
|
||||
!it->get()->streaming_in_progress.load(std::memory_order_relaxed))) {
|
||||
main_thread_task_runner_->PostTask(
|
||||
HERE, std::bind(&AudioBase::EndCallback, this, *it));
|
||||
HERE, std::bind(&AudioMixer::EndCallback, this, *it));
|
||||
it = end_list_.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
|
@ -271,19 +269,20 @@ void AudioBase::RenderAudio(float* output_buffer, size_t num_frames) {
|
|||
}
|
||||
}
|
||||
|
||||
void AudioBase::DoStream(std::shared_ptr<Resource> resource, bool loop) {
|
||||
void AudioMixer::DoStream(std::shared_ptr<Resource> resource, bool loop) {
|
||||
resource->sound->Stream(loop);
|
||||
|
||||
// Memory barrier to ensure all memory writes become visible to the audio
|
||||
// thread.
|
||||
resource->streaming_in_progress.store(false, std::memory_order_release);
|
||||
}
|
||||
|
||||
void AudioBase::EndCallback(std::shared_ptr<Resource> resource) {
|
||||
void AudioMixer::EndCallback(std::shared_ptr<Resource> resource) {
|
||||
resource->active = false;
|
||||
|
||||
if (resource->end_cb)
|
||||
resource->end_cb();
|
||||
if (resource->restart_cb) {
|
||||
resource->restart_cb();
|
||||
resource->restart_cb = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace eng
|
|
@ -1,12 +1,14 @@
|
|||
#ifndef ENGINE_AUDIO_AUDIO_BASE_H
|
||||
#define ENGINE_AUDIO_AUDIO_BASE_H
|
||||
#ifndef ENGINE_AUDIO_AUDIO_MIXER_H
|
||||
#define ENGINE_AUDIO_AUDIO_MIXER_H
|
||||
|
||||
#include <atomic>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "base/closure.h"
|
||||
#include "engine/audio/audio_driver_delegate.h"
|
||||
|
||||
namespace base {
|
||||
class TaskRunner;
|
||||
|
@ -16,11 +18,14 @@ namespace eng {
|
|||
|
||||
class Sound;
|
||||
|
||||
// Class representing the end-point for rendered audio. A platform specific
|
||||
// implementation is expected to periodically call RenderAudio() in a background
|
||||
// Mix and render audio with low overhead. A platform specific AudioDriver
|
||||
// implementation is expected to periodically call Render() in a background
|
||||
// thread.
|
||||
class AudioBase {
|
||||
class AudioMixer : public AudioDriverDelegate {
|
||||
public:
|
||||
AudioMixer();
|
||||
~AudioMixer();
|
||||
|
||||
uint64_t CreateResource();
|
||||
void DestroyResource(uint64_t resource_id);
|
||||
|
||||
|
@ -39,21 +44,15 @@ class AudioBase {
|
|||
|
||||
void SetEnableAudio(bool enable) { audio_enabled_ = enable; }
|
||||
|
||||
protected:
|
||||
static constexpr int kChannelCount = 2;
|
||||
|
||||
AudioBase();
|
||||
~AudioBase();
|
||||
|
||||
void RenderAudio(float* output_buffer, size_t num_frames);
|
||||
|
||||
private:
|
||||
enum SampleFlags { kLoop = 1, kStopped = 2, kSimulateStereo = 4 };
|
||||
static constexpr int kChannelCount = 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<Sound> sound;
|
||||
|
@ -83,14 +82,17 @@ class AudioBase {
|
|||
|
||||
bool audio_enabled_ = true;
|
||||
|
||||
// AudioDriverDelegate implementation
|
||||
void RenderAudio(float* output_buffer, size_t num_frames) final;
|
||||
|
||||
void DoStream(std::shared_ptr<Resource> sample, bool loop);
|
||||
|
||||
void EndCallback(std::shared_ptr<Resource> sample);
|
||||
|
||||
AudioBase(const AudioBase&) = delete;
|
||||
AudioBase& operator=(const AudioBase&) = delete;
|
||||
AudioMixer(const AudioMixer&) = delete;
|
||||
AudioMixer& operator=(const AudioMixer&) = delete;
|
||||
};
|
||||
|
||||
} // namespace eng
|
||||
|
||||
#endif // ENGINE_AUDIO_AUDIO_BASE_H
|
||||
#endif // ENGINE_AUDIO_AUDIO_MIXER_H
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
#include "base/log.h"
|
||||
#include "engine/animator.h"
|
||||
#include "engine/audio/audio.h"
|
||||
#include "engine/audio/audio_driver.h"
|
||||
#include "engine/audio/audio_mixer.h"
|
||||
#include "engine/drawable.h"
|
||||
#include "engine/font.h"
|
||||
#include "engine/game.h"
|
||||
|
@ -25,11 +26,17 @@ namespace eng {
|
|||
|
||||
Engine* Engine::singleton = nullptr;
|
||||
|
||||
Engine::Engine(Platform* platform, Renderer* renderer, Audio* audio)
|
||||
: platform_(platform), renderer_(renderer), audio_(audio) {
|
||||
Engine::Engine(Platform* platform,
|
||||
Renderer* renderer,
|
||||
AudioDriver* audio_driver)
|
||||
: platform_(platform),
|
||||
renderer_(renderer),
|
||||
audio_driver_(audio_driver),
|
||||
audio_mixer_{std::make_unique<AudioMixer>()} {
|
||||
DCHECK(!singleton);
|
||||
singleton = this;
|
||||
|
||||
audio_driver_->SetDelegate(audio_mixer_.get());
|
||||
renderer_->SetContextLostCB(std::bind(&Engine::ContextLost, this));
|
||||
|
||||
quad_ = CreateRenderResource<Geometry>();
|
||||
|
@ -151,14 +158,14 @@ void Engine::Draw(float frame_frac) {
|
|||
}
|
||||
|
||||
void Engine::LostFocus() {
|
||||
audio_->Suspend();
|
||||
audio_driver_->Suspend();
|
||||
|
||||
if (game_)
|
||||
game_->LostFocus();
|
||||
}
|
||||
|
||||
void Engine::GainedFocus(bool from_interstitial_ad) {
|
||||
audio_->Resume();
|
||||
audio_driver_->Resume();
|
||||
|
||||
if (game_)
|
||||
game_->GainedFocus(from_interstitial_ad);
|
||||
|
@ -436,7 +443,7 @@ void Engine::SetKeepScreenOn(bool keep_screen_on) {
|
|||
}
|
||||
|
||||
void Engine::SetEnableAudio(bool enable) {
|
||||
audio_->SetEnableAudio(enable);
|
||||
audio_mixer_->SetEnableAudio(enable);
|
||||
}
|
||||
|
||||
TextureCompressor* Engine::GetTextureCompressor(bool opacity) {
|
||||
|
@ -474,7 +481,7 @@ const std::string& Engine::GetSharedDataPath() const {
|
|||
}
|
||||
|
||||
int Engine::GetAudioHardwareSampleRate() {
|
||||
return audio_->GetHardwareSampleRate();
|
||||
return audio_driver_->GetHardwareSampleRate();
|
||||
}
|
||||
|
||||
bool Engine::IsMobile() const {
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
|
||||
#include "base/random.h"
|
||||
#include "base/vecmath.h"
|
||||
#include "engine/audio/audio_forward.h"
|
||||
#include "engine/persistent_data.h"
|
||||
|
||||
class TextureCompressor;
|
||||
|
@ -17,6 +16,8 @@ class TextureCompressor;
|
|||
namespace eng {
|
||||
|
||||
class Animator;
|
||||
class AudioDriver;
|
||||
class AudioMixer;
|
||||
class Font;
|
||||
class Game;
|
||||
class Drawable;
|
||||
|
@ -33,7 +34,7 @@ class Engine {
|
|||
public:
|
||||
using CreateImageCB = std::function<std::unique_ptr<Image>()>;
|
||||
|
||||
Engine(Platform* platform, Renderer* renderer, Audio* audio);
|
||||
Engine(Platform* platform, Renderer* renderer, AudioDriver* audio_driver);
|
||||
~Engine();
|
||||
|
||||
static Engine& Get();
|
||||
|
@ -107,7 +108,7 @@ class Engine {
|
|||
|
||||
void SetEnableVibration(bool enable) { vibration_enabled_ = enable; }
|
||||
|
||||
Audio* GetAudio() { return audio_; }
|
||||
AudioMixer* GetAudioMixer() { return audio_mixer_.get(); }
|
||||
|
||||
// Access to the render resources.
|
||||
Geometry* GetQuad() { return quad_.get(); }
|
||||
|
@ -172,11 +173,10 @@ class Engine {
|
|||
static Engine* singleton;
|
||||
|
||||
Platform* platform_ = nullptr;
|
||||
|
||||
Renderer* renderer_ = nullptr;
|
||||
AudioDriver* audio_driver_ = nullptr;
|
||||
|
||||
Audio* audio_ = nullptr;
|
||||
|
||||
std::unique_ptr<AudioMixer> audio_mixer_;
|
||||
std::unique_ptr<Game> game_;
|
||||
|
||||
std::unique_ptr<Geometry> quad_;
|
||||
|
|
|
@ -2,11 +2,16 @@
|
|||
|
||||
#include "base/log.h"
|
||||
#include "base/task_runner.h"
|
||||
#include "engine/audio/audio.h"
|
||||
#include "engine/engine.h"
|
||||
#include "engine/renderer/opengl/renderer_opengl.h"
|
||||
#include "engine/renderer/vulkan/renderer_vulkan.h"
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#include "engine/audio/audio_driver_oboe.h"
|
||||
#elif defined(__linux__)
|
||||
#include "engine/audio/audio_driver_alsa.h"
|
||||
#endif
|
||||
|
||||
using namespace base;
|
||||
|
||||
namespace eng {
|
||||
|
@ -23,9 +28,13 @@ void Platform::InitializeCommon() {
|
|||
thread_pool_.Initialize();
|
||||
TaskRunner::CreateThreadLocalTaskRunner();
|
||||
|
||||
audio_ = std::make_unique<Audio>();
|
||||
if (!audio_->Initialize()) {
|
||||
LOG << "Failed to initialize audio system.";
|
||||
#if defined(__ANDROID__)
|
||||
audio_driver_ = std::make_unique<AudioDriverOboe>();
|
||||
#elif defined(__linux__)
|
||||
audio_driver_ = std::make_unique<AudioDriverAlsa>();
|
||||
#endif
|
||||
if (!audio_driver_->Initialize()) {
|
||||
LOG << "Failed to initialize audio driver.";
|
||||
throw internal_error;
|
||||
}
|
||||
|
||||
|
@ -41,12 +50,13 @@ void Platform::InitializeCommon() {
|
|||
void Platform::ShutdownCommon() {
|
||||
LOG << "Shutting down platform.";
|
||||
|
||||
audio_->Shutdown();
|
||||
audio_driver_->Shutdown();
|
||||
renderer_->Shutdown();
|
||||
}
|
||||
|
||||
void Platform::RunMainLoop() {
|
||||
engine_ = std::make_unique<Engine>(this, renderer_.get(), audio_.get());
|
||||
engine_ =
|
||||
std::make_unique<Engine>(this, renderer_.get(), audio_driver_.get());
|
||||
if (!engine_->Initialize()) {
|
||||
LOG << "Failed to initialize the engine.";
|
||||
throw internal_error;
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
|
||||
#include "base/thread_pool.h"
|
||||
#include "base/timer.h"
|
||||
#include "engine/audio/audio_forward.h"
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
|
||||
|
@ -25,6 +24,7 @@ struct AInputEvent;
|
|||
|
||||
namespace eng {
|
||||
|
||||
class AudioDriver;
|
||||
class Renderer;
|
||||
class Engine;
|
||||
|
||||
|
@ -80,7 +80,7 @@ class Platform {
|
|||
bool has_focus_ = false;
|
||||
bool should_exit_ = false;
|
||||
|
||||
std::unique_ptr<Audio> audio_;
|
||||
std::unique_ptr<AudioDriver> audio_driver_;
|
||||
std::unique_ptr<Renderer> renderer_;
|
||||
std::unique_ptr<Engine> engine_;
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
#include "base/log.h"
|
||||
#include "base/task_runner.h"
|
||||
#include "engine/audio/audio.h"
|
||||
#include "engine/engine.h"
|
||||
#include "engine/input_event.h"
|
||||
#include "engine/renderer/renderer.h"
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
#include "base/log.h"
|
||||
#include "base/task_runner.h"
|
||||
#include "base/vecmath.h"
|
||||
#include "engine/audio/audio.h"
|
||||
#include "engine/engine.h"
|
||||
#include "engine/input_event.h"
|
||||
#include "engine/renderer/renderer.h"
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
#include "base/interpolation.h"
|
||||
#include "base/log.h"
|
||||
#include "engine/audio/audio.h"
|
||||
#include "engine/audio/audio_mixer.h"
|
||||
#include "engine/engine.h"
|
||||
#include "engine/sound.h"
|
||||
|
||||
|
@ -11,10 +11,10 @@ using namespace base;
|
|||
namespace eng {
|
||||
|
||||
SoundPlayer::SoundPlayer()
|
||||
: resource_id_(Engine::Get().GetAudio()->CreateResource()) {}
|
||||
: resource_id_(Engine::Get().GetAudioMixer()->CreateResource()) {}
|
||||
|
||||
SoundPlayer::~SoundPlayer() {
|
||||
Engine::Get().GetAudio()->DestroyResource(resource_id_);
|
||||
Engine::Get().GetAudioMixer()->DestroyResource(resource_id_);
|
||||
}
|
||||
|
||||
void SoundPlayer::SetSound(std::shared_ptr<Sound> sound) {
|
||||
|
@ -32,14 +32,14 @@ void SoundPlayer::Play(bool loop, float fade_in_duration) {
|
|||
return;
|
||||
|
||||
int step = variate_ ? Engine::Get().GetRandomGenerator().Roll(3) - 2 : 0;
|
||||
Engine::Get().GetAudio()->SetResampleStep(resource_id_, step * 12);
|
||||
Engine::Get().GetAudio()->SetLoop(resource_id_, loop);
|
||||
Engine::Get().GetAudioMixer()->SetResampleStep(resource_id_, step * 12);
|
||||
Engine::Get().GetAudioMixer()->SetLoop(resource_id_, loop);
|
||||
if (fade_in_duration > 0)
|
||||
Engine::Get().GetAudio()->SetAmplitudeInc(
|
||||
Engine::Get().GetAudioMixer()->SetAmplitudeInc(
|
||||
resource_id_, 1.0f / (sound_->sample_rate() * fade_in_duration));
|
||||
else
|
||||
Engine::Get().GetAudio()->SetAmplitudeInc(resource_id_, 0);
|
||||
Engine::Get().GetAudio()->Play(
|
||||
Engine::Get().GetAudioMixer()->SetAmplitudeInc(resource_id_, 0);
|
||||
Engine::Get().GetAudioMixer()->Play(
|
||||
resource_id_, sound_, fade_in_duration > 0 ? 0 : max_amplitude_, true);
|
||||
}
|
||||
|
||||
|
@ -48,9 +48,9 @@ void SoundPlayer::Resume(float fade_in_duration) {
|
|||
return;
|
||||
|
||||
if (fade_in_duration > 0)
|
||||
Engine::Get().GetAudio()->SetAmplitudeInc(
|
||||
Engine::Get().GetAudioMixer()->SetAmplitudeInc(
|
||||
resource_id_, 1.0f / (sound_->sample_rate() * fade_in_duration));
|
||||
Engine::Get().GetAudio()->Play(resource_id_, sound_,
|
||||
Engine::Get().GetAudioMixer()->Play(resource_id_, sound_,
|
||||
fade_in_duration > 0 ? 0 : -1, false);
|
||||
}
|
||||
|
||||
|
@ -59,10 +59,10 @@ void SoundPlayer::Stop(float fade_out_duration) {
|
|||
return;
|
||||
|
||||
if (fade_out_duration > 0)
|
||||
Engine::Get().GetAudio()->SetAmplitudeInc(
|
||||
Engine::Get().GetAudioMixer()->SetAmplitudeInc(
|
||||
resource_id_, -1.0f / (sound_->sample_rate() * fade_out_duration));
|
||||
else
|
||||
Engine::Get().GetAudio()->Stop(resource_id_);
|
||||
Engine::Get().GetAudioMixer()->Stop(resource_id_);
|
||||
}
|
||||
|
||||
void SoundPlayer::SetVariate(bool variate) {
|
||||
|
@ -70,16 +70,16 @@ void SoundPlayer::SetVariate(bool variate) {
|
|||
}
|
||||
|
||||
void SoundPlayer::SetSimulateStereo(bool simulate) {
|
||||
Engine::Get().GetAudio()->SetSimulateStereo(resource_id_, simulate);
|
||||
Engine::Get().GetAudioMixer()->SetSimulateStereo(resource_id_, simulate);
|
||||
}
|
||||
|
||||
void SoundPlayer::SetMaxAplitude(float max_amplitude) {
|
||||
max_amplitude_ = max_amplitude;
|
||||
Engine::Get().GetAudio()->SetMaxAmplitude(resource_id_, max_amplitude);
|
||||
Engine::Get().GetAudioMixer()->SetMaxAmplitude(resource_id_, max_amplitude);
|
||||
}
|
||||
|
||||
void SoundPlayer::SetEndCallback(base::Closure cb) {
|
||||
Engine::Get().GetAudio()->SetEndCallback(resource_id_, cb);
|
||||
Engine::Get().GetAudioMixer()->SetEndCallback(resource_id_, cb);
|
||||
}
|
||||
|
||||
} // namespace eng
|
||||
|
|
Loading…
Reference in New Issue