mirror of https://github.com/auygun/kaliber.git
Compare commits
No commits in common. "7b637a95d90da3046b76e37eb54bae9045e3cb94" and "f6f67d7e53344c600c56f191a440445040e19029" have entirely different histories.
7b637a95d9
...
f6f67d7e53
|
@ -49,6 +49,7 @@ project(kaliber)
|
||||||
add_library(kaliber SHARED
|
add_library(kaliber SHARED
|
||||||
../../../src/base/collusion_test.cc
|
../../../src/base/collusion_test.cc
|
||||||
../../../src/base/log.cc
|
../../../src/base/log.cc
|
||||||
|
../../../src/base/sinc_resampler.cc
|
||||||
../../../src/base/task_runner.cc
|
../../../src/base/task_runner.cc
|
||||||
../../../src/base/thread_pool.cc
|
../../../src/base/thread_pool.cc
|
||||||
../../../src/base/timer.cc
|
../../../src/base/timer.cc
|
||||||
|
@ -61,10 +62,8 @@ add_library(kaliber SHARED
|
||||||
../../../src/demo/sky_quad.cc
|
../../../src/demo/sky_quad.cc
|
||||||
../../../src/engine/animatable.cc
|
../../../src/engine/animatable.cc
|
||||||
../../../src/engine/animator.cc
|
../../../src/engine/animator.cc
|
||||||
../../../src/engine/audio/audio_bus.cc
|
|
||||||
../../../src/engine/audio/audio_mixer.cc
|
../../../src/engine/audio/audio_mixer.cc
|
||||||
../../../src/engine/audio/audio_sink_oboe.cc
|
../../../src/engine/audio/audio_sink_oboe.cc
|
||||||
../../../src/engine/audio/sinc_resampler.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
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:versionCode="19"
|
android:versionCode="18"
|
||||||
android:versionName="1.0.3">
|
android:versionName="1.0.2">
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
BUILD="$1"
|
|
||||||
if [[ -z "$1" ]]; then
|
|
||||||
BUILD="debug"
|
|
||||||
fi
|
|
||||||
adb logcat | ndk-stack -sym ./app/build/intermediates/merged_native_libs/"$BUILD"/out/lib/arm64-v8a
|
|
|
@ -78,6 +78,7 @@ objs_from_src_in = $(call objs_from_src, $(shell find $(1) -name "*.cc" -o -name
|
||||||
BASE_SRC := \
|
BASE_SRC := \
|
||||||
$(SRC_ROOT)/base/collusion_test.cc \
|
$(SRC_ROOT)/base/collusion_test.cc \
|
||||||
$(SRC_ROOT)/base/log.cc \
|
$(SRC_ROOT)/base/log.cc \
|
||||||
|
$(SRC_ROOT)/base/sinc_resampler.cc \
|
||||||
$(SRC_ROOT)/base/task_runner.cc \
|
$(SRC_ROOT)/base/task_runner.cc \
|
||||||
$(SRC_ROOT)/base/thread_pool.cc \
|
$(SRC_ROOT)/base/thread_pool.cc \
|
||||||
$(SRC_ROOT)/base/timer.cc
|
$(SRC_ROOT)/base/timer.cc
|
||||||
|
@ -94,10 +95,8 @@ $(BASE_LIB): $(BASE_OBJS)
|
||||||
ENGINE_SRC := \
|
ENGINE_SRC := \
|
||||||
$(SRC_ROOT)/engine/animatable.cc \
|
$(SRC_ROOT)/engine/animatable.cc \
|
||||||
$(SRC_ROOT)/engine/animator.cc \
|
$(SRC_ROOT)/engine/animator.cc \
|
||||||
$(SRC_ROOT)/engine/audio/audio_bus.cc \
|
|
||||||
$(SRC_ROOT)/engine/audio/audio_mixer.cc \
|
$(SRC_ROOT)/engine/audio/audio_mixer.cc \
|
||||||
$(SRC_ROOT)/engine/audio/audio_sink_alsa.cc \
|
$(SRC_ROOT)/engine/audio/audio_sink_alsa.cc \
|
||||||
$(SRC_ROOT)/engine/audio/sinc_resampler.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 \
|
||||||
|
|
|
@ -74,7 +74,7 @@
|
||||||
// Note: we're glossing over how the sub-sample handling works with
|
// Note: we're glossing over how the sub-sample handling works with
|
||||||
// |virtual_source_idx_|, etc.
|
// |virtual_source_idx_|, etc.
|
||||||
|
|
||||||
#include "engine/audio/sinc_resampler.h"
|
#include "base/sinc_resampler.h"
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
@ -94,9 +94,7 @@
|
||||||
#include <arm_neon.h>
|
#include <arm_neon.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
using namespace base;
|
namespace base {
|
||||||
|
|
||||||
namespace eng {
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
@ -259,14 +257,14 @@ void SincResampler::InitializeKernel() {
|
||||||
for (int i = 0; i < kernel_size_; ++i) {
|
for (int i = 0; i < kernel_size_; ++i) {
|
||||||
const int idx = i + offset_idx * kernel_size_;
|
const int idx = i + offset_idx * kernel_size_;
|
||||||
const float pre_sinc =
|
const float pre_sinc =
|
||||||
kPiFloat * (i - kernel_size_ / 2 - subsample_offset);
|
base::kPiFloat * (i - kernel_size_ / 2 - subsample_offset);
|
||||||
kernel_pre_sinc_storage_[idx] = pre_sinc;
|
kernel_pre_sinc_storage_[idx] = pre_sinc;
|
||||||
|
|
||||||
// Compute Blackman window, matching the offset of the sinc().
|
// Compute Blackman window, matching the offset of the sinc().
|
||||||
const float x = (i - subsample_offset) / kernel_size_;
|
const float x = (i - subsample_offset) / kernel_size_;
|
||||||
const float window =
|
const float window =
|
||||||
static_cast<float>(kA0 - kA1 * cos(2.0 * kPiDouble * x) +
|
static_cast<float>(kA0 - kA1 * cos(2.0 * base::kPiDouble * x) +
|
||||||
kA2 * cos(4.0 * kPiDouble * x));
|
kA2 * cos(4.0 * base::kPiDouble * x));
|
||||||
kernel_window_storage_[idx] = window;
|
kernel_window_storage_[idx] = window;
|
||||||
|
|
||||||
// Compute the sinc with offset, then window the sinc() function and store
|
// Compute the sinc with offset, then window the sinc() function and store
|
||||||
|
@ -547,4 +545,4 @@ float SincResampler::Convolve_NEON(const int kernel_size,
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
} // namespace eng
|
} // namespace base
|
|
@ -1,18 +1,16 @@
|
||||||
// Copyright 2012 The Chromium Authors
|
// Copyright 2012 The Chromium Authors
|
||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
//
|
|
||||||
// Modified for Kaliber engine.
|
|
||||||
|
|
||||||
#ifndef ENGINE_AUDIO__SINC_RESAMPLER_H
|
#ifndef BASE_SINC_RESAMPLER_H
|
||||||
#define ENGINE_AUDIO__SINC_RESAMPLER_H
|
#define BASE_SINC_RESAMPLER_H
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "base/mem.h"
|
#include "base/mem.h"
|
||||||
|
|
||||||
namespace eng {
|
namespace base {
|
||||||
|
|
||||||
// SincResampler is a high-quality single-channel sample-rate converter.
|
// SincResampler is a high-quality single-channel sample-rate converter.
|
||||||
class SincResampler {
|
class SincResampler {
|
||||||
|
@ -165,12 +163,12 @@ class SincResampler {
|
||||||
// Contains kKernelOffsetCount kernels back-to-back, each of size
|
// Contains kKernelOffsetCount kernels back-to-back, each of size
|
||||||
// `kernel_size_`. The kernel offsets are sub-sample shifts of a windowed sinc
|
// `kernel_size_`. The kernel offsets are sub-sample shifts of a windowed sinc
|
||||||
// shifted from 0.0 to 1.0 sample.
|
// shifted from 0.0 to 1.0 sample.
|
||||||
base::AlignedMemPtr<float[]> kernel_storage_;
|
AlignedMemPtr<float[]> kernel_storage_;
|
||||||
base::AlignedMemPtr<float[]> kernel_pre_sinc_storage_;
|
AlignedMemPtr<float[]> kernel_pre_sinc_storage_;
|
||||||
base::AlignedMemPtr<float[]> kernel_window_storage_;
|
AlignedMemPtr<float[]> kernel_window_storage_;
|
||||||
|
|
||||||
// Data from the source is copied into this buffer for each processing pass.
|
// Data from the source is copied into this buffer for each processing pass.
|
||||||
base::AlignedMemPtr<float[]> input_buffer_;
|
AlignedMemPtr<float[]> input_buffer_;
|
||||||
|
|
||||||
// Stores the runtime selection of which Convolve function to use.
|
// Stores the runtime selection of which Convolve function to use.
|
||||||
using ConvolveProc =
|
using ConvolveProc =
|
||||||
|
@ -186,6 +184,6 @@ class SincResampler {
|
||||||
float* r4_;
|
float* r4_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace eng
|
} // namespace base
|
||||||
|
|
||||||
#endif // ENGINE_AUDIO__SINC_RESAMPLER_H
|
#endif // BASE_SINC_RESAMPLER_H
|
|
@ -261,15 +261,15 @@ void Demo::EnterGameOverState() {
|
||||||
hud_.ShowMessage("Game Over", 3);
|
hud_.ShowMessage("Game Over", 3);
|
||||||
state_ = kGameOver;
|
state_ = kGameOver;
|
||||||
|
|
||||||
|
if (boss_fight_) {
|
||||||
|
music_.Resume(10);
|
||||||
|
boss_music_.Stop(10);
|
||||||
|
}
|
||||||
|
|
||||||
SetDelayedWork(1, [&]() -> void {
|
SetDelayedWork(1, [&]() -> void {
|
||||||
enemy_.RemoveAll();
|
enemy_.RemoveAll();
|
||||||
|
// hud_.Hide();
|
||||||
SetDelayedWork(3, [&]() -> void {
|
SetDelayedWork(3, [&]() -> void {
|
||||||
if (saved_data_.root().get("music", Json::Value(true)).asBool()) {
|
|
||||||
if (boss_fight_) {
|
|
||||||
music_.Resume(10);
|
|
||||||
boss_music_.Stop(10);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wave_ = 0;
|
wave_ = 0;
|
||||||
boss_fight_ = false;
|
boss_fight_ = false;
|
||||||
EnterMenuState();
|
EnterMenuState();
|
||||||
|
@ -396,6 +396,14 @@ void Demo::StartNextStage(bool boss) {
|
||||||
enemy_.PauseProgress();
|
enemy_.PauseProgress();
|
||||||
enemy_.StopAllEnemyUnits();
|
enemy_.StopAllEnemyUnits();
|
||||||
|
|
||||||
|
if (boss) {
|
||||||
|
boss_music_.Play(true, 10);
|
||||||
|
music_.Stop(10);
|
||||||
|
} else if (boss_fight_) {
|
||||||
|
music_.Resume(10);
|
||||||
|
boss_music_.Stop(10);
|
||||||
|
}
|
||||||
|
|
||||||
SetDelayedWork(1.25f, [&, boss]() -> void {
|
SetDelayedWork(1.25f, [&, boss]() -> void {
|
||||||
enemy_.KillAllEnemyUnits();
|
enemy_.KillAllEnemyUnits();
|
||||||
|
|
||||||
|
@ -405,10 +413,6 @@ void Demo::StartNextStage(bool boss) {
|
||||||
|
|
||||||
hud_.HideProgress();
|
hud_.HideProgress();
|
||||||
|
|
||||||
if (saved_data_.root().get("music", Json::Value(true)).asBool()) {
|
|
||||||
boss_music_.Play(true, 10);
|
|
||||||
music_.Stop(10);
|
|
||||||
}
|
|
||||||
boss_fight_ = true;
|
boss_fight_ = true;
|
||||||
DLOG << "Boss fight.";
|
DLOG << "Boss fight.";
|
||||||
} else {
|
} else {
|
||||||
|
@ -451,13 +455,8 @@ void Demo::StartNextStage(bool boss) {
|
||||||
hud_.Show();
|
hud_.Show();
|
||||||
hud_.SetProgress(1);
|
hud_.SetProgress(1);
|
||||||
|
|
||||||
if (boss_fight_) {
|
if (boss_fight_)
|
||||||
player_.TakeDamage(-1);
|
player_.TakeDamage(-1);
|
||||||
if (saved_data_.root().get("music", Json::Value(true)).asBool()) {
|
|
||||||
music_.Resume(10);
|
|
||||||
boss_music_.Stop(10);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
total_enemies_ = 23.0897f * log((float)wave_ + 1.0f) - 10.0f;
|
total_enemies_ = 23.0897f * log((float)wave_ + 1.0f) - 10.0f;
|
||||||
last_num_enemies_killed_ = 0;
|
last_num_enemies_killed_ = 0;
|
||||||
|
|
|
@ -350,8 +350,8 @@ void Enemy::HitTarget(DamageType damage_type) {
|
||||||
|
|
||||||
if (target->damage_type != kDamageType_Any &&
|
if (target->damage_type != kDamageType_Any &&
|
||||||
target->damage_type != damage_type) {
|
target->damage_type != damage_type) {
|
||||||
// No shield until wave 8.
|
// No shield until wave 4.
|
||||||
if (wave_ <= 8)
|
if (wave_ <= 3)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!target->shield_active) {
|
if (!target->shield_active) {
|
||||||
|
|
|
@ -22,7 +22,7 @@ using namespace eng;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr char kVersionStr[] = "Version 1.0.3";
|
constexpr char kVersionStr[] = "Version 1.0.2";
|
||||||
|
|
||||||
constexpr char kMenuOption[Menu::kOption_Max][10] = {"continue", "start",
|
constexpr char kMenuOption[Menu::kOption_Max][10] = {"continue", "start",
|
||||||
"credits", "exit"};
|
"credits", "exit"};
|
||||||
|
|
|
@ -1,112 +0,0 @@
|
||||||
#include "engine/audio/audio_bus.h"
|
|
||||||
|
|
||||||
#include "base/log.h"
|
|
||||||
#include "engine/audio/sinc_resampler.h"
|
|
||||||
#include "engine/engine.h"
|
|
||||||
|
|
||||||
using namespace base;
|
|
||||||
|
|
||||||
namespace eng {
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
std::array<std::unique_ptr<T[]>, 2> Deinterleave(size_t num_channels,
|
|
||||||
size_t num_samples,
|
|
||||||
float* input_buffer) {
|
|
||||||
std::array<std::unique_ptr<T[]>, 2> channels;
|
|
||||||
|
|
||||||
if (num_channels == 1) {
|
|
||||||
// Single channel.
|
|
||||||
if constexpr (std::is_same<float, T>::value) {
|
|
||||||
channels[0] = std::make_unique<T[]>(num_samples);
|
|
||||||
memcpy(channels[0].get(), input_buffer, num_samples * sizeof(float));
|
|
||||||
} else {
|
|
||||||
channels[0] = std::make_unique<T[]>(num_samples);
|
|
||||||
for (int i = 0; i < num_samples; ++i)
|
|
||||||
channels[0].get()[i] = static_cast<T>(input_buffer[i]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Deinterleave into separate channels.
|
|
||||||
channels[0] = std::make_unique<T[]>(num_samples);
|
|
||||||
channels[1] = std::make_unique<T[]>(num_samples);
|
|
||||||
for (size_t i = 0, j = 0; i < num_samples * 2; i += 2) {
|
|
||||||
channels[0].get()[j] = static_cast<T>(input_buffer[i]);
|
|
||||||
channels[1].get()[j++] = static_cast<T>(input_buffer[i + 1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return channels;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<SincResampler> CreateResampler(int src_samle_rate,
|
|
||||||
int dst_sample_rate,
|
|
||||||
size_t num_samples) {
|
|
||||||
const double io_ratio = static_cast<double>(src_samle_rate) /
|
|
||||||
static_cast<double>(dst_sample_rate);
|
|
||||||
auto resampler = std::make_unique<SincResampler>(io_ratio, num_samples);
|
|
||||||
resampler->PrimeWithSilence();
|
|
||||||
return resampler;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
AudioBus::AudioBus() = default;
|
|
||||||
AudioBus::~AudioBus() = default;
|
|
||||||
|
|
||||||
void AudioBus::SetAudioConfig(size_t num_channels, size_t sample_rate) {
|
|
||||||
num_channels_ = num_channels;
|
|
||||||
sample_rate_ = sample_rate;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AudioBus::FromInterleaved(std::unique_ptr<float[]> input_buffer,
|
|
||||||
size_t samples_per_channel) {
|
|
||||||
auto channels = Deinterleave<float>(num_channels_, samples_per_channel,
|
|
||||||
input_buffer.get());
|
|
||||||
|
|
||||||
size_t hw_sample_rate = Engine::Get().GetAudioHardwareSampleRate();
|
|
||||||
|
|
||||||
if (hw_sample_rate == sample_rate_) {
|
|
||||||
// No need for resmapling.
|
|
||||||
channel_data_[0] = std::move(channels[0]);
|
|
||||||
if (num_channels_ == 2)
|
|
||||||
channel_data_[1] = std::move(channels[1]);
|
|
||||||
samples_per_channel_ = samples_per_channel;
|
|
||||||
} else {
|
|
||||||
if (!resampler_[0]) {
|
|
||||||
for (size_t i = 0; i < num_channels_; ++i) {
|
|
||||||
resampler_[i] =
|
|
||||||
CreateResampler(sample_rate_, hw_sample_rate, samples_per_channel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t num_resampled_samples =
|
|
||||||
(size_t)(((float)hw_sample_rate / (float)sample_rate_) *
|
|
||||||
samples_per_channel);
|
|
||||||
DCHECK(num_resampled_samples <= (size_t)resampler_[0]->ChunkSize());
|
|
||||||
|
|
||||||
if (!channel_data_[0]) {
|
|
||||||
channel_data_[0] = std::make_unique<float[]>(num_resampled_samples);
|
|
||||||
if (num_channels_ == 2)
|
|
||||||
channel_data_[1] = std::make_unique<float[]>(num_resampled_samples);
|
|
||||||
}
|
|
||||||
samples_per_channel_ = num_resampled_samples;
|
|
||||||
|
|
||||||
// Resample to match the system sample rate.
|
|
||||||
for (size_t i = 0; i < num_channels_; ++i) {
|
|
||||||
resampler_[i]->Resample(num_resampled_samples, channel_data_[i].get(),
|
|
||||||
[&](int frames, float* destination) {
|
|
||||||
memcpy(destination, channels[i].get(),
|
|
||||||
frames * sizeof(float));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IsEndOfStream()) {
|
|
||||||
// We are done with the resampler.
|
|
||||||
for (size_t i = 0; i < num_channels_; ++i)
|
|
||||||
resampler_[i].reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace eng
|
|
|
@ -1,45 +0,0 @@
|
||||||
#ifndef ENGINE_AUDIO_AUDIO_BUS_H
|
|
||||||
#define ENGINE_AUDIO_AUDIO_BUS_H
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
namespace eng {
|
|
||||||
|
|
||||||
class SincResampler;
|
|
||||||
|
|
||||||
class AudioBus {
|
|
||||||
public:
|
|
||||||
AudioBus();
|
|
||||||
virtual ~AudioBus();
|
|
||||||
|
|
||||||
virtual void Stream(bool loop) = 0;
|
|
||||||
virtual void SwapBuffers() = 0;
|
|
||||||
virtual void ResetStream() = 0;
|
|
||||||
virtual bool IsEndOfStream() const = 0;
|
|
||||||
|
|
||||||
float* GetChannelData(int channel) const {
|
|
||||||
return channel_data_[channel].get();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t samples_per_channel() const { return samples_per_channel_; }
|
|
||||||
int sample_rate() const { return sample_rate_; }
|
|
||||||
int num_channels() const { return num_channels_; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void SetAudioConfig(size_t num_channels, size_t sample_rate);
|
|
||||||
|
|
||||||
void FromInterleaved(std::unique_ptr<float[]> input_buffer,
|
|
||||||
size_t samples_per_channel);
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::unique_ptr<float[]> channel_data_[2];
|
|
||||||
size_t samples_per_channel_ = 0;
|
|
||||||
size_t sample_rate_ = 0;
|
|
||||||
size_t num_channels_ = 0;
|
|
||||||
|
|
||||||
std::unique_ptr<SincResampler> resampler_[2];
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace eng
|
|
||||||
|
|
||||||
#endif // ENGINE_AUDIO_AUDIO_BUS_H
|
|
|
@ -5,7 +5,7 @@
|
||||||
#include "base/log.h"
|
#include "base/log.h"
|
||||||
#include "base/task_runner.h"
|
#include "base/task_runner.h"
|
||||||
#include "base/thread_pool.h"
|
#include "base/thread_pool.h"
|
||||||
#include "engine/audio/audio_bus.h"
|
#include "engine/sound.h"
|
||||||
|
|
||||||
#if defined(__ANDROID__)
|
#if defined(__ANDROID__)
|
||||||
#include "engine/audio/audio_sink_oboe.h"
|
#include "engine/audio/audio_sink_oboe.h"
|
||||||
|
@ -49,7 +49,7 @@ void AudioMixer::DestroyResource(uint64_t resource_id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioMixer::Play(uint64_t resource_id,
|
void AudioMixer::Play(uint64_t resource_id,
|
||||||
std::shared_ptr<AudioBus> sound,
|
std::shared_ptr<Sound> sound,
|
||||||
float amplitude,
|
float amplitude,
|
||||||
bool reset_pos) {
|
bool reset_pos) {
|
||||||
if (!audio_enabled_)
|
if (!audio_enabled_)
|
||||||
|
@ -76,8 +76,6 @@ void AudioMixer::Play(uint64_t resource_id,
|
||||||
it->second->src_index = 0;
|
it->second->src_index = 0;
|
||||||
it->second->accumulator = 0;
|
it->second->accumulator = 0;
|
||||||
sound->ResetStream();
|
sound->ResetStream();
|
||||||
} else if (it->second->src_index >= sound->samples_per_channel()) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
it->second->active = true;
|
it->second->active = true;
|
||||||
|
@ -163,7 +161,7 @@ void AudioMixer::Resume() {
|
||||||
audio_sink_->Resume();
|
audio_sink_->Resume();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AudioMixer::GetHardwareSampleRate() {
|
int AudioMixer::GetHardwareSampleRate() {
|
||||||
return audio_sink_->GetHardwareSampleRate();
|
return audio_sink_->GetHardwareSampleRate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,12 +182,11 @@ void AudioMixer::RenderAudio(float* output_buffer, size_t num_frames) {
|
||||||
if (flags & kStopped) {
|
if (flags & kStopped) {
|
||||||
marked_for_removal = true;
|
marked_for_removal = true;
|
||||||
} else {
|
} else {
|
||||||
const float* src[2] = {sound->GetChannelData(0),
|
const float* src[2] = {sound->GetBuffer(0), sound->GetBuffer(1)};
|
||||||
sound->GetChannelData(1)};
|
|
||||||
if (!src[1])
|
if (!src[1])
|
||||||
src[1] = src[0]; // mono.
|
src[1] = src[0]; // mono.
|
||||||
|
|
||||||
size_t num_samples = sound->samples_per_channel();
|
size_t num_samples = sound->GetNumSamples();
|
||||||
size_t src_index = it->get()->src_index;
|
size_t src_index = it->get()->src_index;
|
||||||
size_t step = it->get()->step.load(std::memory_order_relaxed);
|
size_t step = it->get()->step.load(std::memory_order_relaxed);
|
||||||
size_t accumulator = it->get()->accumulator;
|
size_t accumulator = it->get()->accumulator;
|
||||||
|
@ -198,59 +195,71 @@ void AudioMixer::RenderAudio(float* output_buffer, size_t num_frames) {
|
||||||
it->get()->amplitude_inc.load(std::memory_order_relaxed);
|
it->get()->amplitude_inc.load(std::memory_order_relaxed);
|
||||||
float max_amplitude =
|
float max_amplitude =
|
||||||
it->get()->max_amplitude.load(std::memory_order_relaxed);
|
it->get()->max_amplitude.load(std::memory_order_relaxed);
|
||||||
size_t channel_offset =
|
|
||||||
(flags & kSimulateStereo) ? sound->sample_rate() / 10 : 0;
|
|
||||||
|
|
||||||
DCHECK(num_samples > 0);
|
size_t channel_offset =
|
||||||
|
(flags & kSimulateStereo) && !sound->is_streaming_sound()
|
||||||
|
? sound->sample_rate() / 10
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
DCHECK(num_samples || sound->is_streaming_sound());
|
||||||
|
|
||||||
for (size_t i = 0; i < num_frames * kChannelCount;) {
|
for (size_t i = 0; i < num_frames * kChannelCount;) {
|
||||||
// Mix the 1st channel.
|
if (num_samples) {
|
||||||
output_buffer[i++] += src[0][src_index] * amplitude;
|
// Mix the 1st channel.
|
||||||
|
output_buffer[i++] += src[0][src_index] * amplitude;
|
||||||
|
|
||||||
// Mix the 2nd channel. Offset the source index for stereo simulation.
|
// Mix the 2nd channel. Offset the source index for stereo simulation.
|
||||||
size_t ind = channel_offset + src_index;
|
size_t ind = channel_offset + src_index;
|
||||||
if (ind < num_samples)
|
if (ind < num_samples)
|
||||||
output_buffer[i++] += src[1][ind] * amplitude;
|
output_buffer[i++] += src[1][ind] * amplitude;
|
||||||
else if (flags & kLoop)
|
else if (flags & kLoop)
|
||||||
output_buffer[i++] += src[1][ind % num_samples] * amplitude;
|
output_buffer[i++] += src[1][ind % num_samples] * amplitude;
|
||||||
else
|
else
|
||||||
i++;
|
i++;
|
||||||
|
|
||||||
// Apply amplitude modification.
|
// Apply amplitude modification.
|
||||||
amplitude += amplitude_inc;
|
amplitude += amplitude_inc;
|
||||||
if (amplitude <= 0) {
|
if (amplitude <= 0) {
|
||||||
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Advance source index. Apply basic resampling for variations.
|
||||||
|
accumulator += step;
|
||||||
|
src_index += accumulator / 100;
|
||||||
|
accumulator %= 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Advance source index. Apply basic resampling for variations.
|
|
||||||
accumulator += step;
|
|
||||||
src_index += accumulator / 100;
|
|
||||||
accumulator %= 100;
|
|
||||||
|
|
||||||
// Remove, loop or stream if the source data is consumed
|
// Remove, loop or stream if the source data is consumed
|
||||||
if (src_index >= num_samples) {
|
if (src_index >= num_samples) {
|
||||||
src_index %= num_samples;
|
if (num_samples)
|
||||||
|
src_index %= num_samples;
|
||||||
|
|
||||||
if (sound->IsEndOfStream()) {
|
if (!sound->is_streaming_sound()) {
|
||||||
marked_for_removal = true;
|
if (!(flags & kLoop)) {
|
||||||
break;
|
marked_for_removal = true;
|
||||||
}
|
break;
|
||||||
|
}
|
||||||
|
} else if (!it->get()->streaming_in_progress.load(
|
||||||
|
std::memory_order_acquire)) {
|
||||||
|
if (sound->eof()) {
|
||||||
|
DCHECK((flags & kLoop) == 0);
|
||||||
|
marked_for_removal = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (!it->get()->streaming_in_progress.load(
|
|
||||||
std::memory_order_acquire)) {
|
|
||||||
it->get()->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();
|
||||||
src[0] = sound->GetChannelData(0);
|
src[0] = sound->GetBuffer(0);
|
||||||
src[1] = sound->GetChannelData(1);
|
src[1] = sound->GetBuffer(1);
|
||||||
if (!src[1])
|
if (!src[1])
|
||||||
src[1] = src[0]; // mono.
|
src[1] = src[0]; // mono.
|
||||||
num_samples = sound->samples_per_channel();
|
num_samples = sound->GetNumSamples();
|
||||||
|
|
||||||
ThreadPool::Get().PostTask(
|
ThreadPool::Get().PostTask(
|
||||||
HERE,
|
HERE,
|
||||||
|
@ -275,7 +284,8 @@ void AudioMixer::RenderAudio(float* output_buffer, size_t num_frames) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto it = end_list_.begin(); it != end_list_.end();) {
|
for (auto it = end_list_.begin(); it != end_list_.end();) {
|
||||||
if (!it->get()->streaming_in_progress.load(std::memory_order_relaxed)) {
|
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(&AudioMixer::EndCallback, this, *it));
|
HERE, std::bind(&AudioMixer::EndCallback, this, *it));
|
||||||
it = end_list_.erase(it);
|
it = end_list_.erase(it);
|
||||||
|
|
|
@ -17,7 +17,7 @@ class TaskRunner;
|
||||||
namespace eng {
|
namespace eng {
|
||||||
|
|
||||||
class AudioSink;
|
class AudioSink;
|
||||||
class AudioBus;
|
class Sound;
|
||||||
|
|
||||||
// Mix and render audio with low overhead. A platform specific AudioSink
|
// Mix and render audio with low overhead. A platform specific AudioSink
|
||||||
// implementation is expected to periodically call RenderAudio() in a background
|
// implementation is expected to periodically call RenderAudio() in a background
|
||||||
|
@ -31,7 +31,7 @@ class AudioMixer : public AudioSinkDelegate {
|
||||||
void DestroyResource(uint64_t resource_id);
|
void DestroyResource(uint64_t resource_id);
|
||||||
|
|
||||||
void Play(uint64_t resource_id,
|
void Play(uint64_t resource_id,
|
||||||
std::shared_ptr<AudioBus> sound,
|
std::shared_ptr<Sound> sound,
|
||||||
float amplitude,
|
float amplitude,
|
||||||
bool reset_pos);
|
bool reset_pos);
|
||||||
void Stop(uint64_t resource_id);
|
void Stop(uint64_t resource_id);
|
||||||
|
@ -48,7 +48,7 @@ class AudioMixer : public AudioSinkDelegate {
|
||||||
void Suspend();
|
void Suspend();
|
||||||
void Resume();
|
void Resume();
|
||||||
|
|
||||||
size_t GetHardwareSampleRate();
|
int GetHardwareSampleRate();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum SampleFlags { kLoop = 1, kStopped = 2, kSimulateStereo = 4 };
|
enum SampleFlags { kLoop = 1, kStopped = 2, kSimulateStereo = 4 };
|
||||||
|
@ -62,7 +62,7 @@ class AudioMixer : public AudioSinkDelegate {
|
||||||
base::Closure restart_cb;
|
base::Closure restart_cb;
|
||||||
|
|
||||||
// Initialized by main thread, used by audio thread.
|
// Initialized by main thread, used by audio thread.
|
||||||
std::shared_ptr<AudioBus> sound;
|
std::shared_ptr<Sound> sound;
|
||||||
size_t src_index = 0;
|
size_t src_index = 0;
|
||||||
size_t accumulator = 0;
|
size_t accumulator = 0;
|
||||||
float amplitude = 1.0f;
|
float amplitude = 1.0f;
|
||||||
|
|
|
@ -15,7 +15,7 @@ class AudioSink {
|
||||||
virtual void Suspend() = 0;
|
virtual void Suspend() = 0;
|
||||||
virtual void Resume() = 0;
|
virtual void Resume() = 0;
|
||||||
|
|
||||||
virtual size_t GetHardwareSampleRate() = 0;
|
virtual int GetHardwareSampleRate() = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AudioSink(const AudioSink&) = delete;
|
AudioSink(const AudioSink&) = delete;
|
||||||
|
|
|
@ -152,7 +152,7 @@ void AudioSinkAlsa::Resume() {
|
||||||
suspend_audio_thread_.store(false, std::memory_order_relaxed);
|
suspend_audio_thread_.store(false, std::memory_order_relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AudioSinkAlsa::GetHardwareSampleRate() {
|
int AudioSinkAlsa::GetHardwareSampleRate() {
|
||||||
return sample_rate_;
|
return sample_rate_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ class AudioSinkAlsa final : public AudioSink {
|
||||||
void Suspend() final;
|
void Suspend() final;
|
||||||
void Resume() final;
|
void Resume() final;
|
||||||
|
|
||||||
size_t GetHardwareSampleRate() final;
|
int GetHardwareSampleRate() final;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Handle for the PCM device.
|
// Handle for the PCM device.
|
||||||
|
@ -33,7 +33,7 @@ class AudioSinkAlsa final : public AudioSink {
|
||||||
std::atomic<bool> suspend_audio_thread_{false};
|
std::atomic<bool> suspend_audio_thread_{false};
|
||||||
|
|
||||||
size_t num_channels_ = 0;
|
size_t num_channels_ = 0;
|
||||||
size_t sample_rate_ = 0;
|
int sample_rate_ = 0;
|
||||||
size_t period_size_ = 0;
|
size_t period_size_ = 0;
|
||||||
|
|
||||||
AudioSinkDelegate* delegate_ = nullptr;
|
AudioSinkDelegate* delegate_ = nullptr;
|
||||||
|
|
|
@ -29,7 +29,7 @@ void AudioSinkOboe::Resume() {
|
||||||
stream_->start();
|
stream_->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AudioSinkOboe::GetHardwareSampleRate() {
|
int AudioSinkOboe::GetHardwareSampleRate() {
|
||||||
return stream_->getSampleRate();
|
return stream_->getSampleRate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ class AudioSinkOboe final : public AudioSink {
|
||||||
void Suspend() final;
|
void Suspend() final;
|
||||||
void Resume() final;
|
void Resume() final;
|
||||||
|
|
||||||
size_t GetHardwareSampleRate() final;
|
int GetHardwareSampleRate() final;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class StreamCallback final : public oboe::AudioStreamCallback {
|
class StreamCallback final : public oboe::AudioStreamCallback {
|
||||||
|
|
|
@ -22,6 +22,8 @@
|
||||||
#include "engine/shader_source.h"
|
#include "engine/shader_source.h"
|
||||||
#include "third_party/texture_compressor/texture_compressor.h"
|
#include "third_party/texture_compressor/texture_compressor.h"
|
||||||
|
|
||||||
|
#define USE_VULKAN_RENDERER 1
|
||||||
|
|
||||||
using namespace base;
|
using namespace base;
|
||||||
|
|
||||||
namespace eng {
|
namespace eng {
|
||||||
|
@ -73,17 +75,6 @@ void Engine::Run() {
|
||||||
float frame_frac = 0.0f;
|
float frame_frac = 0.0f;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
TaskRunner::GetThreadLocalTaskRunner()->SingleConsumerRun();
|
|
||||||
|
|
||||||
platform_->Update();
|
|
||||||
if (platform_->should_exit())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!renderer_->IsInitialzed()) {
|
|
||||||
timer_.Reset();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Draw(frame_frac);
|
Draw(frame_frac);
|
||||||
|
|
||||||
// Accumulate time.
|
// Accumulate time.
|
||||||
|
@ -92,8 +83,13 @@ void Engine::Run() {
|
||||||
|
|
||||||
// Subdivide the frame time using fixed time steps.
|
// Subdivide the frame time using fixed time steps.
|
||||||
while (accumulator >= time_step_) {
|
while (accumulator >= time_step_) {
|
||||||
|
TaskRunner::GetThreadLocalTaskRunner()->SingleConsumerRun();
|
||||||
|
platform_->Update();
|
||||||
Update(time_step_);
|
Update(time_step_);
|
||||||
accumulator -= time_step_;
|
accumulator -= time_step_;
|
||||||
|
|
||||||
|
if (platform_->should_exit())
|
||||||
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Calculate frame fraction from remainder of the frame time.
|
// Calculate frame fraction from remainder of the frame time.
|
||||||
|
@ -474,7 +470,7 @@ const std::string& Engine::GetSharedDataPath() const {
|
||||||
return platform_->GetSharedDataPath();
|
return platform_->GetSharedDataPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t Engine::GetAudioHardwareSampleRate() {
|
int Engine::GetAudioHardwareSampleRate() {
|
||||||
return audio_mixer_->GetHardwareSampleRate();
|
return audio_mixer_->GetHardwareSampleRate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -523,13 +519,13 @@ void Engine::AddInputEvent(std::unique_ptr<InputEvent> event) {
|
||||||
case InputEvent::kDragEnd:
|
case InputEvent::kDragEnd:
|
||||||
if (((GetScreenSize() / 2) * 0.9f - event->GetVector()).Length() <=
|
if (((GetScreenSize() / 2) * 0.9f - event->GetVector()).Length() <=
|
||||||
0.25f) {
|
0.25f) {
|
||||||
SetStatsVisible(!stats_->IsVisible());
|
SetSatsVisible(!stats_->IsVisible());
|
||||||
// TODO: Enqueue DragCancel so we can consume this event.
|
// TODO: Enqueue DragCancel so we can consume this event.
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case InputEvent::kKeyPress:
|
case InputEvent::kKeyPress:
|
||||||
if (event->GetKeyPress() == 's') {
|
if (event->GetKeyPress() == 's') {
|
||||||
SetStatsVisible(!stats_->IsVisible());
|
SetSatsVisible(!stats_->IsVisible());
|
||||||
// Consume event.
|
// Consume event.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -623,7 +619,7 @@ void Engine::ContextLost() {
|
||||||
game_->ContextLost();
|
game_->ContextLost();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Engine::SetStatsVisible(bool visible) {
|
void Engine::SetSatsVisible(bool visible) {
|
||||||
stats_->SetVisible(visible);
|
stats_->SetVisible(visible);
|
||||||
if (visible)
|
if (visible)
|
||||||
stats_->Create("stats_tex");
|
stats_->Create("stats_tex");
|
||||||
|
|
|
@ -137,7 +137,7 @@ class Engine : public PlatformObserver {
|
||||||
|
|
||||||
const std::string& GetSharedDataPath() const;
|
const std::string& GetSharedDataPath() const;
|
||||||
|
|
||||||
size_t GetAudioHardwareSampleRate();
|
int GetAudioHardwareSampleRate();
|
||||||
|
|
||||||
bool IsMobile() const;
|
bool IsMobile() const;
|
||||||
|
|
||||||
|
@ -199,6 +199,7 @@ class Engine : public PlatformObserver {
|
||||||
int fps_ = 0;
|
int fps_ = 0;
|
||||||
|
|
||||||
float seconds_accumulated_ = 0.0f;
|
float seconds_accumulated_ = 0.0f;
|
||||||
|
|
||||||
float time_step_ = 1.0f / 60.0f;
|
float time_step_ = 1.0f / 60.0f;
|
||||||
size_t tick_ = 0;
|
size_t tick_ = 0;
|
||||||
|
|
||||||
|
@ -234,7 +235,7 @@ class Engine : public PlatformObserver {
|
||||||
|
|
||||||
void ContextLost();
|
void ContextLost();
|
||||||
|
|
||||||
void SetStatsVisible(bool visible);
|
void SetSatsVisible(bool visible);
|
||||||
std::unique_ptr<Image> PrintStats();
|
std::unique_ptr<Image> PrintStats();
|
||||||
|
|
||||||
Engine(const Engine&) = delete;
|
Engine(const Engine&) = delete;
|
||||||
|
|
|
@ -53,8 +53,6 @@ RendererOpenGL::~RendererOpenGL() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererOpenGL::Shutdown() {
|
void RendererOpenGL::Shutdown() {
|
||||||
is_initialized_ = false;
|
|
||||||
|
|
||||||
#ifdef THREADED_RENDERING
|
#ifdef THREADED_RENDERING
|
||||||
if (terminate_render_thread_)
|
if (terminate_render_thread_)
|
||||||
return;
|
return;
|
||||||
|
@ -315,8 +313,6 @@ bool RendererOpenGL::InitCommon() {
|
||||||
|
|
||||||
glClearColor(0, 0, 0, 1);
|
glClearColor(0, 0, 0, 1);
|
||||||
|
|
||||||
is_initialized_ = true;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,11 +40,9 @@ class RendererOpenGL final : public Renderer {
|
||||||
RendererOpenGL(base::Closure context_lost_cb);
|
RendererOpenGL(base::Closure context_lost_cb);
|
||||||
~RendererOpenGL() final;
|
~RendererOpenGL() final;
|
||||||
|
|
||||||
bool Initialize(Platform* platform) final;
|
virtual bool Initialize(Platform* platform) final;
|
||||||
void Shutdown() final;
|
void Shutdown() final;
|
||||||
|
|
||||||
bool IsInitialzed() const final { return is_initialized_; }
|
|
||||||
|
|
||||||
uint64_t CreateGeometry(std::unique_ptr<Mesh> mesh) final;
|
uint64_t CreateGeometry(std::unique_ptr<Mesh> mesh) final;
|
||||||
void DestroyGeometry(uint64_t resource_id) final;
|
void DestroyGeometry(uint64_t resource_id) final;
|
||||||
void Draw(uint64_t resource_id) final;
|
void Draw(uint64_t resource_id) final;
|
||||||
|
@ -126,8 +124,6 @@ class RendererOpenGL final : public Renderer {
|
||||||
bool vertex_array_objects_ = false;
|
bool vertex_array_objects_ = false;
|
||||||
bool npot_ = false;
|
bool npot_ = false;
|
||||||
|
|
||||||
bool is_initialized_ = false;
|
|
||||||
|
|
||||||
#ifdef THREADED_RENDERING
|
#ifdef THREADED_RENDERING
|
||||||
// Global commands are independent from frames and guaranteed to be processed.
|
// Global commands are independent from frames and guaranteed to be processed.
|
||||||
std::deque<std::unique_ptr<RenderCommand>> global_commands_;
|
std::deque<std::unique_ptr<RenderCommand>> global_commands_;
|
||||||
|
|
|
@ -26,8 +26,6 @@ class Renderer {
|
||||||
virtual bool Initialize(Platform* platform) = 0;
|
virtual bool Initialize(Platform* platform) = 0;
|
||||||
virtual void Shutdown() = 0;
|
virtual void Shutdown() = 0;
|
||||||
|
|
||||||
virtual bool IsInitialzed() const = 0;
|
|
||||||
|
|
||||||
virtual uint64_t CreateGeometry(std::unique_ptr<Mesh> mesh) = 0;
|
virtual uint64_t CreateGeometry(std::unique_ptr<Mesh> mesh) = 0;
|
||||||
virtual void DestroyGeometry(uint64_t resource_id) = 0;
|
virtual void DestroyGeometry(uint64_t resource_id) = 0;
|
||||||
virtual void Draw(uint64_t resource_id) = 0;
|
virtual void Draw(uint64_t resource_id) = 0;
|
||||||
|
|
|
@ -907,7 +907,7 @@ bool RendererVulkan::InitializeInternal() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// In this simple engine we use only one descriptor set layout that is for
|
// In this simple engine we use only one descriptor set layout that is for
|
||||||
// textures. We use push constants for everything else.
|
// textures. We use push contants for everything else.
|
||||||
VkDescriptorSetLayoutBinding ds_layout_binding;
|
VkDescriptorSetLayoutBinding ds_layout_binding;
|
||||||
ds_layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
ds_layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||||
ds_layout_binding.descriptorCount = 1;
|
ds_layout_binding.descriptorCount = 1;
|
||||||
|
|
|
@ -23,11 +23,9 @@ class RendererVulkan final : public Renderer {
|
||||||
RendererVulkan(base::Closure context_lost_cb);
|
RendererVulkan(base::Closure context_lost_cb);
|
||||||
~RendererVulkan() final;
|
~RendererVulkan() final;
|
||||||
|
|
||||||
bool Initialize(Platform* platform) final;
|
virtual bool Initialize(Platform* platform) final;
|
||||||
void Shutdown() final;
|
void Shutdown() final;
|
||||||
|
|
||||||
bool IsInitialzed() const final { return device_ != VK_NULL_HANDLE; }
|
|
||||||
|
|
||||||
uint64_t CreateGeometry(std::unique_ptr<Mesh> mesh) final;
|
uint64_t CreateGeometry(std::unique_ptr<Mesh> mesh) final;
|
||||||
void DestroyGeometry(uint64_t resource_id) final;
|
void DestroyGeometry(uint64_t resource_id) final;
|
||||||
void Draw(uint64_t resource_id) final;
|
void Draw(uint64_t resource_id) final;
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
#include "base/log.h"
|
#include "base/log.h"
|
||||||
|
#include "base/sinc_resampler.h"
|
||||||
#define MINIMP3_ONLY_MP3
|
#define MINIMP3_ONLY_MP3
|
||||||
#define MINIMP3_ONLY_SIMD
|
#define MINIMP3_ONLY_SIMD
|
||||||
#define MINIMP3_FLOAT_OUTPUT
|
#define MINIMP3_FLOAT_OUTPUT
|
||||||
|
@ -14,9 +15,52 @@
|
||||||
|
|
||||||
using namespace base;
|
using namespace base;
|
||||||
|
|
||||||
namespace eng {
|
namespace {
|
||||||
|
|
||||||
constexpr size_t kMaxSamplesPerChunk = MINIMP3_MAX_SAMPLES_PER_FRAME;
|
constexpr size_t kMaxSamplesPerChunk = MINIMP3_MAX_SAMPLES_PER_FRAME * 10;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::array<std::unique_ptr<T[]>, 2> Deinterleave(size_t num_channels,
|
||||||
|
size_t num_samples,
|
||||||
|
float* input_buffer) {
|
||||||
|
std::array<std::unique_ptr<T[]>, 2> channels;
|
||||||
|
|
||||||
|
if (num_channels == 1) {
|
||||||
|
// Single channel.
|
||||||
|
if constexpr (std::is_same<float, T>::value) {
|
||||||
|
channels[0] = std::make_unique<T[]>(num_samples);
|
||||||
|
memcpy(channels[0].get(), input_buffer, num_samples * sizeof(float));
|
||||||
|
} else {
|
||||||
|
channels[0] = std::make_unique<T[]>(num_samples);
|
||||||
|
for (int i = 0; i < num_samples; ++i)
|
||||||
|
channels[0].get()[i] = static_cast<T>(input_buffer[i]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Deinterleave into separate channels.
|
||||||
|
channels[0] = std::make_unique<T[]>(num_samples);
|
||||||
|
channels[1] = std::make_unique<T[]>(num_samples);
|
||||||
|
for (size_t i = 0, j = 0; i < num_samples * 2; i += 2) {
|
||||||
|
channels[0].get()[j] = static_cast<T>(input_buffer[i]);
|
||||||
|
channels[1].get()[j++] = static_cast<T>(input_buffer[i + 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return channels;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<SincResampler> CreateResampler(int src_samle_rate,
|
||||||
|
int dst_sample_rate,
|
||||||
|
size_t num_samples) {
|
||||||
|
const double io_ratio = static_cast<double>(src_samle_rate) /
|
||||||
|
static_cast<double>(dst_sample_rate);
|
||||||
|
auto resampler = std::make_unique<SincResampler>(io_ratio, num_samples);
|
||||||
|
resampler->PrimeWithSilence();
|
||||||
|
return resampler;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
namespace eng {
|
||||||
|
|
||||||
Sound::Sound() = default;
|
Sound::Sound() = default;
|
||||||
|
|
||||||
|
@ -48,98 +92,164 @@ bool Sound::Load(const std::string& file_name, bool stream) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG << (stream ? "Streaming " : "Loaded ") << file_name << ". "
|
is_streaming_sound_ = stream;
|
||||||
|
|
||||||
|
LOG << (is_streaming_sound_ ? "Streaming " : "Loaded ") << file_name << ". "
|
||||||
<< mp3_dec_->samples << " samples, " << mp3_dec_->info.channels
|
<< mp3_dec_->samples << " samples, " << mp3_dec_->info.channels
|
||||||
<< " channels, " << mp3_dec_->info.hz << " hz, "
|
<< " channels, " << mp3_dec_->info.hz << " hz, "
|
||||||
<< "layer " << mp3_dec_->info.layer << ", "
|
<< "layer " << mp3_dec_->info.layer << ", "
|
||||||
<< "avg_bitrate_kbps " << mp3_dec_->info.bitrate_kbps;
|
<< "avg_bitrate_kbps " << mp3_dec_->info.bitrate_kbps;
|
||||||
|
|
||||||
SetAudioConfig(mp3_dec_->info.channels, mp3_dec_->info.hz);
|
num_channels_ = mp3_dec_->info.channels;
|
||||||
|
sample_rate_ = mp3_dec_->info.hz;
|
||||||
|
num_samples_back_ = cur_sample_back_ = 0;
|
||||||
|
eof_ = false;
|
||||||
|
|
||||||
samples_per_channel_ = 0;
|
DCHECK(num_channels_ > 0 && num_channels_ <= 2);
|
||||||
eos_ = false;
|
|
||||||
|
|
||||||
DCHECK(mp3_dec_->info.channels > 0 && mp3_dec_->info.channels <= 2);
|
hw_sample_rate_ = Engine::Get().GetAudioHardwareSampleRate();
|
||||||
|
|
||||||
if (stream) {
|
if (is_streaming_sound_) {
|
||||||
// Fill up the buffer.
|
if (sample_rate_ != hw_sample_rate_) {
|
||||||
StreamInternal(kMaxSamplesPerChunk * mp3_dec_->info.channels, false);
|
for (int i = 0; i < mp3_dec_->info.channels; ++i) {
|
||||||
|
resampler_[i] =
|
||||||
|
CreateResampler(sample_rate_, hw_sample_rate_,
|
||||||
|
(int)kMaxSamplesPerChunk / mp3_dec_->info.channels);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill up buffers.
|
||||||
|
StreamInternal(kMaxSamplesPerChunk, false);
|
||||||
SwapBuffers();
|
SwapBuffers();
|
||||||
StreamInternal(kMaxSamplesPerChunk * mp3_dec_->info.channels, false);
|
StreamInternal(kMaxSamplesPerChunk, false);
|
||||||
|
|
||||||
// No need to stream if sample fits into the buffer.
|
// No need to stream if sample fits into the buffer.
|
||||||
if (eos_)
|
if (eof_)
|
||||||
stream = false;
|
is_streaming_sound_ = false;
|
||||||
} else {
|
} else {
|
||||||
|
if (sample_rate_ != hw_sample_rate_) {
|
||||||
|
for (int i = 0; i < mp3_dec_->info.channels; ++i) {
|
||||||
|
resampler_[i] =
|
||||||
|
CreateResampler(sample_rate_, hw_sample_rate_,
|
||||||
|
mp3_dec_->samples / mp3_dec_->info.channels);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Decode entire file.
|
// Decode entire file.
|
||||||
StreamInternal(mp3_dec_->samples, false);
|
StreamInternal(mp3_dec_->samples, false);
|
||||||
SwapBuffers();
|
SwapBuffers();
|
||||||
eos_ = true;
|
eof_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!stream) {
|
if (!is_streaming_sound_) {
|
||||||
// We are done with decoding.
|
// We are done with decoding.
|
||||||
encoded_data_.reset();
|
encoded_data_.reset();
|
||||||
|
for (int i = 0; i < mp3_dec_->info.channels; ++i)
|
||||||
|
resampler_[i].reset();
|
||||||
mp3dec_ex_close(mp3_dec_.get());
|
mp3dec_ex_close(mp3_dec_.get());
|
||||||
mp3_dec_.reset();
|
mp3_dec_.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
read_pos_ = 0;
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sound::Stream(bool loop) {
|
bool Sound::Stream(bool loop) {
|
||||||
DCHECK(mp3_dec_);
|
DCHECK(is_streaming_sound_);
|
||||||
StreamInternal(kMaxSamplesPerChunk * mp3_dec_->info.channels, loop);
|
|
||||||
|
return StreamInternal(kMaxSamplesPerChunk, loop);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sound::SwapBuffers() {
|
void Sound::SwapBuffers() {
|
||||||
FromInterleaved(std::move(interleaved_data_), samples_per_channel_);
|
front_buffer_[0].swap(back_buffer_[0]);
|
||||||
samples_per_channel_ = 0;
|
front_buffer_[1].swap(back_buffer_[1]);
|
||||||
|
|
||||||
|
cur_sample_front_ = cur_sample_back_;
|
||||||
|
num_samples_front_ = num_samples_back_;
|
||||||
|
num_samples_back_ = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sound::ResetStream() {
|
void Sound::ResetStream() {
|
||||||
if (mp3_dec_ && read_pos_ != 0) {
|
if (is_streaming_sound_ && cur_sample_front_ != 0) {
|
||||||
// Seek to 0 and stream.
|
// Seek to 0 and ivalidate decoded data.
|
||||||
mp3dec_ex_seek(mp3_dec_.get(), 0);
|
mp3dec_ex_seek(mp3_dec_.get(), 0);
|
||||||
eos_ = false;
|
eof_ = false;
|
||||||
StreamInternal(kMaxSamplesPerChunk * mp3_dec_->info.channels, false);
|
num_samples_back_ = num_samples_front_ = 0;
|
||||||
SwapBuffers();
|
cur_sample_front_ = cur_sample_back_ = 0;
|
||||||
StreamInternal(kMaxSamplesPerChunk * mp3_dec_->info.channels, false);
|
|
||||||
read_pos_ = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sound::StreamInternal(size_t num_samples, bool loop) {
|
float* Sound::GetBuffer(int channel) const {
|
||||||
|
return front_buffer_[channel].get();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Sound::StreamInternal(size_t num_samples, bool loop) {
|
||||||
auto buffer = std::make_unique<float[]>(num_samples);
|
auto buffer = std::make_unique<float[]>(num_samples);
|
||||||
size_t samples_read_per_channel = 0;
|
size_t samples_read_per_channel = 0;
|
||||||
|
|
||||||
read_pos_ = mp3_dec_->cur_sample;
|
cur_sample_back_ = mp3_dec_->cur_sample;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
size_t samples_read =
|
size_t samples_read =
|
||||||
mp3dec_ex_read(mp3_dec_.get(), buffer.get(), num_samples);
|
mp3dec_ex_read(mp3_dec_.get(), buffer.get(), num_samples);
|
||||||
if (samples_read != num_samples && mp3_dec_->last_error) {
|
if (samples_read != num_samples && mp3_dec_->last_error) {
|
||||||
LOG << "mp3 decode error: " << mp3_dec_->last_error;
|
LOG << "mp3 decode error: " << mp3_dec_->last_error;
|
||||||
break;
|
eof_ = true;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (samples_read == 0 && loop) {
|
samples_read_per_channel = samples_read / mp3_dec_->info.channels;
|
||||||
|
if (!samples_read_per_channel && loop) {
|
||||||
mp3dec_ex_seek(mp3_dec_.get(), 0);
|
mp3dec_ex_seek(mp3_dec_.get(), 0);
|
||||||
loop = false;
|
loop = false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
samples_read_per_channel = samples_read / mp3_dec_->info.channels;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (samples_read_per_channel > 0) {
|
if (samples_read_per_channel) {
|
||||||
interleaved_data_ = std::move(buffer);
|
Preprocess(std::move(buffer), samples_read_per_channel);
|
||||||
samples_per_channel_ = samples_read_per_channel;
|
|
||||||
} else {
|
} else {
|
||||||
samples_per_channel_ = 0;
|
num_samples_back_ = 0;
|
||||||
eos_ = true;
|
eof_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sound::Preprocess(std::unique_ptr<float[]> input_buffer,
|
||||||
|
size_t samples_per_channel) {
|
||||||
|
auto channels = Deinterleave<float>(num_channels_, samples_per_channel,
|
||||||
|
input_buffer.get());
|
||||||
|
|
||||||
|
if (hw_sample_rate_ == sample_rate_) {
|
||||||
|
// No need for resmapling.
|
||||||
|
back_buffer_[0] = std::move(channels[0]);
|
||||||
|
if (num_channels_ == 2)
|
||||||
|
back_buffer_[1] = std::move(channels[1]);
|
||||||
|
num_samples_back_ = samples_per_channel;
|
||||||
|
} else {
|
||||||
|
size_t num_resampled_samples = resampler_[0]->ChunkSize();
|
||||||
|
DCHECK(num_resampled_samples ==
|
||||||
|
(size_t)(((float)hw_sample_rate_ / (float)sample_rate_) *
|
||||||
|
samples_per_channel));
|
||||||
|
|
||||||
|
if (!back_buffer_[0]) {
|
||||||
|
if (max_samples_ < num_resampled_samples)
|
||||||
|
max_samples_ = num_resampled_samples;
|
||||||
|
back_buffer_[0] = std::make_unique<float[]>(max_samples_);
|
||||||
|
if (num_channels_ == 2)
|
||||||
|
back_buffer_[1] = std::make_unique<float[]>(max_samples_);
|
||||||
|
}
|
||||||
|
num_samples_back_ = num_resampled_samples;
|
||||||
|
|
||||||
|
// Resample to match the system sample rate.
|
||||||
|
for (size_t i = 0; i < num_channels_; ++i) {
|
||||||
|
resampler_[i]->Resample(num_resampled_samples, back_buffer_[i].get(),
|
||||||
|
[&](int frames, float* destination) {
|
||||||
|
memcpy(destination, channels[i].get(),
|
||||||
|
frames * sizeof(float));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,40 +5,73 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "engine/audio/audio_bus.h"
|
|
||||||
|
|
||||||
typedef struct mp3dec_ex mp3dec_ex_t;
|
typedef struct mp3dec_ex mp3dec_ex_t;
|
||||||
|
|
||||||
|
namespace base {
|
||||||
|
class SincResampler;
|
||||||
|
} // namespace base
|
||||||
|
|
||||||
namespace eng {
|
namespace eng {
|
||||||
|
|
||||||
// Class for streaming and non-streaming sound assets. Loads and decodes mp3
|
// Class for streaming and non-streaming sound assets. Loads and decodes mp3
|
||||||
// files. Resamples the decoded audio to match the system sample rate if
|
// files. Resamples the decoded audio to match the system sample rate if
|
||||||
// necessary. Non-streaming sounds Can be shared between multiple audio
|
// necessary. Non-streaming sounds Can be shared between multiple audio
|
||||||
// resources and played simultaneously.
|
// resources and played simultaneously.
|
||||||
class Sound final : public AudioBus {
|
class Sound {
|
||||||
public:
|
public:
|
||||||
Sound();
|
Sound();
|
||||||
~Sound() final;
|
~Sound();
|
||||||
|
|
||||||
bool Load(const std::string& file_name, bool stream);
|
bool Load(const std::string& file_name, bool stream);
|
||||||
|
|
||||||
// AudioBus interface
|
bool Stream(bool loop);
|
||||||
void Stream(bool loop) final;
|
|
||||||
void SwapBuffers() final;
|
void SwapBuffers();
|
||||||
void ResetStream() final;
|
|
||||||
bool IsEndOfStream() const final { return eos_; }
|
void ResetStream();
|
||||||
|
|
||||||
|
float* GetBuffer(int channel) const;
|
||||||
|
|
||||||
|
size_t GetNumSamples() const { return num_samples_front_; }
|
||||||
|
|
||||||
|
size_t num_channels() const { return num_channels_; }
|
||||||
|
int sample_rate() const { return sample_rate_; }
|
||||||
|
|
||||||
|
bool is_streaming_sound() const { return is_streaming_sound_; }
|
||||||
|
|
||||||
|
bool eof() const { return eof_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Buffer holding decoded audio.
|
// Buffer holding decoded audio.
|
||||||
std::unique_ptr<float[]> interleaved_data_;
|
std::unique_ptr<float[]> back_buffer_[2];
|
||||||
size_t samples_per_channel_ = 0;
|
std::unique_ptr<float[]> front_buffer_[2];
|
||||||
|
|
||||||
|
size_t num_samples_back_ = 0;
|
||||||
|
size_t num_samples_front_ = 0;
|
||||||
|
size_t max_samples_ = 0;
|
||||||
|
|
||||||
|
size_t cur_sample_front_ = 0;
|
||||||
|
size_t cur_sample_back_ = 0;
|
||||||
|
|
||||||
|
size_t num_channels_ = 0;
|
||||||
|
int sample_rate_ = 0;
|
||||||
|
|
||||||
|
int hw_sample_rate_ = 0;
|
||||||
|
|
||||||
std::unique_ptr<char[]> encoded_data_;
|
std::unique_ptr<char[]> encoded_data_;
|
||||||
std::unique_ptr<mp3dec_ex_t> mp3_dec_;
|
|
||||||
uint64_t read_pos_ = 0;
|
|
||||||
bool eos_ = false;
|
|
||||||
|
|
||||||
void StreamInternal(size_t num_samples, bool loop);
|
std::unique_ptr<mp3dec_ex_t> mp3_dec_;
|
||||||
|
|
||||||
|
std::unique_ptr<base::SincResampler> resampler_[2];
|
||||||
|
|
||||||
|
bool eof_ = false;
|
||||||
|
|
||||||
|
bool is_streaming_sound_ = false;
|
||||||
|
|
||||||
|
bool StreamInternal(size_t num_samples, bool loop);
|
||||||
|
|
||||||
|
void Preprocess(std::unique_ptr<float[]> input_buffer,
|
||||||
|
size_t samples_per_channel);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace eng
|
} // namespace eng
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
#include "base/interpolation.h"
|
#include "base/interpolation.h"
|
||||||
#include "base/log.h"
|
#include "base/log.h"
|
||||||
#include "engine/audio/audio_bus.h"
|
|
||||||
#include "engine/audio/audio_mixer.h"
|
#include "engine/audio/audio_mixer.h"
|
||||||
#include "engine/engine.h"
|
#include "engine/engine.h"
|
||||||
|
#include "engine/sound.h"
|
||||||
|
|
||||||
using namespace base;
|
using namespace base;
|
||||||
|
|
||||||
|
@ -17,10 +17,16 @@ SoundPlayer::~SoundPlayer() {
|
||||||
Engine::Get().GetAudioMixer()->DestroyResource(resource_id_);
|
Engine::Get().GetAudioMixer()->DestroyResource(resource_id_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SoundPlayer::SetSound(std::shared_ptr<AudioBus> sound) {
|
void SoundPlayer::SetSound(std::shared_ptr<Sound> sound) {
|
||||||
|
CHECK(!sound->is_streaming_sound()) << "Streaming sound cannot be shared.";
|
||||||
|
|
||||||
sound_ = sound;
|
sound_ = sound;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SoundPlayer::SetSound(std::unique_ptr<Sound> sound) {
|
||||||
|
sound_ = std::move(sound);
|
||||||
|
}
|
||||||
|
|
||||||
void SoundPlayer::Play(bool loop, float fade_in_duration) {
|
void SoundPlayer::Play(bool loop, float fade_in_duration) {
|
||||||
if (!sound_)
|
if (!sound_)
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -7,14 +7,15 @@
|
||||||
|
|
||||||
namespace eng {
|
namespace eng {
|
||||||
|
|
||||||
class AudioBus;
|
class Sound;
|
||||||
|
|
||||||
class SoundPlayer {
|
class SoundPlayer {
|
||||||
public:
|
public:
|
||||||
SoundPlayer();
|
SoundPlayer();
|
||||||
~SoundPlayer();
|
~SoundPlayer();
|
||||||
|
|
||||||
void SetSound(std::shared_ptr<AudioBus> sound);
|
void SetSound(std::shared_ptr<Sound> sound);
|
||||||
|
void SetSound(std::unique_ptr<Sound> sound);
|
||||||
|
|
||||||
void Play(bool loop, float fade_in_duration = 0);
|
void Play(bool loop, float fade_in_duration = 0);
|
||||||
|
|
||||||
|
@ -36,7 +37,7 @@ class SoundPlayer {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint64_t resource_id_ = 0;
|
uint64_t resource_id_ = 0;
|
||||||
std::shared_ptr<AudioBus> sound_;
|
std::shared_ptr<Sound> sound_;
|
||||||
|
|
||||||
float max_amplitude_ = 1.0f;
|
float max_amplitude_ = 1.0f;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue