Compare commits

..

3 Commits

Author SHA1 Message Date
Attila Uygun 261e7f41d6 Move custom shader from ImageQuad to Drawable 2023-09-30 23:30:34 +02:00
Attila Uygun 02418aa42a Rename AudioSink to AudioDevice 2023-09-30 22:43:28 +02:00
Attila Uygun e08f6e2022 cleanup build.gradle 2023-09-29 14:05:38 +02:00
18 changed files with 191 additions and 184 deletions

View File

@ -114,13 +114,13 @@ android {
java.srcDirs += ['../../../src/engine/platform/java/com/kaliber/base'] java.srcDirs += ['../../../src/engine/platform/java/com/kaliber/base']
android.buildTypes.each { buildType -> android.buildTypes.each { buildType ->
"${buildType.name}" { "${buildType.name}" {
assets.srcDirs = [utils.getAssetsDir(buildType.name)] assets.srcDirs = ["${utils.getGnOutDir(buildType.name)}/assets"]
} }
} }
} }
android.buildTypes.each { buildType -> android.buildTypes.each { buildType ->
"${buildType.name}" { "${buildType.name}" {
jniLibs.srcDirs = [utils.getJniLibsDir(buildType.name)] jniLibs.srcDirs = ["${utils.getGnOutDir(buildType.name)}/jniLibs"]
} }
} }
} }
@ -143,7 +143,7 @@ dependencies {
utils.addTask('generateGnArgsFor') { String taskName, String buildType, String arch -> utils.addTask('generateGnArgsFor') { String taskName, String buildType, String arch ->
task(taskName, type: WriteFileTask) { task(taskName, type: WriteFileTask) {
content = utils.generateGnArgsContent(buildType, arch) content = utils.generateGnArgsContent(buildType, arch)
target = project.layout.file(provider { new File("${utils.getOutDir(buildType)}/${arch}", 'args.gn') }) target = project.layout.file(provider { new File("${utils.getGnOutDir(buildType)}/${arch}", 'args.gn') })
} }
} }
@ -153,11 +153,11 @@ utils.addTask('runGnFor') { String taskName, String buildType, String arch ->
dependsOn "generateGnArgsFor${arch}${buildType}" dependsOn "generateGnArgsFor${arch}${buildType}"
executable rootProject.ext.gn executable rootProject.ext.gn
args '--fail-on-unused-args', 'gen', "${utils.getOutDir(buildType)}/${arch}" args '--fail-on-unused-args', 'gen', "${utils.getGnOutDir(buildType)}/${arch}"
// Need to run gn only once unless the configuration in `args.gn` changes. // Need to run gn only once unless the configuration in `args.gn` changes.
inputs.file(new File("${utils.getOutDir(buildType)}/${arch}", 'args.gn')) inputs.file(new File("${utils.getGnOutDir(buildType)}/${arch}", 'args.gn'))
outputs.file(new File("${utils.getOutDir(buildType)}/${arch}", 'build.ninja')) outputs.file(new File("${utils.getGnOutDir(buildType)}/${arch}", 'build.ninja'))
} }
} }
@ -167,7 +167,7 @@ utils.addGameTask('runNinjaFor') { String taskName, String buildType, String arc
dependsOn "runGnFor${arch}${buildType}" dependsOn "runGnFor${arch}${buildType}"
executable rootProject.ext.ninja executable rootProject.ext.ninja
args '-C', "${utils.getOutDir(buildType)}/${arch}", "src/${utils.getGnTargetFor(game)}" args '-C', "${utils.getGnOutDir(buildType)}/${arch}", "src/${utils.getGnTargetFor(game)}"
// Always run ninja and let it figure out what needs to be compiled. // Always run ninja and let it figure out what needs to be compiled.
outputs.upToDateWhen { false } outputs.upToDateWhen { false }
@ -180,8 +180,8 @@ utils.addGameTask('copyAssetsFor') { String taskName, String buildType, String a
task(taskName, type: Copy) { task(taskName, type: Copy) {
dependsOn "runNinjaFor${game}${arch}${buildType}" dependsOn "runNinjaFor${game}${arch}${buildType}"
from "${utils.getOutDir(buildType)}/${arch}/assets" from "${utils.getGnOutDir(buildType)}/${arch}/assets"
into utils.getAssetsDir(buildType) into "${utils.getGnOutDir(buildType)}/assets"
} }
} }
@ -190,10 +190,10 @@ utils.addGameTask('copyJniLibsFor') { String taskName, String buildType, String
task(taskName, type: Copy) { task(taskName, type: Copy) {
dependsOn "runNinjaFor${game}${arch}${buildType}" dependsOn "runNinjaFor${game}${arch}${buildType}"
from("${utils.getOutDir(buildType)}/${arch}") { from("${utils.getGnOutDir(buildType)}/${arch}") {
include "lib${utils.getGnTargetFor(game)}.so" include "lib${utils.getGnTargetFor(game)}.so"
} }
into "${utils.getJniLibsDir(buildType)}/${utils.getAbiCodeFor(arch)}" into "${utils.getGnOutDir(buildType)}/jniLibs/${utils.getAbiCodeFor(arch)}"
} }
} }
@ -357,17 +357,9 @@ class Utils implements Plugin<Project> {
return content return content
} }
def getOutDir(String buildType) { def getGnOutDir(String buildType) {
return "${project.buildDir}/gn_out/${buildType.toLowerCase()}" return "${project.buildDir}/gn_out/${buildType.toLowerCase()}"
} }
def getAssetsDir(String buildType) {
return "${project.buildDir}/gn_out/${buildType.toLowerCase()}/assets"
}
def getJniLibsDir(String buildType) {
return "${project.buildDir}/gn_out/jniLibs/${buildType.toLowerCase()}"
}
} }
abstract class WriteFileTask extends DefaultTask { abstract class WriteFileTask extends DefaultTask {

View File

@ -2,9 +2,9 @@ source_set("audio") {
sources = [ sources = [
"audio_bus.cc", "audio_bus.cc",
"audio_bus.h", "audio_bus.h",
"audio_device.h",
"audio_mixer.cc", "audio_mixer.cc",
"audio_mixer.h", "audio_mixer.h",
"audio_sink.h",
"mixer_input.cc", "mixer_input.cc",
"mixer_input.h", "mixer_input.h",
"sinc_resampler.cc", "sinc_resampler.cc",
@ -16,16 +16,16 @@ source_set("audio") {
if (target_os == "linux") { if (target_os == "linux") {
sources += [ sources += [
"audio_sink_alsa.cc", "audio_device_alsa.cc",
"audio_sink_alsa.h", "audio_device_alsa.h",
] ]
libs += [ "asound" ] libs += [ "asound" ]
} else if (target_os == "win") { } else if (target_os == "win") {
sources += [ "audio_sink_null.h" ] sources += [ "audio_device_null.h" ]
} else if (target_os == "android") { } else if (target_os == "android") {
sources += [ sources += [
"audio_sink_oboe.cc", "audio_device_oboe.cc",
"audio_sink_oboe.h", "audio_device_oboe.h",
] ]
deps += [ "//src/third_party/oboe" ] deps += [ "//src/third_party/oboe" ]
} }

View File

@ -0,0 +1,37 @@
#ifndef ENGINE_AUDIO_AUDIO_DEVICE_H
#define ENGINE_AUDIO_AUDIO_DEVICE_H
namespace eng {
// Models an audio device sending mixed audio to the audio driver. Audio data
// from the mixer source is delivered on a pull model using Delegate.
class AudioDevice {
public:
class Delegate {
public:
Delegate() = default;
virtual ~Delegate() = default;
virtual int GetChannelCount() = 0;
virtual void RenderAudio(float* output_buffer, size_t num_frames) = 0;
};
AudioDevice() = default;
virtual ~AudioDevice() = default;
virtual bool Initialize() = 0;
virtual void Suspend() = 0;
virtual void Resume() = 0;
virtual size_t GetHardwareSampleRate() = 0;
private:
AudioDevice(const AudioDevice&) = delete;
AudioDevice& operator=(const AudioDevice&) = delete;
};
} // namespace eng
#endif // ENGINE_AUDIO_AUDIO_DEVICE_H

View File

@ -1,4 +1,4 @@
#include "engine/audio/audio_sink_alsa.h" #include "engine/audio/audio_device_alsa.h"
#include <memory> #include <memory>
@ -11,10 +11,10 @@ using namespace base;
namespace eng { namespace eng {
AudioSinkAlsa::AudioSinkAlsa(AudioSink::Delegate* delegate) AudioDeviceAlsa::AudioDeviceAlsa(AudioDevice::Delegate* delegate)
: delegate_(delegate) {} : delegate_(delegate) {}
AudioSinkAlsa::~AudioSinkAlsa() { AudioDeviceAlsa::~AudioDeviceAlsa() {
LOG(0) << "Shutting down audio."; LOG(0) << "Shutting down audio.";
TerminateAudioThread(); TerminateAudioThread();
@ -22,7 +22,7 @@ AudioSinkAlsa::~AudioSinkAlsa() {
snd_pcm_close(device_); snd_pcm_close(device_);
} }
bool AudioSinkAlsa::Initialize() { bool AudioDeviceAlsa::Initialize() {
LOG(0) << "Initializing audio."; LOG(0) << "Initializing audio.";
int err; int err;
@ -145,28 +145,28 @@ bool AudioSinkAlsa::Initialize() {
return false; return false;
} }
void AudioSinkAlsa::Suspend() { void AudioDeviceAlsa::Suspend() {
suspend_audio_thread_.store(true, std::memory_order_relaxed); suspend_audio_thread_.store(true, std::memory_order_relaxed);
} }
void AudioSinkAlsa::Resume() { void AudioDeviceAlsa::Resume() {
suspend_audio_thread_.store(false, std::memory_order_relaxed); suspend_audio_thread_.store(false, std::memory_order_relaxed);
} }
size_t AudioSinkAlsa::GetHardwareSampleRate() { size_t AudioDeviceAlsa::GetHardwareSampleRate() {
return sample_rate_; return sample_rate_;
} }
void AudioSinkAlsa::StartAudioThread() { void AudioDeviceAlsa::StartAudioThread() {
DCHECK(!audio_thread_.joinable()); DCHECK(!audio_thread_.joinable());
LOG(0) << "Starting audio thread."; LOG(0) << "Starting audio thread.";
terminate_audio_thread_.store(false, std::memory_order_relaxed); terminate_audio_thread_.store(false, std::memory_order_relaxed);
suspend_audio_thread_.store(false, std::memory_order_relaxed); suspend_audio_thread_.store(false, std::memory_order_relaxed);
audio_thread_ = std::thread(&AudioSinkAlsa::AudioThreadMain, this); audio_thread_ = std::thread(&AudioDeviceAlsa::AudioThreadMain, this);
} }
void AudioSinkAlsa::TerminateAudioThread() { void AudioDeviceAlsa::TerminateAudioThread() {
if (!audio_thread_.joinable()) if (!audio_thread_.joinable())
return; return;
@ -176,7 +176,7 @@ void AudioSinkAlsa::TerminateAudioThread() {
audio_thread_.join(); audio_thread_.join();
} }
void AudioSinkAlsa::AudioThreadMain() { void AudioDeviceAlsa::AudioThreadMain() {
DCHECK(delegate_); DCHECK(delegate_);
size_t num_frames = period_size_ / (num_channels_ * sizeof(float)); size_t num_frames = period_size_ / (num_channels_ * sizeof(float));

View File

@ -1,19 +1,19 @@
#ifndef ENGINE_AUDIO_AUDIO_SINK_ALSA_H #ifndef ENGINE_AUDIO_AUDIO_DEVICE_ALSA_H
#define ENGINE_AUDIO_AUDIO_SINK_ALSA_H #define ENGINE_AUDIO_AUDIO_DEVICE_ALSA_H
#include <atomic> #include <atomic>
#include <thread> #include <thread>
#include "engine/audio/audio_sink.h" #include "engine/audio/audio_device.h"
typedef struct _snd_pcm snd_pcm_t; typedef struct _snd_pcm snd_pcm_t;
namespace eng { namespace eng {
class AudioSinkAlsa final : public AudioSink { class AudioDeviceAlsa final : public AudioDevice {
public: public:
AudioSinkAlsa(AudioSink::Delegate* delegate); AudioDeviceAlsa(AudioDevice::Delegate* delegate);
~AudioSinkAlsa() final; ~AudioDeviceAlsa() final;
bool Initialize() final; bool Initialize() final;
@ -34,7 +34,7 @@ class AudioSinkAlsa final : public AudioSink {
size_t sample_rate_ = 0; size_t sample_rate_ = 0;
size_t period_size_ = 0; size_t period_size_ = 0;
AudioSink::Delegate* delegate_ = nullptr; AudioDevice::Delegate* delegate_ = nullptr;
void StartAudioThread(); void StartAudioThread();
void TerminateAudioThread(); void TerminateAudioThread();
@ -44,4 +44,4 @@ class AudioSinkAlsa final : public AudioSink {
} // namespace eng } // namespace eng
#endif // ENGINE_AUDIO_AUDIO_SINK_ALSA_H #endif // ENGINE_AUDIO_AUDIO_DEVICE_ALSA_H

View File

@ -0,0 +1,23 @@
#ifndef ENGINE_AUDIO_AUDIO_DEVICE_NULL_H
#define ENGINE_AUDIO_AUDIO_DEVICE_NULL_H
#include "engine/audio/audio_device.h"
namespace eng {
class AudioDeviceNull final : public AudioDevice {
public:
AudioDeviceNull() = default;
~AudioDeviceNull() final = default;
bool Initialize() final { return true; }
void Suspend() final {}
void Resume() final {}
size_t GetHardwareSampleRate() final { return 0; }
};
} // namespace eng
#endif // ENGINE_AUDIO_AUDIO_DEVICE_NULL_H

View File

@ -1,4 +1,4 @@
#include "engine/audio/audio_sink_oboe.h" #include "engine/audio/audio_device_oboe.h"
#include "base/log.h" #include "base/log.h"
#include "third_party/oboe/include/oboe/Oboe.h" #include "third_party/oboe/include/oboe/Oboe.h"
@ -7,54 +7,54 @@ using namespace base;
namespace eng { namespace eng {
AudioSinkOboe::AudioSinkOboe(AudioSink::Delegate* delegate) AudioDeviceOboe::AudioDeviceOboe(AudioDevice::Delegate* delegate)
: callback_(std::make_unique<StreamCallback>(this)), delegate_(delegate) {} : callback_(std::make_unique<StreamCallback>(this)), delegate_(delegate) {}
AudioSinkOboe::~AudioSinkOboe() { AudioDeviceOboe::~AudioDeviceOboe() {
LOG(0) << "Shutting down audio."; LOG(0) << "Shutting down audio.";
stream_->stop(); stream_->stop();
} }
bool AudioSinkOboe::Initialize() { bool AudioDeviceOboe::Initialize() {
LOG(0) << "Initializing audio."; LOG(0) << "Initializing audio.";
return RestartStream(); return RestartStream();
} }
void AudioSinkOboe::Suspend() { void AudioDeviceOboe::Suspend() {
stream_->pause(); stream_->pause();
} }
void AudioSinkOboe::Resume() { void AudioDeviceOboe::Resume() {
stream_->start(); stream_->start();
} }
size_t AudioSinkOboe::GetHardwareSampleRate() { size_t AudioDeviceOboe::GetHardwareSampleRate() {
return stream_->getSampleRate(); return stream_->getSampleRate();
} }
AudioSinkOboe::StreamCallback::StreamCallback(AudioSinkOboe* audio_sink) AudioDeviceOboe::StreamCallback::StreamCallback(AudioDeviceOboe* audio_device)
: audio_sink_(audio_sink) {} : audio_device_(audio_device) {}
AudioSinkOboe::StreamCallback::~StreamCallback() = default; AudioDeviceOboe::StreamCallback::~StreamCallback() = default;
oboe::DataCallbackResult AudioSinkOboe::StreamCallback::onAudioReady( oboe::DataCallbackResult AudioDeviceOboe::StreamCallback::onAudioReady(
oboe::AudioStream* oboe_stream, oboe::AudioStream* oboe_stream,
void* audio_data, void* audio_data,
int32_t num_frames) { int32_t num_frames) {
float* output_buffer = static_cast<float*>(audio_data); float* output_buffer = static_cast<float*>(audio_data);
audio_sink_->delegate_->RenderAudio(output_buffer, num_frames); audio_device_->delegate_->RenderAudio(output_buffer, num_frames);
return oboe::DataCallbackResult::Continue; return oboe::DataCallbackResult::Continue;
} }
void AudioSinkOboe::StreamCallback::onErrorAfterClose( void AudioDeviceOboe::StreamCallback::onErrorAfterClose(
oboe::AudioStream* oboe_stream, oboe::AudioStream* oboe_stream,
oboe::Result error) { oboe::Result error) {
LOG(0) << "Error after close. Error: " << oboe::convertToText(error); LOG(0) << "Error after close. Error: " << oboe::convertToText(error);
audio_sink_->RestartStream(); audio_device_->RestartStream();
} }
bool AudioSinkOboe::RestartStream() { bool AudioDeviceOboe::RestartStream() {
oboe::AudioStreamBuilder builder; oboe::AudioStreamBuilder builder;
oboe::Result result = oboe::Result result =
builder.setSharingMode(oboe::SharingMode::Exclusive) builder.setSharingMode(oboe::SharingMode::Exclusive)

View File

@ -1,19 +1,19 @@
#ifndef ENGINE_AUDIO_AUDIO_SINK_OBOE_H #ifndef ENGINE_AUDIO_AUDIO_DEVICE_OBOE_H
#define ENGINE_AUDIO_AUDIO_SINK_OBOE_H #define ENGINE_AUDIO_AUDIO_DEVICE_OBOE_H
#include <memory> #include <memory>
#include "third_party/oboe/include/oboe/AudioStream.h" #include "third_party/oboe/include/oboe/AudioStream.h"
#include "third_party/oboe/include/oboe/AudioStreamCallback.h" #include "third_party/oboe/include/oboe/AudioStreamCallback.h"
#include "engine/audio/audio_sink.h" #include "engine/audio/audio_device.h"
namespace eng { namespace eng {
class AudioSinkOboe final : public AudioSink { class AudioDeviceOboe final : public AudioDevice {
public: public:
AudioSinkOboe(AudioSink::Delegate* delegate); AudioDeviceOboe(AudioDevice::Delegate* delegate);
~AudioSinkOboe() final; ~AudioDeviceOboe() final;
bool Initialize() final; bool Initialize() final;
@ -25,7 +25,7 @@ class AudioSinkOboe final : public AudioSink {
private: private:
class StreamCallback final : public oboe::AudioStreamCallback { class StreamCallback final : public oboe::AudioStreamCallback {
public: public:
StreamCallback(AudioSinkOboe* audio); StreamCallback(AudioDeviceOboe* audio);
~StreamCallback() final; ~StreamCallback() final;
oboe::DataCallbackResult onAudioReady(oboe::AudioStream* oboe_stream, oboe::DataCallbackResult onAudioReady(oboe::AudioStream* oboe_stream,
@ -36,17 +36,17 @@ class AudioSinkOboe final : public AudioSink {
oboe::Result error) final; oboe::Result error) final;
private: private:
AudioSinkOboe* audio_sink_; AudioDeviceOboe* audio_device_;
}; };
oboe::ManagedStream stream_; oboe::ManagedStream stream_;
std::unique_ptr<StreamCallback> callback_; std::unique_ptr<StreamCallback> callback_;
AudioSink::Delegate* delegate_ = nullptr; AudioDevice::Delegate* delegate_ = nullptr;
bool RestartStream(); bool RestartStream();
}; };
} // namespace eng } // namespace eng
#endif // ENGINE_AUDIO_AUDIO_SINK_OBOE_H #endif // ENGINE_AUDIO_AUDIO_DEVICE_OBOE_H

View File

@ -8,11 +8,11 @@
#include "engine/audio/mixer_input.h" #include "engine/audio/mixer_input.h"
#if defined(__ANDROID__) #if defined(__ANDROID__)
#include "engine/audio/audio_sink_oboe.h" #include "engine/audio/audio_device_oboe.h"
#elif defined(__linux__) #elif defined(__linux__)
#include "engine/audio/audio_sink_alsa.h" #include "engine/audio/audio_device_alsa.h"
#elif defined(_WIN32) #elif defined(_WIN32)
#include "engine/audio/audio_sink_null.h" #include "engine/audio/audio_device_null.h"
#endif #endif
using namespace base; using namespace base;
@ -22,19 +22,19 @@ namespace eng {
AudioMixer::AudioMixer() AudioMixer::AudioMixer()
: main_thread_task_runner_(TaskRunner::GetThreadLocalTaskRunner()), : main_thread_task_runner_(TaskRunner::GetThreadLocalTaskRunner()),
#if defined(__ANDROID__) #if defined(__ANDROID__)
audio_sink_{std::make_unique<AudioSinkOboe>(this)} { audio_device_{std::make_unique<AudioDeviceOboe>(this)} {
#elif defined(__linux__) #elif defined(__linux__)
audio_sink_{std::make_unique<AudioSinkAlsa>(this)} { audio_device_{std::make_unique<AudioDeviceAlsa>(this)} {
#elif defined(_WIN32) #elif defined(_WIN32)
// TODO: Implement AudioSinkWindows // TODO: Implement AudioDeviceWindows
audio_sink_{std::make_unique<AudioSinkNull>()} { audio_device_{std::make_unique<AudioDeviceNull>()} {
#endif #endif
bool res = audio_sink_->Initialize(); bool res = audio_device_->Initialize();
CHECK(res) << "Failed to initialize audio sink."; CHECK(res) << "Failed to initialize audio device.";
} }
AudioMixer::~AudioMixer() { AudioMixer::~AudioMixer() {
audio_sink_.reset(); audio_device_.reset();
} }
void AudioMixer::AddInput(std::shared_ptr<MixerInput> mixer_input) { void AudioMixer::AddInput(std::shared_ptr<MixerInput> mixer_input) {
@ -45,15 +45,15 @@ void AudioMixer::AddInput(std::shared_ptr<MixerInput> mixer_input) {
} }
void AudioMixer::Suspend() { void AudioMixer::Suspend() {
audio_sink_->Suspend(); audio_device_->Suspend();
} }
void AudioMixer::Resume() { void AudioMixer::Resume() {
audio_sink_->Resume(); audio_device_->Resume();
} }
size_t AudioMixer::GetHardwareSampleRate() { size_t AudioMixer::GetHardwareSampleRate() {
return audio_sink_->GetHardwareSampleRate(); return audio_device_->GetHardwareSampleRate();
} }
void AudioMixer::RenderAudio(float* output_buffer, size_t num_frames) { void AudioMixer::RenderAudio(float* output_buffer, size_t num_frames) {

View File

@ -6,7 +6,7 @@
#include <mutex> #include <mutex>
#include "base/closure.h" #include "base/closure.h"
#include "engine/audio/audio_sink.h" #include "engine/audio/audio_device.h"
namespace base { namespace base {
class TaskRunner; class TaskRunner;
@ -21,9 +21,9 @@ class MixerInput;
// when it needs more data. Input source will be removed once end-of-stream is // 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 // reached. Any unfilled frames will be filled with silence. The mixer always
// outputs audio when active, even if input sources underflow. A platform // outputs audio when active, even if input sources underflow. A platform
// specific AudioSink implementation is expected to periodically call // specific AudioDevice implementation is expected to periodically call
// RenderAudio() in a background thread. // RenderAudio() in a background thread.
class AudioMixer : public AudioSink::Delegate { class AudioMixer : public AudioDevice::Delegate {
public: public:
AudioMixer(); AudioMixer();
~AudioMixer(); ~AudioMixer();
@ -48,11 +48,11 @@ class AudioMixer : public AudioSink::Delegate {
std::shared_ptr<base::TaskRunner> main_thread_task_runner_; std::shared_ptr<base::TaskRunner> main_thread_task_runner_;
std::unique_ptr<AudioSink> audio_sink_; std::unique_ptr<AudioDevice> audio_device_;
bool audio_enabled_ = true; bool audio_enabled_ = true;
// AudioSink::Delegate interface // AudioDevice::Delegate interface
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;

View File

@ -1,37 +0,0 @@
#ifndef ENGINE_AUDIO_AUDIO_SINK_H
#define ENGINE_AUDIO_AUDIO_SINK_H
namespace eng {
// Models an audio sink sending mixed audio to the audio driver. Audio data from
// the mixer source is delivered on a pull model using Delegate.
class AudioSink {
public:
class Delegate {
public:
Delegate() = default;
virtual ~Delegate() = default;
virtual int GetChannelCount() = 0;
virtual void RenderAudio(float* output_buffer, size_t num_frames) = 0;
};
AudioSink() = default;
virtual ~AudioSink() = default;
virtual bool Initialize() = 0;
virtual void Suspend() = 0;
virtual void Resume() = 0;
virtual size_t GetHardwareSampleRate() = 0;
private:
AudioSink(const AudioSink&) = delete;
AudioSink& operator=(const AudioSink&) = delete;
};
} // namespace eng
#endif // ENGINE_AUDIO_AUDIO_SINK_H

View File

@ -1,23 +0,0 @@
#ifndef ENGINE_AUDIO_AUDIO_SINK_NULL_H
#define ENGINE_AUDIO_AUDIO_SINK_NULL_H
#include "engine/audio/audio_sink.h"
namespace eng {
class AudioSinkNull final : public AudioSink {
public:
AudioSinkNull() = default;
~AudioSinkNull() final = default;
bool Initialize() final { return true; }
void Suspend() final {}
void Resume() final {}
size_t GetHardwareSampleRate() final { return 0; }
};
} // namespace eng
#endif // ENGINE_AUDIO_AUDIO_SINK_NULL_H

View File

@ -11,8 +11,8 @@ namespace eng {
class AudioBus; class AudioBus;
class AudioMixer; class AudioMixer;
// An audio input stream that gets mixed and rendered to the audio sink. Handles // An audio input stream that gets mixed and rendered to the audio device.
// playback and volume control. // Handles playback and volume control.
class MixerInput : public std::enable_shared_from_this<MixerInput> { class MixerInput : public std::enable_shared_from_this<MixerInput> {
public: public:
enum Flags { kLoop = 1, kStopped = 2, kSimulateStereo = 4 }; enum Flags { kLoop = 1, kStopped = 2, kSimulateStereo = 4 };

View File

@ -1,6 +1,7 @@
#include "engine/drawable.h" #include "engine/drawable.h"
#include "engine/engine.h" #include "engine/engine.h"
#include "engine/renderer/shader.h"
namespace eng { namespace eng {
@ -12,4 +13,17 @@ Drawable::~Drawable() {
Engine::Get().RemoveDrawable(this); Engine::Get().RemoveDrawable(this);
} }
void Drawable::SetCustomShader(const std::string& asset_name) {
custom_shader_ = Engine::Get().GetShader(asset_name);
custom_uniforms_.clear();
}
void Drawable::DoSetCustomUniforms() {
if (custom_shader_) {
for (auto& cu : custom_uniforms_)
std::visit([&](auto&& arg) { custom_shader_->SetUniform(cu.first, arg); },
cu.second);
}
}
} // namespace eng } // namespace eng

View File

@ -1,10 +1,16 @@
#ifndef ENGINE_DRAWABLE_H #ifndef ENGINE_DRAWABLE_H
#define ENGINE_DRAWABLE_H #define ENGINE_DRAWABLE_H
#include <string>
#include <unordered_map>
#include <variant>
#include "base/vecmath.h" #include "base/vecmath.h"
namespace eng { namespace eng {
class Shader;
class Drawable { class Drawable {
public: public:
Drawable(); Drawable();
@ -21,9 +27,30 @@ class Drawable {
int GetZOrder() const { return z_order_; } int GetZOrder() const { return z_order_; }
bool IsVisible() const { return visible_; } bool IsVisible() const { return visible_; }
void SetCustomShader(const std::string& asset_name);
template <typename T>
void SetCustomUniform(const std::string& name, T value) {
custom_uniforms_[name] = UniformValue(value);
}
protected:
Shader* GetCustomShader() { return custom_shader_; }
void DoSetCustomUniforms();
private: private:
using UniformValue = std::variant<base::Vector2f,
base::Vector3f,
base::Vector4f,
base::Matrix4f,
float,
int>;
bool visible_ = false; bool visible_ = false;
int z_order_ = 0; int z_order_ = 0;
Shader* custom_shader_ = nullptr;
std::unordered_map<std::string, UniformValue> custom_uniforms_;
}; };
} // namespace eng } // namespace eng

View File

@ -40,11 +40,6 @@ void ImageQuad::Destroy() {
} }
} }
void ImageQuad::SetCustomShader(const std::string& asset_name) {
custom_shader_ = Engine::Get().GetShader(asset_name);
custom_uniforms_.clear();
}
void ImageQuad::SetFrame(size_t frame) { void ImageQuad::SetFrame(size_t frame) {
DCHECK(frame < GetNumFrames()) DCHECK(frame < GetNumFrames())
<< "asset: " << asset_name_ << " frame: " << frame; << "asset: " << asset_name_ << " frame: " << frame;
@ -66,8 +61,9 @@ void ImageQuad::Draw(float frame_frac) {
Vector2f tex_scale = {GetFrameWidth() / texture_->GetWidth(), Vector2f tex_scale = {GetFrameWidth() / texture_->GetWidth(),
GetFrameHeight() / texture_->GetHeight()}; GetFrameHeight() / texture_->GetHeight()};
Shader* shader = Shader* shader = GetCustomShader();
custom_shader_ ? custom_shader_ : Engine::Get().GetPassThroughShader(); if (!shader)
shader = Engine::Get().GetPassThroughShader();
shader->Activate(); shader->Activate();
shader->SetUniform("offset", position_); shader->SetUniform("offset", position_);
@ -78,12 +74,7 @@ void ImageQuad::Draw(float frame_frac) {
shader->SetUniform("projection", Engine::Get().GetProjectionMatrix()); shader->SetUniform("projection", Engine::Get().GetProjectionMatrix());
shader->SetUniform("color", color_); shader->SetUniform("color", color_);
shader->SetUniform("texture_0", 0); shader->SetUniform("texture_0", 0);
if (custom_shader_) { DoSetCustomUniforms();
for (auto& cu : custom_uniforms_)
std::visit(
[shader, &cu](auto&& arg) { shader->SetUniform(cu.first, arg); },
cu.second);
}
shader->UploadUniforms(); shader->UploadUniforms();
Engine::Get().GetQuad()->Draw(); Engine::Get().GetQuad()->Draw();

View File

@ -3,15 +3,12 @@
#include <array> #include <array>
#include <string> #include <string>
#include <unordered_map>
#include <variant>
#include "base/vecmath.h" #include "base/vecmath.h"
#include "engine/animatable.h" #include "engine/animatable.h"
namespace eng { namespace eng {
class Shader;
class Texture; class Texture;
class ImageQuad final : public Animatable { class ImageQuad final : public Animatable {
@ -26,13 +23,6 @@ class ImageQuad final : public Animatable {
void Destroy(); void Destroy();
void SetCustomShader(const std::string& asset_name);
template <typename T>
void SetCustomUniform(const std::string& name, T value) {
custom_uniforms_[name] = UniformValue(value);
}
// Animatable interface. // Animatable interface.
void SetFrame(size_t frame) final; void SetFrame(size_t frame) final;
size_t GetFrame() const final { return current_frame_; } size_t GetFrame() const final { return current_frame_; }
@ -44,18 +34,8 @@ class ImageQuad final : public Animatable {
void Draw(float frame_frac) final; void Draw(float frame_frac) final;
private: private:
using UniformValue = std::variant<base::Vector2f,
base::Vector3f,
base::Vector4f,
base::Matrix4f,
float,
int>;
Texture* texture_ = nullptr; Texture* texture_ = nullptr;
Shader* custom_shader_ = nullptr;
std::unordered_map<std::string, UniformValue> custom_uniforms_;
size_t current_frame_ = 0; size_t current_frame_ = 0;
std::array<int, 2> num_frames_ = {1, 1}; // horizontal, vertical std::array<int, 2> num_frames_ = {1, 1}; // horizontal, vertical
int frame_width_ = 0; int frame_width_ = 0;

View File

@ -12,7 +12,9 @@ namespace eng {
void SolidQuad::Draw(float frame_frac) { void SolidQuad::Draw(float frame_frac) {
DCHECK(IsVisible()); DCHECK(IsVisible());
Shader* shader = Engine::Get().GetSolidShader(); Shader* shader = GetCustomShader();
if (!shader)
shader = Engine::Get().GetSolidShader();
shader->Activate(); shader->Activate();
shader->SetUniform("offset", position_); shader->SetUniform("offset", position_);
@ -20,6 +22,7 @@ void SolidQuad::Draw(float frame_frac) {
shader->SetUniform("rotation", rotation_); shader->SetUniform("rotation", rotation_);
shader->SetUniform("projection", Engine::Get().GetProjectionMatrix()); shader->SetUniform("projection", Engine::Get().GetProjectionMatrix());
shader->SetUniform("color", color_); shader->SetUniform("color", color_);
DoSetCustomUniforms();
shader->UploadUniforms(); shader->UploadUniforms();
Engine::Get().GetQuad()->Draw(); Engine::Get().GetQuad()->Draw();