summaryrefslogtreecommitdiffstats
path: root/src/audio_core
diff options
context:
space:
mode:
Diffstat (limited to 'src/audio_core')
-rw-r--r--src/audio_core/CMakeLists.txt7
-rw-r--r--src/audio_core/audio_core.h2
-rw-r--r--src/audio_core/hle/common.h9
-rw-r--r--src/audio_core/hle/dsp.h12
-rw-r--r--src/audio_core/interpolate.cpp85
-rw-r--r--src/audio_core/interpolate.h41
6 files changed, 143 insertions, 13 deletions
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index 869da5e83..a965af291 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -4,6 +4,7 @@ set(SRCS
hle/dsp.cpp
hle/filter.cpp
hle/pipe.cpp
+ interpolate.cpp
)
set(HEADERS
@@ -13,9 +14,13 @@ set(HEADERS
hle/dsp.h
hle/filter.h
hle/pipe.h
+ interpolate.h
sink.h
)
+include_directories(../../externals/soundtouch/include)
+
create_directory_groups(${SRCS} ${HEADERS})
-add_library(audio_core STATIC ${SRCS} ${HEADERS}) \ No newline at end of file
+add_library(audio_core STATIC ${SRCS} ${HEADERS})
+target_link_libraries(audio_core SoundTouch)
diff --git a/src/audio_core/audio_core.h b/src/audio_core/audio_core.h
index 64c330914..b349895ea 100644
--- a/src/audio_core/audio_core.h
+++ b/src/audio_core/audio_core.h
@@ -10,8 +10,6 @@ class VMManager;
namespace AudioCore {
-constexpr int num_sources = 24;
-constexpr int samples_per_frame = 160; ///< Samples per audio frame at native sample rate
constexpr int native_sample_rate = 32728; ///< 32kHz
/// Initialise Audio Core
diff --git a/src/audio_core/hle/common.h b/src/audio_core/hle/common.h
index 37d441eb2..7910f42ae 100644
--- a/src/audio_core/hle/common.h
+++ b/src/audio_core/hle/common.h
@@ -7,18 +7,19 @@
#include <algorithm>
#include <array>
-#include "audio_core/audio_core.h"
-
#include "common/common_types.h"
namespace DSP {
namespace HLE {
+constexpr int num_sources = 24;
+constexpr int samples_per_frame = 160; ///< Samples per audio frame at native sample rate
+
/// The final output to the speakers is stereo. Preprocessing output in Source is also stereo.
-using StereoFrame16 = std::array<std::array<s16, 2>, AudioCore::samples_per_frame>;
+using StereoFrame16 = std::array<std::array<s16, 2>, samples_per_frame>;
/// The DSP is quadraphonic internally.
-using QuadFrame32 = std::array<std::array<s32, 4>, AudioCore::samples_per_frame>;
+using QuadFrame32 = std::array<std::array<s32, 4>, samples_per_frame>;
/**
* This performs the filter operation defined by FilterT::ProcessSample on the frame in-place.
diff --git a/src/audio_core/hle/dsp.h b/src/audio_core/hle/dsp.h
index f94ec9467..f0f125284 100644
--- a/src/audio_core/hle/dsp.h
+++ b/src/audio_core/hle/dsp.h
@@ -8,7 +8,7 @@
#include <cstddef>
#include <type_traits>
-#include "audio_core/audio_core.h"
+#include "audio_core/hle/common.h"
#include "common/bit_field.h"
#include "common/common_funcs.h"
@@ -305,7 +305,7 @@ struct SourceConfiguration {
u16_le buffer_id;
};
- Configuration config[AudioCore::num_sources];
+ Configuration config[num_sources];
};
ASSERT_DSP_STRUCT(SourceConfiguration::Configuration, 192);
ASSERT_DSP_STRUCT(SourceConfiguration::Configuration::Buffer, 20);
@@ -320,7 +320,7 @@ struct SourceStatus {
INSERT_PADDING_DSPWORDS(1);
};
- Status status[AudioCore::num_sources];
+ Status status[num_sources];
};
ASSERT_DSP_STRUCT(SourceStatus::Status, 12);
@@ -413,7 +413,7 @@ ASSERT_DSP_STRUCT(DspConfiguration::ReverbEffect, 52);
struct AdpcmCoefficients {
/// Coefficients are signed fixed point with 11 fractional bits.
/// Each source has 16 coefficients associated with it.
- s16_le coeff[AudioCore::num_sources][16];
+ s16_le coeff[num_sources][16];
};
ASSERT_DSP_STRUCT(AdpcmCoefficients, 768);
@@ -427,7 +427,7 @@ ASSERT_DSP_STRUCT(DspStatus, 32);
/// Final mixed output in PCM16 stereo format, what you hear out of the speakers.
/// When the application writes to this region it has no effect.
struct FinalMixSamples {
- s16_le pcm16[2 * AudioCore::samples_per_frame];
+ s16_le pcm16[2 * samples_per_frame];
};
ASSERT_DSP_STRUCT(FinalMixSamples, 640);
@@ -437,7 +437,7 @@ ASSERT_DSP_STRUCT(FinalMixSamples, 640);
/// Values that exceed s16 range will be clipped by the DSP after further processing.
struct IntermediateMixSamples {
struct Samples {
- s32_le pcm32[4][AudioCore::samples_per_frame]; ///< Little-endian as opposed to DSP middle-endian.
+ s32_le pcm32[4][samples_per_frame]; ///< Little-endian as opposed to DSP middle-endian.
};
Samples mix1;
diff --git a/src/audio_core/interpolate.cpp b/src/audio_core/interpolate.cpp
new file mode 100644
index 000000000..fcd3aa066
--- /dev/null
+++ b/src/audio_core/interpolate.cpp
@@ -0,0 +1,85 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "audio_core/interpolate.h"
+
+#include "common/assert.h"
+#include "common/math_util.h"
+
+namespace AudioInterp {
+
+// Calculations are done in fixed point with 24 fractional bits.
+// (This is not verified. This was chosen for minimal error.)
+constexpr u64 scale_factor = 1 << 24;
+constexpr u64 scale_mask = scale_factor - 1;
+
+/// Here we step over the input in steps of rate_multiplier, until we consume all of the input.
+/// Three adjacent samples are passed to fn each step.
+template <typename Function>
+static StereoBuffer16 StepOverSamples(State& state, const StereoBuffer16& input, float rate_multiplier, Function fn) {
+ ASSERT(rate_multiplier > 0);
+
+ if (input.size() < 2)
+ return {};
+
+ StereoBuffer16 output;
+ output.reserve(static_cast<size_t>(input.size() / rate_multiplier));
+
+ u64 step_size = static_cast<u64>(rate_multiplier * scale_factor);
+
+ u64 fposition = 0;
+ const u64 max_fposition = input.size() * scale_factor;
+
+ while (fposition < 1 * scale_factor) {
+ u64 fraction = fposition & scale_mask;
+
+ output.push_back(fn(fraction, state.xn2, state.xn1, input[0]));
+
+ fposition += step_size;
+ }
+
+ while (fposition < 2 * scale_factor) {
+ u64 fraction = fposition & scale_mask;
+
+ output.push_back(fn(fraction, state.xn1, input[0], input[1]));
+
+ fposition += step_size;
+ }
+
+ while (fposition < max_fposition) {
+ u64 fraction = fposition & scale_mask;
+
+ size_t index = static_cast<size_t>(fposition / scale_factor);
+ output.push_back(fn(fraction, input[index - 2], input[index - 1], input[index]));
+
+ fposition += step_size;
+ }
+
+ state.xn2 = input[input.size() - 2];
+ state.xn1 = input[input.size() - 1];
+
+ return output;
+}
+
+StereoBuffer16 None(State& state, const StereoBuffer16& input, float rate_multiplier) {
+ return StepOverSamples(state, input, rate_multiplier, [](u64 fraction, const auto& x0, const auto& x1, const auto& x2) {
+ return x0;
+ });
+}
+
+StereoBuffer16 Linear(State& state, const StereoBuffer16& input, float rate_multiplier) {
+ // Note on accuracy: Some values that this produces are +/- 1 from the actual firmware.
+ return StepOverSamples(state, input, rate_multiplier, [](u64 fraction, const auto& x0, const auto& x1, const auto& x2) {
+ // This is a saturated subtraction. (Verified by black-box fuzzing.)
+ s64 delta0 = MathUtil::Clamp<s64>(x1[0] - x0[0], -32768, 32767);
+ s64 delta1 = MathUtil::Clamp<s64>(x1[1] - x0[1], -32768, 32767);
+
+ return std::array<s16, 2> {
+ static_cast<s16>(x0[0] + fraction * delta0 / scale_factor),
+ static_cast<s16>(x0[1] + fraction * delta1 / scale_factor)
+ };
+ });
+}
+
+} // namespace AudioInterp
diff --git a/src/audio_core/interpolate.h b/src/audio_core/interpolate.h
new file mode 100644
index 000000000..a4c0a453d
--- /dev/null
+++ b/src/audio_core/interpolate.h
@@ -0,0 +1,41 @@
+// Copyright 2016 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <vector>
+
+#include "common/common_types.h"
+
+namespace AudioInterp {
+
+/// A variable length buffer of signed PCM16 stereo samples.
+using StereoBuffer16 = std::vector<std::array<s16, 2>>;
+
+struct State {
+ // Two historical samples.
+ std::array<s16, 2> xn1 = {}; ///< x[n-1]
+ std::array<s16, 2> xn2 = {}; ///< x[n-2]
+};
+
+/**
+ * No interpolation. This is equivalent to a zero-order hold. There is a two-sample predelay.
+ * @param input Input buffer.
+ * @param rate_multiplier Stretch factor. Must be a positive non-zero value.
+ * rate_multiplier > 1.0 performs decimation and rate_multipler < 1.0 performs upsampling.
+ * @return The resampled audio buffer.
+ */
+StereoBuffer16 None(State& state, const StereoBuffer16& input, float rate_multiplier);
+
+/**
+ * Linear interpolation. This is equivalent to a first-order hold. There is a two-sample predelay.
+ * @param input Input buffer.
+ * @param rate_multiplier Stretch factor. Must be a positive non-zero value.
+ * rate_multiplier > 1.0 performs decimation and rate_multipler < 1.0 performs upsampling.
+ * @return The resampled audio buffer.
+ */
+StereoBuffer16 Linear(State& state, const StereoBuffer16& input, float rate_multiplier);
+
+} // namespace AudioInterp