summaryrefslogtreecommitdiff
path: root/src/audio_core
diff options
context:
space:
mode:
authorGravatar Yuri Kunde Schlesner2016-09-21 11:29:48 -0700
committerGravatar GitHub2016-09-21 11:29:48 -0700
commitd5d2ca8058a0f1c00ab7ca9fe2c058ba47546c0a (patch)
tree8a22ca73ff838f3f0090b29a548ae81087fc90ed /src/audio_core
parentREADME: Specify master branch for Travis CI badge (diff)
parentFix Travis clang-format check (diff)
downloadyuzu-d5d2ca8058a0f1c00ab7ca9fe2c058ba47546c0a.tar.gz
yuzu-d5d2ca8058a0f1c00ab7ca9fe2c058ba47546c0a.tar.xz
yuzu-d5d2ca8058a0f1c00ab7ca9fe2c058ba47546c0a.zip
Merge pull request #2086 from linkmauve/clang-format
Add clang-format as part of our {commit,travis}-time checks
Diffstat (limited to 'src/audio_core')
-rw-r--r--src/audio_core/audio_core.cpp20
-rw-r--r--src/audio_core/audio_core.h2
-rw-r--r--src/audio_core/codec.cpp27
-rw-r--r--src/audio_core/codec.h11
-rw-r--r--src/audio_core/hle/common.h12
-rw-r--r--src/audio_core/hle/dsp.cpp17
-rw-r--r--src/audio_core/hle/dsp.h148
-rw-r--r--src/audio_core/hle/filter.cpp10
-rw-r--r--src/audio_core/hle/filter.h14
-rw-r--r--src/audio_core/hle/mixers.cpp67
-rw-r--r--src/audio_core/hle/mixers.h10
-rw-r--r--src/audio_core/hle/pipe.cpp23
-rw-r--r--src/audio_core/hle/pipe.h12
-rw-r--r--src/audio_core/hle/source.cpp86
-rw-r--r--src/audio_core/hle/source.h19
-rw-r--r--src/audio_core/interpolate.cpp31
-rw-r--r--src/audio_core/interpolate.h7
-rw-r--r--src/audio_core/null_sink.h1
-rw-r--r--src/audio_core/sdl2_sink.cpp25
-rw-r--r--src/audio_core/sdl2_sink.h1
-rw-r--r--src/audio_core/sink.h9
-rw-r--r--src/audio_core/sink_details.cpp6
-rw-r--r--src/audio_core/time_stretch.cpp13
-rw-r--r--src/audio_core/time_stretch.h7
24 files changed, 324 insertions, 254 deletions
diff --git a/src/audio_core/audio_core.cpp b/src/audio_core/audio_core.cpp
index 8e19ec0c4..49260de7c 100644
--- a/src/audio_core/audio_core.cpp
+++ b/src/audio_core/audio_core.cpp
@@ -4,14 +4,12 @@
4 4
5#include <memory> 5#include <memory>
6#include <string> 6#include <string>
7
8#include "audio_core/audio_core.h" 7#include "audio_core/audio_core.h"
9#include "audio_core/hle/dsp.h" 8#include "audio_core/hle/dsp.h"
10#include "audio_core/hle/pipe.h" 9#include "audio_core/hle/pipe.h"
11#include "audio_core/null_sink.h" 10#include "audio_core/null_sink.h"
12#include "audio_core/sink.h" 11#include "audio_core/sink.h"
13#include "audio_core/sink_details.h" 12#include "audio_core/sink_details.h"
14
15#include "core/core_timing.h" 13#include "core/core_timing.h"
16#include "core/hle/kernel/vm_manager.h" 14#include "core/hle/kernel/vm_manager.h"
17#include "core/hle/service/dsp_dsp.h" 15#include "core/hle/service/dsp_dsp.h"
@@ -42,10 +40,18 @@ void Init() {
42} 40}
43 41
44void AddAddressSpace(Kernel::VMManager& address_space) { 42void AddAddressSpace(Kernel::VMManager& address_space) {
45 auto r0_vma = address_space.MapBackingMemory(DSP::HLE::region0_base, reinterpret_cast<u8*>(&DSP::HLE::g_regions[0]), sizeof(DSP::HLE::SharedMemory), Kernel::MemoryState::IO).MoveFrom(); 43 auto r0_vma = address_space
44 .MapBackingMemory(DSP::HLE::region0_base,
45 reinterpret_cast<u8*>(&DSP::HLE::g_regions[0]),
46 sizeof(DSP::HLE::SharedMemory), Kernel::MemoryState::IO)
47 .MoveFrom();
46 address_space.Reprotect(r0_vma, Kernel::VMAPermission::ReadWrite); 48 address_space.Reprotect(r0_vma, Kernel::VMAPermission::ReadWrite);
47 49
48 auto r1_vma = address_space.MapBackingMemory(DSP::HLE::region1_base, reinterpret_cast<u8*>(&DSP::HLE::g_regions[1]), sizeof(DSP::HLE::SharedMemory), Kernel::MemoryState::IO).MoveFrom(); 50 auto r1_vma = address_space
51 .MapBackingMemory(DSP::HLE::region1_base,
52 reinterpret_cast<u8*>(&DSP::HLE::g_regions[1]),
53 sizeof(DSP::HLE::SharedMemory), Kernel::MemoryState::IO)
54 .MoveFrom();
49 address_space.Reprotect(r1_vma, Kernel::VMAPermission::ReadWrite); 55 address_space.Reprotect(r1_vma, Kernel::VMAPermission::ReadWrite);
50} 56}
51 57
@@ -58,9 +64,9 @@ void SelectSink(std::string sink_id) {
58 return; 64 return;
59 } 65 }
60 66
61 auto iter = std::find_if(g_sink_details.begin(), g_sink_details.end(), [sink_id](const auto& sink_detail) { 67 auto iter =
62 return sink_detail.id == sink_id; 68 std::find_if(g_sink_details.begin(), g_sink_details.end(),
63 }); 69 [sink_id](const auto& sink_detail) { return sink_detail.id == sink_id; });
64 70
65 if (iter == g_sink_details.end()) { 71 if (iter == g_sink_details.end()) {
66 LOG_ERROR(Audio, "AudioCore::SelectSink given invalid sink_id"); 72 LOG_ERROR(Audio, "AudioCore::SelectSink given invalid sink_id");
diff --git a/src/audio_core/audio_core.h b/src/audio_core/audio_core.h
index 7e678aba5..0edf6dd15 100644
--- a/src/audio_core/audio_core.h
+++ b/src/audio_core/audio_core.h
@@ -12,7 +12,7 @@ class VMManager;
12 12
13namespace AudioCore { 13namespace AudioCore {
14 14
15constexpr int native_sample_rate = 32728; ///< 32kHz 15constexpr int native_sample_rate = 32728; ///< 32kHz
16 16
17/// Initialise Audio Core 17/// Initialise Audio Core
18void Init(); 18void Init();
diff --git a/src/audio_core/codec.cpp b/src/audio_core/codec.cpp
index 3e23323f1..7a3bd7eb3 100644
--- a/src/audio_core/codec.cpp
+++ b/src/audio_core/codec.cpp
@@ -6,31 +6,32 @@
6#include <cstddef> 6#include <cstddef>
7#include <cstring> 7#include <cstring>
8#include <vector> 8#include <vector>
9
10#include "audio_core/codec.h" 9#include "audio_core/codec.h"
11
12#include "common/assert.h" 10#include "common/assert.h"
13#include "common/common_types.h" 11#include "common/common_types.h"
14#include "common/math_util.h" 12#include "common/math_util.h"
15 13
16namespace Codec { 14namespace Codec {
17 15
18StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count, const std::array<s16, 16>& adpcm_coeff, ADPCMState& state) { 16StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count,
17 const std::array<s16, 16>& adpcm_coeff, ADPCMState& state) {
19 // GC-ADPCM with scale factor and variable coefficients. 18 // GC-ADPCM with scale factor and variable coefficients.
20 // Frames are 8 bytes long containing 14 samples each. 19 // Frames are 8 bytes long containing 14 samples each.
21 // Samples are 4 bits (one nibble) long. 20 // Samples are 4 bits (one nibble) long.
22 21
23 constexpr size_t FRAME_LEN = 8; 22 constexpr size_t FRAME_LEN = 8;
24 constexpr size_t SAMPLES_PER_FRAME = 14; 23 constexpr size_t SAMPLES_PER_FRAME = 14;
25 constexpr std::array<int, 16> SIGNED_NIBBLES {{ 0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1 }}; 24 constexpr std::array<int, 16> SIGNED_NIBBLES = {
25 {0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1}};
26 26
27 const size_t ret_size = sample_count % 2 == 0 ? sample_count : sample_count + 1; // Ensure multiple of two. 27 const size_t ret_size =
28 sample_count % 2 == 0 ? sample_count : sample_count + 1; // Ensure multiple of two.
28 StereoBuffer16 ret(ret_size); 29 StereoBuffer16 ret(ret_size);
29 30
30 int yn1 = state.yn1, 31 int yn1 = state.yn1, yn2 = state.yn2;
31 yn2 = state.yn2;
32 32
33 const size_t NUM_FRAMES = (sample_count + (SAMPLES_PER_FRAME - 1)) / SAMPLES_PER_FRAME; // Round up. 33 const size_t NUM_FRAMES =
34 (sample_count + (SAMPLES_PER_FRAME - 1)) / SAMPLES_PER_FRAME; // Round up.
34 for (size_t framei = 0; framei < NUM_FRAMES; framei++) { 35 for (size_t framei = 0; framei < NUM_FRAMES; framei++) {
35 const int frame_header = data[framei * FRAME_LEN]; 36 const int frame_header = data[framei * FRAME_LEN];
36 const int scale = 1 << (frame_header & 0xF); 37 const int scale = 1 << (frame_header & 0xF);
@@ -43,7 +44,8 @@ StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count, cons
43 // Decodes an audio sample. One nibble produces one sample. 44 // Decodes an audio sample. One nibble produces one sample.
44 const auto decode_sample = [&](const int nibble) -> s16 { 45 const auto decode_sample = [&](const int nibble) -> s16 {
45 const int xn = nibble * scale; 46 const int xn = nibble * scale;
46 // We first transform everything into 11 bit fixed point, perform the second order digital filter, then transform back. 47 // We first transform everything into 11 bit fixed point, perform the second order
48 // digital filter, then transform back.
47 // 0x400 == 0.5 in 11 bit fixed point. 49 // 0x400 == 0.5 in 11 bit fixed point.
48 // Filter: y[n] = x[n] + 0.5 + c1 * y[n-1] + c2 * y[n-2] 50 // Filter: y[n] = x[n] + 0.5 + c1 * y[n-1] + c2 * y[n-2]
49 int val = ((xn << 11) + 0x400 + coef1 * yn1 + coef2 * yn2) >> 11; 51 int val = ((xn << 11) + 0x400 + coef1 * yn1 + coef2 * yn2) >> 11;
@@ -82,7 +84,8 @@ static s16 SignExtendS8(u8 x) {
82 return static_cast<s16>(static_cast<s8>(x)); 84 return static_cast<s16>(static_cast<s8>(x));
83} 85}
84 86
85StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data, const size_t sample_count) { 87StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data,
88 const size_t sample_count) {
86 ASSERT(num_channels == 1 || num_channels == 2); 89 ASSERT(num_channels == 1 || num_channels == 2);
87 90
88 StereoBuffer16 ret(sample_count); 91 StereoBuffer16 ret(sample_count);
@@ -101,7 +104,8 @@ StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data, con
101 return ret; 104 return ret;
102} 105}
103 106
104StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data, const size_t sample_count) { 107StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data,
108 const size_t sample_count) {
105 ASSERT(num_channels == 1 || num_channels == 2); 109 ASSERT(num_channels == 1 || num_channels == 2);
106 110
107 StereoBuffer16 ret(sample_count); 111 StereoBuffer16 ret(sample_count);
@@ -118,5 +122,4 @@ StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data, co
118 122
119 return ret; 123 return ret;
120} 124}
121
122}; 125};
diff --git a/src/audio_core/codec.h b/src/audio_core/codec.h
index e695f2edc..2b0c395e6 100644
--- a/src/audio_core/codec.h
+++ b/src/audio_core/codec.h
@@ -6,7 +6,6 @@
6 6
7#include <array> 7#include <array>
8#include <vector> 8#include <vector>
9
10#include "common/common_types.h" 9#include "common/common_types.h"
11 10
12namespace Codec { 11namespace Codec {
@@ -29,7 +28,8 @@ struct ADPCMState {
29 * @param state ADPCM state, this is updated with new state 28 * @param state ADPCM state, this is updated with new state
30 * @return Decoded stereo signed PCM16 data, sample_count in length 29 * @return Decoded stereo signed PCM16 data, sample_count in length
31 */ 30 */
32StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count, const std::array<s16, 16>& adpcm_coeff, ADPCMState& state); 31StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count,
32 const std::array<s16, 16>& adpcm_coeff, ADPCMState& state);
33 33
34/** 34/**
35 * @param num_channels Number of channels 35 * @param num_channels Number of channels
@@ -37,7 +37,8 @@ StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count, cons
37 * @param sample_count Length of buffer in terms of number of samples 37 * @param sample_count Length of buffer in terms of number of samples
38 * @return Decoded stereo signed PCM16 data, sample_count in length 38 * @return Decoded stereo signed PCM16 data, sample_count in length
39 */ 39 */
40StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data, const size_t sample_count); 40StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data,
41 const size_t sample_count);
41 42
42/** 43/**
43 * @param num_channels Number of channels 44 * @param num_channels Number of channels
@@ -45,6 +46,6 @@ StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data, con
45 * @param sample_count Length of buffer in terms of number of samples 46 * @param sample_count Length of buffer in terms of number of samples
46 * @return Decoded stereo signed PCM16 data, sample_count in length 47 * @return Decoded stereo signed PCM16 data, sample_count in length
47 */ 48 */
48StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data, const size_t sample_count); 49StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data,
49 50 const size_t sample_count);
50}; 51};
diff --git a/src/audio_core/hle/common.h b/src/audio_core/hle/common.h
index 596b67eaf..7fbc3ad9a 100644
--- a/src/audio_core/hle/common.h
+++ b/src/audio_core/hle/common.h
@@ -6,30 +6,28 @@
6 6
7#include <algorithm> 7#include <algorithm>
8#include <array> 8#include <array>
9
10#include "common/common_types.h" 9#include "common/common_types.h"
11 10
12namespace DSP { 11namespace DSP {
13namespace HLE { 12namespace HLE {
14 13
15constexpr int num_sources = 24; 14constexpr int num_sources = 24;
16constexpr int samples_per_frame = 160; ///< Samples per audio frame at native sample rate 15constexpr int samples_per_frame = 160; ///< Samples per audio frame at native sample rate
17 16
18/// The final output to the speakers is stereo. Preprocessing output in Source is also stereo. 17/// The final output to the speakers is stereo. Preprocessing output in Source is also stereo.
19using StereoFrame16 = std::array<std::array<s16, 2>, samples_per_frame>; 18using StereoFrame16 = std::array<std::array<s16, 2>, samples_per_frame>;
20 19
21/// The DSP is quadraphonic internally. 20/// The DSP is quadraphonic internally.
22using QuadFrame32 = std::array<std::array<s32, 4>, samples_per_frame>; 21using QuadFrame32 = std::array<std::array<s32, 4>, samples_per_frame>;
23 22
24/** 23/**
25 * This performs the filter operation defined by FilterT::ProcessSample on the frame in-place. 24 * This performs the filter operation defined by FilterT::ProcessSample on the frame in-place.
26 * FilterT::ProcessSample is called sequentially on the samples. 25 * FilterT::ProcessSample is called sequentially on the samples.
27 */ 26 */
28template<typename FrameT, typename FilterT> 27template <typename FrameT, typename FilterT>
29void FilterFrame(FrameT& frame, FilterT& filter) { 28void FilterFrame(FrameT& frame, FilterT& filter) {
30 std::transform(frame.begin(), frame.end(), frame.begin(), [&filter](const auto& sample) { 29 std::transform(frame.begin(), frame.end(), frame.begin(),
31 return filter.ProcessSample(sample); 30 [&filter](const auto& sample) { return filter.ProcessSample(sample); });
32 });
33} 31}
34 32
35} // namespace HLE 33} // namespace HLE
diff --git a/src/audio_core/hle/dsp.cpp b/src/audio_core/hle/dsp.cpp
index 1420bf2dd..58690970a 100644
--- a/src/audio_core/hle/dsp.cpp
+++ b/src/audio_core/hle/dsp.cpp
@@ -4,7 +4,6 @@
4 4
5#include <array> 5#include <array>
6#include <memory> 6#include <memory>
7
8#include "audio_core/hle/dsp.h" 7#include "audio_core/hle/dsp.h"
9#include "audio_core/hle/mixers.h" 8#include "audio_core/hle/mixers.h"
10#include "audio_core/hle/pipe.h" 9#include "audio_core/hle/pipe.h"
@@ -47,10 +46,9 @@ static SharedMemory& WriteRegion() {
47// Audio processing and mixing 46// Audio processing and mixing
48 47
49static std::array<Source, num_sources> sources = { 48static std::array<Source, num_sources> sources = {
50 Source(0), Source(1), Source(2), Source(3), Source(4), Source(5), 49 Source(0), Source(1), Source(2), Source(3), Source(4), Source(5), Source(6), Source(7),
51 Source(6), Source(7), Source(8), Source(9), Source(10), Source(11), 50 Source(8), Source(9), Source(10), Source(11), Source(12), Source(13), Source(14), Source(15),
52 Source(12), Source(13), Source(14), Source(15), Source(16), Source(17), 51 Source(16), Source(17), Source(18), Source(19), Source(20), Source(21), Source(22), Source(23),
53 Source(18), Source(19), Source(20), Source(21), Source(22), Source(23)
54}; 52};
55static Mixers mixers; 53static Mixers mixers;
56 54
@@ -62,14 +60,16 @@ static StereoFrame16 GenerateCurrentFrame() {
62 60
63 // Generate intermediate mixes 61 // Generate intermediate mixes
64 for (size_t i = 0; i < num_sources; i++) { 62 for (size_t i = 0; i < num_sources; i++) {
65 write.source_statuses.status[i] = sources[i].Tick(read.source_configurations.config[i], read.adpcm_coefficients.coeff[i]); 63 write.source_statuses.status[i] =
64 sources[i].Tick(read.source_configurations.config[i], read.adpcm_coefficients.coeff[i]);
66 for (size_t mix = 0; mix < 3; mix++) { 65 for (size_t mix = 0; mix < 3; mix++) {
67 sources[i].MixInto(intermediate_mixes[mix], mix); 66 sources[i].MixInto(intermediate_mixes[mix], mix);
68 } 67 }
69 } 68 }
70 69
71 // Generate final mix 70 // Generate final mix
72 write.dsp_status = mixers.Tick(read.dsp_configuration, read.intermediate_mix_samples, write.intermediate_mix_samples, intermediate_mixes); 71 write.dsp_status = mixers.Tick(read.dsp_configuration, read.intermediate_mix_samples,
72 write.intermediate_mix_samples, intermediate_mixes);
73 73
74 StereoFrame16 output_frame = mixers.GetOutput(); 74 StereoFrame16 output_frame = mixers.GetOutput();
75 75
@@ -152,7 +152,8 @@ void Shutdown() {
152bool Tick() { 152bool Tick() {
153 StereoFrame16 current_frame = {}; 153 StereoFrame16 current_frame = {};
154 154
155 // TODO: Check dsp::DSP semaphore (which indicates emulated application has finished writing to shared memory region) 155 // TODO: Check dsp::DSP semaphore (which indicates emulated application has finished writing to
156 // shared memory region)
156 current_frame = GenerateCurrentFrame(); 157 current_frame = GenerateCurrentFrame();
157 158
158 OutputCurrentFrame(current_frame); 159 OutputCurrentFrame(current_frame);
diff --git a/src/audio_core/hle/dsp.h b/src/audio_core/hle/dsp.h
index 565f20b6f..0a0f60ac1 100644
--- a/src/audio_core/hle/dsp.h
+++ b/src/audio_core/hle/dsp.h
@@ -8,9 +8,7 @@
8#include <cstddef> 8#include <cstddef>
9#include <memory> 9#include <memory>
10#include <type_traits> 10#include <type_traits>
11
12#include "audio_core/hle/common.h" 11#include "audio_core/hle/common.h"
13
14#include "common/bit_field.h" 12#include "common/bit_field.h"
15#include "common/common_funcs.h" 13#include "common/common_funcs.h"
16#include "common/common_types.h" 14#include "common/common_types.h"
@@ -23,15 +21,15 @@ class Sink;
23namespace DSP { 21namespace DSP {
24namespace HLE { 22namespace HLE {
25 23
26// The application-accessible region of DSP memory consists of two parts. 24// The application-accessible region of DSP memory consists of two parts. Both are marked as IO and
27// Both are marked as IO and have Read/Write permissions. 25// have Read/Write permissions.
28// 26//
29// First Region: 0x1FF50000 (Size: 0x8000) 27// First Region: 0x1FF50000 (Size: 0x8000)
30// Second Region: 0x1FF70000 (Size: 0x8000) 28// Second Region: 0x1FF70000 (Size: 0x8000)
31// 29//
32// The DSP reads from each region alternately based on the frame counter for each region much like a 30// The DSP reads from each region alternately based on the frame counter for each region much like a
33// double-buffer. The frame counter is located as the very last u16 of each region and is incremented 31// double-buffer. The frame counter is located as the very last u16 of each region and is
34// each audio tick. 32// incremented each audio tick.
35 33
36constexpr VAddr region0_base = 0x1FF50000; 34constexpr VAddr region0_base = 0x1FF50000;
37constexpr VAddr region1_base = 0x1FF70000; 35constexpr VAddr region1_base = 0x1FF70000;
@@ -56,6 +54,7 @@ struct u32_dsp {
56 void operator=(u32 new_value) { 54 void operator=(u32 new_value) {
57 storage = Convert(new_value); 55 storage = Convert(new_value);
58 } 56 }
57
59private: 58private:
60 static constexpr u32 Convert(u32 value) { 59 static constexpr u32 Convert(u32 value) {
61 return (value << 16) | (value >> 16); 60 return (value << 16) | (value >> 16);
@@ -89,13 +88,13 @@ static_assert(std::is_trivially_copyable<u32_dsp>::value, "u32_dsp isn't trivial
89// #: This refers to the order in which they appear in the DspPipe::Audio DSP pipe. 88// #: This refers to the order in which they appear in the DspPipe::Audio DSP pipe.
90// See also: DSP::HLE::PipeRead. 89// See also: DSP::HLE::PipeRead.
91// 90//
92// Note that the above addresses do vary slightly between audio firmwares observed; the addresses are 91// Note that the above addresses do vary slightly between audio firmwares observed; the addresses
93// not fixed in stone. The addresses above are only an examplar; they're what this implementation 92// are not fixed in stone. The addresses above are only an examplar; they're what this
94// does and provides to applications. 93// implementation does and provides to applications.
95// 94//
96// Application requests the DSP service to convert DSP addresses into ARM11 virtual addresses using the 95// Application requests the DSP service to convert DSP addresses into ARM11 virtual addresses using
97// ConvertProcessAddressFromDspDram service call. Applications seem to derive the addresses for the 96// the ConvertProcessAddressFromDspDram service call. Applications seem to derive the addresses for
98// second region via: 97// the second region via:
99// second_region_dsp_addr = first_region_dsp_addr | 0x10000 98// second_region_dsp_addr = first_region_dsp_addr | 0x10000
100// 99//
101// Applications maintain most of its own audio state, the memory region is used mainly for 100// Applications maintain most of its own audio state, the memory region is used mainly for
@@ -103,21 +102,24 @@ static_assert(std::is_trivially_copyable<u32_dsp>::value, "u32_dsp isn't trivial
103// 102//
104// In the documentation below, filter and effect transfer functions are specified in the z domain. 103// In the documentation below, filter and effect transfer functions are specified in the z domain.
105// (If you are more familiar with the Laplace transform, z = exp(sT). The z domain is the digital 104// (If you are more familiar with the Laplace transform, z = exp(sT). The z domain is the digital
106// frequency domain, just like how the s domain is the analog frequency domain.) 105// frequency domain, just like how the s domain is the analog frequency domain.)
107 106
108#define INSERT_PADDING_DSPWORDS(num_words) INSERT_PADDING_BYTES(2 * (num_words)) 107#define INSERT_PADDING_DSPWORDS(num_words) INSERT_PADDING_BYTES(2 * (num_words))
109 108
110// GCC versions < 5.0 do not implement std::is_trivially_copyable. 109// GCC versions < 5.0 do not implement std::is_trivially_copyable.
111// Excluding MSVC because it has weird behaviour for std::is_trivially_copyable. 110// Excluding MSVC because it has weird behaviour for std::is_trivially_copyable.
112#if (__GNUC__ >= 5) || defined(__clang__) 111#if (__GNUC__ >= 5) || defined(__clang__)
113 #define ASSERT_DSP_STRUCT(name, size) \ 112#define ASSERT_DSP_STRUCT(name, size) \
114 static_assert(std::is_standard_layout<name>::value, "DSP structure " #name " doesn't use standard layout"); \ 113 static_assert(std::is_standard_layout<name>::value, \
115 static_assert(std::is_trivially_copyable<name>::value, "DSP structure " #name " isn't trivially copyable"); \ 114 "DSP structure " #name " doesn't use standard layout"); \
116 static_assert(sizeof(name) == (size), "Unexpected struct size for DSP structure " #name) 115 static_assert(std::is_trivially_copyable<name>::value, \
116 "DSP structure " #name " isn't trivially copyable"); \
117 static_assert(sizeof(name) == (size), "Unexpected struct size for DSP structure " #name)
117#else 118#else
118 #define ASSERT_DSP_STRUCT(name, size) \ 119#define ASSERT_DSP_STRUCT(name, size) \
119 static_assert(std::is_standard_layout<name>::value, "DSP structure " #name " doesn't use standard layout"); \ 120 static_assert(std::is_standard_layout<name>::value, \
120 static_assert(sizeof(name) == (size), "Unexpected struct size for DSP structure " #name) 121 "DSP structure " #name " doesn't use standard layout"); \
122 static_assert(sizeof(name) == (size), "Unexpected struct size for DSP structure " #name)
121#endif 123#endif
122 124
123struct SourceConfiguration { 125struct SourceConfiguration {
@@ -130,7 +132,8 @@ struct SourceConfiguration {
130 BitField<0, 1, u32_le> format_dirty; 132 BitField<0, 1, u32_le> format_dirty;
131 BitField<1, 1, u32_le> mono_or_stereo_dirty; 133 BitField<1, 1, u32_le> mono_or_stereo_dirty;
132 BitField<2, 1, u32_le> adpcm_coefficients_dirty; 134 BitField<2, 1, u32_le> adpcm_coefficients_dirty;
133 BitField<3, 1, u32_le> partial_embedded_buffer_dirty; ///< Tends to be set when a looped buffer is queued. 135 /// Tends to be set when a looped buffer is queued.
136 BitField<3, 1, u32_le> partial_embedded_buffer_dirty;
134 BitField<4, 1, u32_le> partial_reset_flag; 137 BitField<4, 1, u32_le> partial_reset_flag;
135 138
136 BitField<16, 1, u32_le> enable_dirty; 139 BitField<16, 1, u32_le> enable_dirty;
@@ -138,7 +141,8 @@ struct SourceConfiguration {
138 BitField<18, 1, u32_le> rate_multiplier_dirty; 141 BitField<18, 1, u32_le> rate_multiplier_dirty;
139 BitField<19, 1, u32_le> buffer_queue_dirty; 142 BitField<19, 1, u32_le> buffer_queue_dirty;
140 BitField<20, 1, u32_le> loop_related_dirty; 143 BitField<20, 1, u32_le> loop_related_dirty;
141 BitField<21, 1, u32_le> play_position_dirty; ///< Tends to also be set when embedded buffer is updated. 144 /// Tends to also be set when embedded buffer is updated.
145 BitField<21, 1, u32_le> play_position_dirty;
142 BitField<22, 1, u32_le> filters_enabled_dirty; 146 BitField<22, 1, u32_le> filters_enabled_dirty;
143 BitField<23, 1, u32_le> simple_filter_dirty; 147 BitField<23, 1, u32_le> simple_filter_dirty;
144 BitField<24, 1, u32_le> biquad_filter_dirty; 148 BitField<24, 1, u32_le> biquad_filter_dirty;
@@ -153,9 +157,9 @@ struct SourceConfiguration {
153 // Gain control 157 // Gain control
154 158
155 /** 159 /**
156 * Gain is between 0.0-1.0. This determines how much will this source appear on 160 * Gain is between 0.0-1.0. This determines how much will this source appear on each of the
157 * each of the 12 channels that feed into the intermediate mixers. 161 * 12 channels that feed into the intermediate mixers. Each of the three intermediate mixers
158 * Each of the three intermediate mixers is fed two left and two right channels. 162 * is fed two left and two right channels.
159 */ 163 */
160 float_le gain[3][4]; 164 float_le gain[3][4];
161 165
@@ -167,7 +171,7 @@ struct SourceConfiguration {
167 enum class InterpolationMode : u8 { 171 enum class InterpolationMode : u8 {
168 Polyphase = 0, 172 Polyphase = 0,
169 Linear = 1, 173 Linear = 1,
170 None = 2 174 None = 2,
171 }; 175 };
172 176
173 InterpolationMode interpolation_mode; 177 InterpolationMode interpolation_mode;
@@ -191,8 +195,8 @@ struct SourceConfiguration {
191 * This is a normalised biquad filter (second-order). 195 * This is a normalised biquad filter (second-order).
192 * The transfer function of this filter is: 196 * The transfer function of this filter is:
193 * H(z) = (b0 + b1 z^-1 + b2 z^-2) / (1 - a1 z^-1 - a2 z^-2) 197 * H(z) = (b0 + b1 z^-1 + b2 z^-2) / (1 - a1 z^-1 - a2 z^-2)
194 * Nintendo chose to negate the feedbackward coefficients. This differs from standard notation 198 * Nintendo chose to negate the feedbackward coefficients. This differs from standard
195 * as in: https://ccrma.stanford.edu/~jos/filters/Direct_Form_I.html 199 * notation as in: https://ccrma.stanford.edu/~jos/filters/Direct_Form_I.html
196 * Values are signed fixed point with 14 fractional bits. 200 * Values are signed fixed point with 14 fractional bits.
197 */ 201 */
198 struct BiquadFilter { 202 struct BiquadFilter {
@@ -239,23 +243,24 @@ struct SourceConfiguration {
239 /// Is a looping buffer. 243 /// Is a looping buffer.
240 u8 is_looping; 244 u8 is_looping;
241 245
242 /// This value is shown in SourceStatus::previous_buffer_id when this buffer has finished. 246 /// This value is shown in SourceStatus::previous_buffer_id when this buffer has
243 /// This allows the emulated application to tell what buffer is currently playing 247 /// finished. This allows the emulated application to tell what buffer is currently
248 /// playing.
244 u16_le buffer_id; 249 u16_le buffer_id;
245 250
246 INSERT_PADDING_DSPWORDS(1); 251 INSERT_PADDING_DSPWORDS(1);
247 }; 252 };
248 253
249 u16_le buffers_dirty; ///< Bitmap indicating which buffers are dirty (bit i -> buffers[i]) 254 u16_le buffers_dirty; ///< Bitmap indicating which buffers are dirty (bit i -> buffers[i])
250 Buffer buffers[4]; ///< Queued Buffers 255 Buffer buffers[4]; ///< Queued Buffers
251 256
252 // Playback controls 257 // Playback controls
253 258
254 u32_dsp loop_related; 259 u32_dsp loop_related;
255 u8 enable; 260 u8 enable;
256 INSERT_PADDING_BYTES(1); 261 INSERT_PADDING_BYTES(1);
257 u16_le sync; ///< Application-side sync (See also: SourceStatus::sync) 262 u16_le sync; ///< Application-side sync (See also: SourceStatus::sync)
258 u32_dsp play_position; ///< Position. (Units: number of samples) 263 u32_dsp play_position; ///< Position. (Units: number of samples)
259 INSERT_PADDING_DSPWORDS(2); 264 INSERT_PADDING_DSPWORDS(2);
260 265
261 // Embedded Buffer 266 // Embedded Buffer
@@ -270,13 +275,13 @@ struct SourceConfiguration {
270 275
271 enum class MonoOrStereo : u16_le { 276 enum class MonoOrStereo : u16_le {
272 Mono = 1, 277 Mono = 1,
273 Stereo = 2 278 Stereo = 2,
274 }; 279 };
275 280
276 enum class Format : u16_le { 281 enum class Format : u16_le {
277 PCM8 = 0, 282 PCM8 = 0,
278 PCM16 = 1, 283 PCM16 = 1,
279 ADPCM = 2 284 ADPCM = 2,
280 }; 285 };
281 286
282 union { 287 union {
@@ -299,10 +304,11 @@ struct SourceConfiguration {
299 union { 304 union {
300 u16_le flags2_raw; 305 u16_le flags2_raw;
301 BitField<0, 1, u16_le> adpcm_dirty; ///< Has the ADPCM info above been changed? 306 BitField<0, 1, u16_le> adpcm_dirty; ///< Has the ADPCM info above been changed?
302 BitField<1, 1, u16_le> is_looping; ///< Is this a looping buffer? 307 BitField<1, 1, u16_le> is_looping; ///< Is this a looping buffer?
303 }; 308 };
304 309
305 /// Buffer id of embedded buffer (used as a buffer id in SourceStatus to reference this buffer). 310 /// Buffer id of embedded buffer (used as a buffer id in SourceStatus to reference this
311 /// buffer).
306 u16_le buffer_id; 312 u16_le buffer_id;
307 }; 313 };
308 314
@@ -313,11 +319,11 @@ ASSERT_DSP_STRUCT(SourceConfiguration::Configuration::Buffer, 20);
313 319
314struct SourceStatus { 320struct SourceStatus {
315 struct Status { 321 struct Status {
316 u8 is_enabled; ///< Is this channel enabled? (Doesn't have to be playing anything.) 322 u8 is_enabled; ///< Is this channel enabled? (Doesn't have to be playing anything.)
317 u8 current_buffer_id_dirty; ///< Non-zero when current_buffer_id changes 323 u8 current_buffer_id_dirty; ///< Non-zero when current_buffer_id changes
318 u16_le sync; ///< Is set by the DSP to the value of SourceConfiguration::sync 324 u16_le sync; ///< Is set by the DSP to the value of SourceConfiguration::sync
319 u32_dsp buffer_position; ///< Number of samples into the current buffer 325 u32_dsp buffer_position; ///< Number of samples into the current buffer
320 u16_le current_buffer_id; ///< Updated when a buffer finishes playing 326 u16_le current_buffer_id; ///< Updated when a buffer finishes playing
321 INSERT_PADDING_DSPWORDS(1); 327 INSERT_PADDING_DSPWORDS(1);
322 }; 328 };
323 329
@@ -347,7 +353,8 @@ struct DspConfiguration {
347 BitField<28, 1, u32_le> headphones_connected_dirty; 353 BitField<28, 1, u32_le> headphones_connected_dirty;
348 }; 354 };
349 355
350 /// The DSP has three intermediate audio mixers. This controls the volume level (0.0-1.0) for each at the final mixer 356 /// The DSP has three intermediate audio mixers. This controls the volume level (0.0-1.0) for
357 /// each at the final mixer.
351 float_le volume[3]; 358 float_le volume[3];
352 359
353 INSERT_PADDING_DSPWORDS(3); 360 INSERT_PADDING_DSPWORDS(3);
@@ -355,7 +362,7 @@ struct DspConfiguration {
355 enum class OutputFormat : u16_le { 362 enum class OutputFormat : u16_le {
356 Mono = 0, 363 Mono = 0,
357 Stereo = 1, 364 Stereo = 1,
358 Surround = 2 365 Surround = 2,
359 }; 366 };
360 367
361 OutputFormat output_format; 368 OutputFormat output_format;
@@ -388,8 +395,10 @@ struct DspConfiguration {
388 u16_le enable; 395 u16_le enable;
389 INSERT_PADDING_DSPWORDS(1); 396 INSERT_PADDING_DSPWORDS(1);
390 u16_le outputs; 397 u16_le outputs;
391 u32_dsp work_buffer_address; ///< The application allocates a block of memory for the DSP to use as a work buffer. 398 /// The application allocates a block of memory for the DSP to use as a work buffer.
392 u16_le frame_count; ///< Frames to delay by 399 u32_dsp work_buffer_address;
400 /// Frames to delay by
401 u16_le frame_count;
393 402
394 // Coefficients 403 // Coefficients
395 s16_le g; ///< Fixed point with 7 fractional bits 404 s16_le g; ///< Fixed point with 7 fractional bits
@@ -506,21 +515,36 @@ ASSERT_DSP_STRUCT(SharedMemory, 0x8000);
506extern std::array<SharedMemory, 2> g_regions; 515extern std::array<SharedMemory, 2> g_regions;
507 516
508// Structures must have an offset that is a multiple of two. 517// Structures must have an offset that is a multiple of two.
509static_assert(offsetof(SharedMemory, frame_counter) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); 518static_assert(offsetof(SharedMemory, frame_counter) % 2 == 0,
510static_assert(offsetof(SharedMemory, source_configurations) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); 519 "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
511static_assert(offsetof(SharedMemory, source_statuses) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); 520static_assert(offsetof(SharedMemory, source_configurations) % 2 == 0,
512static_assert(offsetof(SharedMemory, adpcm_coefficients) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); 521 "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
513static_assert(offsetof(SharedMemory, dsp_configuration) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); 522static_assert(offsetof(SharedMemory, source_statuses) % 2 == 0,
514static_assert(offsetof(SharedMemory, dsp_status) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); 523 "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
515static_assert(offsetof(SharedMemory, final_samples) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); 524static_assert(offsetof(SharedMemory, adpcm_coefficients) % 2 == 0,
516static_assert(offsetof(SharedMemory, intermediate_mix_samples) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); 525 "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
517static_assert(offsetof(SharedMemory, compressor) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); 526static_assert(offsetof(SharedMemory, dsp_configuration) % 2 == 0,
518static_assert(offsetof(SharedMemory, dsp_debug) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); 527 "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
519static_assert(offsetof(SharedMemory, unknown10) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); 528static_assert(offsetof(SharedMemory, dsp_status) % 2 == 0,
520static_assert(offsetof(SharedMemory, unknown11) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); 529 "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
521static_assert(offsetof(SharedMemory, unknown12) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); 530static_assert(offsetof(SharedMemory, final_samples) % 2 == 0,
522static_assert(offsetof(SharedMemory, unknown13) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); 531 "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
523static_assert(offsetof(SharedMemory, unknown14) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); 532static_assert(offsetof(SharedMemory, intermediate_mix_samples) % 2 == 0,
533 "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
534static_assert(offsetof(SharedMemory, compressor) % 2 == 0,
535 "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
536static_assert(offsetof(SharedMemory, dsp_debug) % 2 == 0,
537 "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
538static_assert(offsetof(SharedMemory, unknown10) % 2 == 0,
539 "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
540static_assert(offsetof(SharedMemory, unknown11) % 2 == 0,
541 "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
542static_assert(offsetof(SharedMemory, unknown12) % 2 == 0,
543 "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
544static_assert(offsetof(SharedMemory, unknown13) % 2 == 0,
545 "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
546static_assert(offsetof(SharedMemory, unknown14) % 2 == 0,
547 "Structures in DSP::HLE::SharedMemory must be 2-byte aligned");
524 548
525#undef INSERT_PADDING_DSPWORDS 549#undef INSERT_PADDING_DSPWORDS
526#undef ASSERT_DSP_STRUCT 550#undef ASSERT_DSP_STRUCT
diff --git a/src/audio_core/hle/filter.cpp b/src/audio_core/hle/filter.cpp
index 2c65ef026..b24a79b89 100644
--- a/src/audio_core/hle/filter.cpp
+++ b/src/audio_core/hle/filter.cpp
@@ -4,11 +4,9 @@
4 4
5#include <array> 5#include <array>
6#include <cstddef> 6#include <cstddef>
7
8#include "audio_core/hle/common.h" 7#include "audio_core/hle/common.h"
9#include "audio_core/hle/dsp.h" 8#include "audio_core/hle/dsp.h"
10#include "audio_core/hle/filter.h" 9#include "audio_core/hle/filter.h"
11
12#include "common/common_types.h" 10#include "common/common_types.h"
13#include "common/math_util.h" 11#include "common/math_util.h"
14 12
@@ -59,7 +57,9 @@ void SourceFilters::SimpleFilter::Reset() {
59 b0 = 1 << 15; 57 b0 = 1 << 15;
60} 58}
61 59
62void SourceFilters::SimpleFilter::Configure(SourceConfiguration::Configuration::SimpleFilter config) { 60void SourceFilters::SimpleFilter::Configure(
61 SourceConfiguration::Configuration::SimpleFilter config) {
62
63 a1 = config.a1; 63 a1 = config.a1;
64 b0 = config.b0; 64 b0 = config.b0;
65} 65}
@@ -88,7 +88,9 @@ void SourceFilters::BiquadFilter::Reset() {
88 b0 = 1 << 14; 88 b0 = 1 << 14;
89} 89}
90 90
91void SourceFilters::BiquadFilter::Configure(SourceConfiguration::Configuration::BiquadFilter config) { 91void SourceFilters::BiquadFilter::Configure(
92 SourceConfiguration::Configuration::BiquadFilter config) {
93
92 a1 = config.a1; 94 a1 = config.a1;
93 a2 = config.a2; 95 a2 = config.a2;
94 b0 = config.b0; 96 b0 = config.b0;
diff --git a/src/audio_core/hle/filter.h b/src/audio_core/hle/filter.h
index 43d2035cd..4281a5898 100644
--- a/src/audio_core/hle/filter.h
+++ b/src/audio_core/hle/filter.h
@@ -5,10 +5,8 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8
9#include "audio_core/hle/common.h" 8#include "audio_core/hle/common.h"
10#include "audio_core/hle/dsp.h" 9#include "audio_core/hle/dsp.h"
11
12#include "common/common_types.h" 10#include "common/common_types.h"
13 11
14namespace DSP { 12namespace DSP {
@@ -17,7 +15,9 @@ namespace HLE {
17/// Preprocessing filters. There is an independent set of filters for each Source. 15/// Preprocessing filters. There is an independent set of filters for each Source.
18class SourceFilters final { 16class SourceFilters final {
19public: 17public:
20 SourceFilters() { Reset(); } 18 SourceFilters() {
19 Reset();
20 }
21 21
22 /// Reset internal state. 22 /// Reset internal state.
23 void Reset(); 23 void Reset();
@@ -54,7 +54,9 @@ private:
54 bool biquad_filter_enabled; 54 bool biquad_filter_enabled;
55 55
56 struct SimpleFilter { 56 struct SimpleFilter {
57 SimpleFilter() { Reset(); } 57 SimpleFilter() {
58 Reset();
59 }
58 60
59 /// Resets internal state. 61 /// Resets internal state.
60 void Reset(); 62 void Reset();
@@ -80,7 +82,9 @@ private:
80 } simple_filter; 82 } simple_filter;
81 83
82 struct BiquadFilter { 84 struct BiquadFilter {
83 BiquadFilter() { Reset(); } 85 BiquadFilter() {
86 Reset();
87 }
84 88
85 /// Resets internal state. 89 /// Resets internal state.
86 void Reset(); 90 void Reset();
diff --git a/src/audio_core/hle/mixers.cpp b/src/audio_core/hle/mixers.cpp
index 18335f7f0..6cc81dfca 100644
--- a/src/audio_core/hle/mixers.cpp
+++ b/src/audio_core/hle/mixers.cpp
@@ -7,7 +7,6 @@
7#include "audio_core/hle/common.h" 7#include "audio_core/hle/common.h"
8#include "audio_core/hle/dsp.h" 8#include "audio_core/hle/dsp.h"
9#include "audio_core/hle/mixers.h" 9#include "audio_core/hle/mixers.h"
10
11#include "common/assert.h" 10#include "common/assert.h"
12#include "common/logging/log.h" 11#include "common/logging/log.h"
13#include "common/math_util.h" 12#include "common/math_util.h"
@@ -20,11 +19,9 @@ void Mixers::Reset() {
20 state = {}; 19 state = {};
21} 20}
22 21
23DspStatus Mixers::Tick(DspConfiguration& config, 22DspStatus Mixers::Tick(DspConfiguration& config, const IntermediateMixSamples& read_samples,
24 const IntermediateMixSamples& read_samples, 23 IntermediateMixSamples& write_samples,
25 IntermediateMixSamples& write_samples, 24 const std::array<QuadFrame32, 3>& input) {
26 const std::array<QuadFrame32, 3>& input)
27{
28 ParseConfig(config); 25 ParseConfig(config);
29 26
30 AuxReturn(read_samples); 27 AuxReturn(read_samples);
@@ -73,13 +70,14 @@ void Mixers::ParseConfig(DspConfiguration& config) {
73 if (config.output_format_dirty) { 70 if (config.output_format_dirty) {
74 config.output_format_dirty.Assign(0); 71 config.output_format_dirty.Assign(0);
75 state.output_format = config.output_format; 72 state.output_format = config.output_format;
76 LOG_TRACE(Audio_DSP, "mixers output_format = %zu", static_cast<size_t>(config.output_format)); 73 LOG_TRACE(Audio_DSP, "mixers output_format = %zu",
74 static_cast<size_t>(config.output_format));
77 } 75 }
78 76
79 if (config.headphones_connected_dirty) { 77 if (config.headphones_connected_dirty) {
80 config.headphones_connected_dirty.Assign(0); 78 config.headphones_connected_dirty.Assign(0);
81 // Do nothing. 79 // Do nothing. (Note: Whether headphones are connected does affect coefficients used for
82 // (Note: Whether headphones are connected does affect coefficients used for surround sound.) 80 // surround sound.)
83 LOG_TRACE(Audio_DSP, "mixers headphones_connected=%hu", config.headphones_connected); 81 LOG_TRACE(Audio_DSP, "mixers headphones_connected=%hu", config.headphones_connected);
84 } 82 }
85 83
@@ -94,11 +92,10 @@ static s16 ClampToS16(s32 value) {
94 return static_cast<s16>(MathUtil::Clamp(value, -32768, 32767)); 92 return static_cast<s16>(MathUtil::Clamp(value, -32768, 32767));
95} 93}
96 94
97static std::array<s16, 2> AddAndClampToS16(const std::array<s16, 2>& a, const std::array<s16, 2>& b) { 95static std::array<s16, 2> AddAndClampToS16(const std::array<s16, 2>& a,
98 return { 96 const std::array<s16, 2>& b) {
99 ClampToS16(static_cast<s32>(a[0]) + static_cast<s32>(b[0])), 97 return {ClampToS16(static_cast<s32>(a[0]) + static_cast<s32>(b[0])),
100 ClampToS16(static_cast<s32>(a[1]) + static_cast<s32>(b[1])) 98 ClampToS16(static_cast<s32>(a[1]) + static_cast<s32>(b[1]))};
101 };
102} 99}
103 100
104void Mixers::DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& samples) { 101void Mixers::DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& samples) {
@@ -106,27 +103,33 @@ void Mixers::DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& sample
106 103
107 switch (state.output_format) { 104 switch (state.output_format) {
108 case OutputFormat::Mono: 105 case OutputFormat::Mono:
109 std::transform(current_frame.begin(), current_frame.end(), samples.begin(), current_frame.begin(), 106 std::transform(
110 [gain](const std::array<s16, 2>& accumulator, const std::array<s32, 4>& sample) -> std::array<s16, 2> { 107 current_frame.begin(), current_frame.end(), samples.begin(), current_frame.begin(),
108 [gain](const std::array<s16, 2>& accumulator,
109 const std::array<s32, 4>& sample) -> std::array<s16, 2> {
111 // Downmix to mono 110 // Downmix to mono
112 s16 mono = ClampToS16(static_cast<s32>((gain * sample[0] + gain * sample[1] + gain * sample[2] + gain * sample[3]) / 2)); 111 s16 mono = ClampToS16(static_cast<s32>(
112 (gain * sample[0] + gain * sample[1] + gain * sample[2] + gain * sample[3]) /
113 2));
113 // Mix into current frame 114 // Mix into current frame
114 return AddAndClampToS16(accumulator, { mono, mono }); 115 return AddAndClampToS16(accumulator, {mono, mono});
115 }); 116 });
116 return; 117 return;
117 118
118 case OutputFormat::Surround: 119 case OutputFormat::Surround:
119 // TODO(merry): Implement surround sound. 120 // TODO(merry): Implement surround sound.
120 // fallthrough 121 // fallthrough
121 122
122 case OutputFormat::Stereo: 123 case OutputFormat::Stereo:
123 std::transform(current_frame.begin(), current_frame.end(), samples.begin(), current_frame.begin(), 124 std::transform(
124 [gain](const std::array<s16, 2>& accumulator, const std::array<s32, 4>& sample) -> std::array<s16, 2> { 125 current_frame.begin(), current_frame.end(), samples.begin(), current_frame.begin(),
126 [gain](const std::array<s16, 2>& accumulator,
127 const std::array<s32, 4>& sample) -> std::array<s16, 2> {
125 // Downmix to stereo 128 // Downmix to stereo
126 s16 left = ClampToS16(static_cast<s32>(gain * sample[0] + gain * sample[2])); 129 s16 left = ClampToS16(static_cast<s32>(gain * sample[0] + gain * sample[2]));
127 s16 right = ClampToS16(static_cast<s32>(gain * sample[1] + gain * sample[3])); 130 s16 right = ClampToS16(static_cast<s32>(gain * sample[1] + gain * sample[3]));
128 // Mix into current frame 131 // Mix into current frame
129 return AddAndClampToS16(accumulator, { left, right }); 132 return AddAndClampToS16(accumulator, {left, right});
130 }); 133 });
131 return; 134 return;
132 } 135 }
@@ -135,12 +138,14 @@ void Mixers::DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& sample
135} 138}
136 139
137void Mixers::AuxReturn(const IntermediateMixSamples& read_samples) { 140void Mixers::AuxReturn(const IntermediateMixSamples& read_samples) {
138 // NOTE: read_samples.mix{1,2}.pcm32 annoyingly have their dimensions in reverse order to QuadFrame32. 141 // NOTE: read_samples.mix{1,2}.pcm32 annoyingly have their dimensions in reverse order to
142 // QuadFrame32.
139 143
140 if (state.mixer1_enabled) { 144 if (state.mixer1_enabled) {
141 for (size_t sample = 0; sample < samples_per_frame; sample++) { 145 for (size_t sample = 0; sample < samples_per_frame; sample++) {
142 for (size_t channel = 0; channel < 4; channel++) { 146 for (size_t channel = 0; channel < 4; channel++) {
143 state.intermediate_mix_buffer[1][sample][channel] = read_samples.mix1.pcm32[channel][sample]; 147 state.intermediate_mix_buffer[1][sample][channel] =
148 read_samples.mix1.pcm32[channel][sample];
144 } 149 }
145 } 150 }
146 } 151 }
@@ -148,14 +153,17 @@ void Mixers::AuxReturn(const IntermediateMixSamples& read_samples) {
148 if (state.mixer2_enabled) { 153 if (state.mixer2_enabled) {
149 for (size_t sample = 0; sample < samples_per_frame; sample++) { 154 for (size_t sample = 0; sample < samples_per_frame; sample++) {
150 for (size_t channel = 0; channel < 4; channel++) { 155 for (size_t channel = 0; channel < 4; channel++) {
151 state.intermediate_mix_buffer[2][sample][channel] = read_samples.mix2.pcm32[channel][sample]; 156 state.intermediate_mix_buffer[2][sample][channel] =
157 read_samples.mix2.pcm32[channel][sample];
152 } 158 }
153 } 159 }
154 } 160 }
155} 161}
156 162
157void Mixers::AuxSend(IntermediateMixSamples& write_samples, const std::array<QuadFrame32, 3>& input) { 163void Mixers::AuxSend(IntermediateMixSamples& write_samples,
158 // NOTE: read_samples.mix{1,2}.pcm32 annoyingly have their dimensions in reverse order to QuadFrame32. 164 const std::array<QuadFrame32, 3>& input) {
165 // NOTE: read_samples.mix{1,2}.pcm32 annoyingly have their dimensions in reverse order to
166 // QuadFrame32.
159 167
160 state.intermediate_mix_buffer[0] = input[0]; 168 state.intermediate_mix_buffer[0] = input[0];
161 169
@@ -184,7 +192,8 @@ void Mixers::MixCurrentFrame() {
184 current_frame.fill({}); 192 current_frame.fill({});
185 193
186 for (size_t mix = 0; mix < 3; mix++) { 194 for (size_t mix = 0; mix < 3; mix++) {
187 DownmixAndMixIntoCurrentFrame(state.intermediate_mixer_volume[mix], state.intermediate_mix_buffer[mix]); 195 DownmixAndMixIntoCurrentFrame(state.intermediate_mixer_volume[mix],
196 state.intermediate_mix_buffer[mix]);
188 } 197 }
189 198
190 // TODO(merry): Compressor. (We currently assume a disabled compressor.) 199 // TODO(merry): Compressor. (We currently assume a disabled compressor.)
diff --git a/src/audio_core/hle/mixers.h b/src/audio_core/hle/mixers.h
index b52952eb5..bf4e865ae 100644
--- a/src/audio_core/hle/mixers.h
+++ b/src/audio_core/hle/mixers.h
@@ -5,7 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8
9#include "audio_core/hle/common.h" 8#include "audio_core/hle/common.h"
10#include "audio_core/hle/dsp.h" 9#include "audio_core/hle/dsp.h"
11 10
@@ -20,10 +19,8 @@ public:
20 19
21 void Reset(); 20 void Reset();
22 21
23 DspStatus Tick(DspConfiguration& config, 22 DspStatus Tick(DspConfiguration& config, const IntermediateMixSamples& read_samples,
24 const IntermediateMixSamples& read_samples, 23 IntermediateMixSamples& write_samples, const std::array<QuadFrame32, 3>& input);
25 IntermediateMixSamples& write_samples,
26 const std::array<QuadFrame32, 3>& input);
27 24
28 StereoFrame16 GetOutput() const { 25 StereoFrame16 GetOutput() const {
29 return current_frame; 26 return current_frame;
@@ -53,7 +50,8 @@ private:
53 void AuxSend(IntermediateMixSamples& write_samples, const std::array<QuadFrame32, 3>& input); 50 void AuxSend(IntermediateMixSamples& write_samples, const std::array<QuadFrame32, 3>& input);
54 /// INTERNAL: Mix current_frame. 51 /// INTERNAL: Mix current_frame.
55 void MixCurrentFrame(); 52 void MixCurrentFrame();
56 /// INTERNAL: Downmix from quadraphonic to stereo based on status.output_format and accumulate into current_frame. 53 /// INTERNAL: Downmix from quadraphonic to stereo based on status.output_format and accumulate
54 /// into current_frame.
57 void DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& samples); 55 void DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& samples);
58 /// INTERNAL: Generate DspStatus based on internal state. 56 /// INTERNAL: Generate DspStatus based on internal state.
59 DspStatus GetCurrentStatus() const; 57 DspStatus GetCurrentStatus() const;
diff --git a/src/audio_core/hle/pipe.cpp b/src/audio_core/hle/pipe.cpp
index 44dff1345..b472c81d8 100644
--- a/src/audio_core/hle/pipe.cpp
+++ b/src/audio_core/hle/pipe.cpp
@@ -4,14 +4,11 @@
4 4
5#include <array> 5#include <array>
6#include <vector> 6#include <vector>
7
8#include "audio_core/hle/dsp.h" 7#include "audio_core/hle/dsp.h"
9#include "audio_core/hle/pipe.h" 8#include "audio_core/hle/pipe.h"
10
11#include "common/assert.h" 9#include "common/assert.h"
12#include "common/common_types.h" 10#include "common/common_types.h"
13#include "common/logging/log.h" 11#include "common/logging/log.h"
14
15#include "core/hle/service/dsp_dsp.h" 12#include "core/hle/service/dsp_dsp.h"
16 13
17namespace DSP { 14namespace DSP {
@@ -44,8 +41,10 @@ std::vector<u8> PipeRead(DspPipe pipe_number, u32 length) {
44 std::vector<u8>& data = pipe_data[pipe_index]; 41 std::vector<u8>& data = pipe_data[pipe_index];
45 42
46 if (length > data.size()) { 43 if (length > data.size()) {
47 LOG_WARNING(Audio_DSP, "pipe_number = %zu is out of data, application requested read of %u but %zu remain", 44 LOG_WARNING(
48 pipe_index, length, data.size()); 45 Audio_DSP,
46 "pipe_number = %zu is out of data, application requested read of %u but %zu remain",
47 pipe_index, length, data.size());
49 length = static_cast<u32>(data.size()); 48 length = static_cast<u32>(data.size());
50 } 49 }
51 50
@@ -95,7 +94,7 @@ static void AudioPipeWriteStructAddresses() {
95 0x8000 + offsetof(SharedMemory, unknown11) / 2, 94 0x8000 + offsetof(SharedMemory, unknown11) / 2,
96 0x8000 + offsetof(SharedMemory, unknown12) / 2, 95 0x8000 + offsetof(SharedMemory, unknown12) / 2,
97 0x8000 + offsetof(SharedMemory, unknown13) / 2, 96 0x8000 + offsetof(SharedMemory, unknown13) / 2,
98 0x8000 + offsetof(SharedMemory, unknown14) / 2 97 0x8000 + offsetof(SharedMemory, unknown14) / 2,
99 }; 98 };
100 99
101 // Begin with a u16 denoting the number of structs. 100 // Begin with a u16 denoting the number of structs.
@@ -112,7 +111,8 @@ void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) {
112 switch (pipe_number) { 111 switch (pipe_number) {
113 case DspPipe::Audio: { 112 case DspPipe::Audio: {
114 if (buffer.size() != 4) { 113 if (buffer.size() != 4) {
115 LOG_ERROR(Audio_DSP, "DspPipe::Audio: Unexpected buffer length %zu was written", buffer.size()); 114 LOG_ERROR(Audio_DSP, "DspPipe::Audio: Unexpected buffer length %zu was written",
115 buffer.size());
116 return; 116 return;
117 } 117 }
118 118
@@ -120,7 +120,7 @@ void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) {
120 Initalize = 0, 120 Initalize = 0,
121 Shutdown = 1, 121 Shutdown = 1,
122 Wakeup = 2, 122 Wakeup = 2,
123 Sleep = 3 123 Sleep = 3,
124 }; 124 };
125 125
126 // The difference between Initialize and Wakeup is that Input state is maintained 126 // The difference between Initialize and Wakeup is that Input state is maintained
@@ -152,7 +152,9 @@ void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) {
152 dsp_state = DspState::Sleeping; 152 dsp_state = DspState::Sleeping;
153 break; 153 break;
154 default: 154 default:
155 LOG_ERROR(Audio_DSP, "Application has requested unknown state transition of DSP hardware %hhu", buffer[0]); 155 LOG_ERROR(Audio_DSP,
156 "Application has requested unknown state transition of DSP hardware %hhu",
157 buffer[0]);
156 dsp_state = DspState::Off; 158 dsp_state = DspState::Off;
157 break; 159 break;
158 } 160 }
@@ -160,7 +162,8 @@ void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) {
160 return; 162 return;
161 } 163 }
162 default: 164 default:
163 LOG_CRITICAL(Audio_DSP, "pipe_number = %zu unimplemented", static_cast<size_t>(pipe_number)); 165 LOG_CRITICAL(Audio_DSP, "pipe_number = %zu unimplemented",
166 static_cast<size_t>(pipe_number));
164 UNIMPLEMENTED(); 167 UNIMPLEMENTED();
165 return; 168 return;
166 } 169 }
diff --git a/src/audio_core/hle/pipe.h b/src/audio_core/hle/pipe.h
index b714c0496..ac053c029 100644
--- a/src/audio_core/hle/pipe.h
+++ b/src/audio_core/hle/pipe.h
@@ -6,7 +6,6 @@
6 6
7#include <cstddef> 7#include <cstddef>
8#include <vector> 8#include <vector>
9
10#include "common/common_types.h" 9#include "common/common_types.h"
11 10
12namespace DSP { 11namespace DSP {
@@ -19,16 +18,18 @@ enum class DspPipe {
19 Debug = 0, 18 Debug = 0,
20 Dma = 1, 19 Dma = 1,
21 Audio = 2, 20 Audio = 2,
22 Binary = 3 21 Binary = 3,
23}; 22};
24constexpr size_t NUM_DSP_PIPE = 8; 23constexpr size_t NUM_DSP_PIPE = 8;
25 24
26/** 25/**
27 * Reads `length` bytes from the DSP pipe identified with `pipe_number`. 26 * Reads `length` bytes from the DSP pipe identified with `pipe_number`.
28 * @note Can read up to the maximum value of a u16 in bytes (65,535). 27 * @note Can read up to the maximum value of a u16 in bytes (65,535).
29 * @note IF an error is encoutered with either an invalid `pipe_number` or `length` value, an empty vector will be returned. 28 * @note IF an error is encoutered with either an invalid `pipe_number` or `length` value, an empty
29 * vector will be returned.
30 * @note IF `length` is set to 0, an empty vector will be returned. 30 * @note IF `length` is set to 0, an empty vector will be returned.
31 * @note IF `length` is greater than the amount of data available, this function will only read the available amount. 31 * @note IF `length` is greater than the amount of data available, this function will only read the
32 * available amount.
32 * @param pipe_number a `DspPipe` 33 * @param pipe_number a `DspPipe`
33 * @param length the number of bytes to read. The max is 65,535 (max of u16). 34 * @param length the number of bytes to read. The max is 65,535 (max of u16).
34 * @returns a vector of bytes from the specified pipe. On error, will be empty. 35 * @returns a vector of bytes from the specified pipe. On error, will be empty.
@@ -52,8 +53,9 @@ void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer);
52enum class DspState { 53enum class DspState {
53 Off, 54 Off,
54 On, 55 On,
55 Sleeping 56 Sleeping,
56}; 57};
58
57/// Get the state of the DSP 59/// Get the state of the DSP
58DspState GetDspState(); 60DspState GetDspState();
59 61
diff --git a/src/audio_core/hle/source.cpp b/src/audio_core/hle/source.cpp
index 30552fe26..2bbf7146e 100644
--- a/src/audio_core/hle/source.cpp
+++ b/src/audio_core/hle/source.cpp
@@ -4,21 +4,19 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <array> 6#include <array>
7
8#include "audio_core/codec.h" 7#include "audio_core/codec.h"
9#include "audio_core/hle/common.h" 8#include "audio_core/hle/common.h"
10#include "audio_core/hle/source.h" 9#include "audio_core/hle/source.h"
11#include "audio_core/interpolate.h" 10#include "audio_core/interpolate.h"
12
13#include "common/assert.h" 11#include "common/assert.h"
14#include "common/logging/log.h" 12#include "common/logging/log.h"
15
16#include "core/memory.h" 13#include "core/memory.h"
17 14
18namespace DSP { 15namespace DSP {
19namespace HLE { 16namespace HLE {
20 17
21SourceStatus::Status Source::Tick(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]) { 18SourceStatus::Status Source::Tick(SourceConfiguration::Configuration& config,
19 const s16_le (&adpcm_coeffs)[16]) {
22 ParseConfig(config, adpcm_coeffs); 20 ParseConfig(config, adpcm_coeffs);
23 21
24 if (state.enabled) { 22 if (state.enabled) {
@@ -47,7 +45,8 @@ void Source::Reset() {
47 state = {}; 45 state = {};
48} 46}
49 47
50void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]) { 48void Source::ParseConfig(SourceConfiguration::Configuration& config,
49 const s16_le (&adpcm_coeffs)[16]) {
51 if (!config.dirty_raw) { 50 if (!config.dirty_raw) {
52 return; 51 return;
53 } 52 }
@@ -82,7 +81,8 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_l
82 LOG_TRACE(Audio_DSP, "source_id=%zu rate=%f", source_id, state.rate_multiplier); 81 LOG_TRACE(Audio_DSP, "source_id=%zu rate=%f", source_id, state.rate_multiplier);
83 82
84 if (state.rate_multiplier <= 0) { 83 if (state.rate_multiplier <= 0) {
85 LOG_ERROR(Audio_DSP, "Was given an invalid rate multiplier: source_id=%zu rate=%f", source_id, state.rate_multiplier); 84 LOG_ERROR(Audio_DSP, "Was given an invalid rate multiplier: source_id=%zu rate=%f",
85 source_id, state.rate_multiplier);
86 state.rate_multiplier = 1.0f; 86 state.rate_multiplier = 1.0f;
87 // Note: Actual firmware starts producing garbage if this occurs. 87 // Note: Actual firmware starts producing garbage if this occurs.
88 } 88 }
@@ -90,37 +90,39 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_l
90 90
91 if (config.adpcm_coefficients_dirty) { 91 if (config.adpcm_coefficients_dirty) {
92 config.adpcm_coefficients_dirty.Assign(0); 92 config.adpcm_coefficients_dirty.Assign(0);
93 std::transform(adpcm_coeffs, adpcm_coeffs + state.adpcm_coeffs.size(), state.adpcm_coeffs.begin(), 93 std::transform(adpcm_coeffs, adpcm_coeffs + state.adpcm_coeffs.size(),
94 [](const auto& coeff) { return static_cast<s16>(coeff); }); 94 state.adpcm_coeffs.begin(),
95 [](const auto& coeff) { return static_cast<s16>(coeff); });
95 LOG_TRACE(Audio_DSP, "source_id=%zu adpcm update", source_id); 96 LOG_TRACE(Audio_DSP, "source_id=%zu adpcm update", source_id);
96 } 97 }
97 98
98 if (config.gain_0_dirty) { 99 if (config.gain_0_dirty) {
99 config.gain_0_dirty.Assign(0); 100 config.gain_0_dirty.Assign(0);
100 std::transform(config.gain[0], config.gain[0] + state.gain[0].size(), state.gain[0].begin(), 101 std::transform(config.gain[0], config.gain[0] + state.gain[0].size(), state.gain[0].begin(),
101 [](const auto& coeff) { return static_cast<float>(coeff); }); 102 [](const auto& coeff) { return static_cast<float>(coeff); });
102 LOG_TRACE(Audio_DSP, "source_id=%zu gain 0 update", source_id); 103 LOG_TRACE(Audio_DSP, "source_id=%zu gain 0 update", source_id);
103 } 104 }
104 105
105 if (config.gain_1_dirty) { 106 if (config.gain_1_dirty) {
106 config.gain_1_dirty.Assign(0); 107 config.gain_1_dirty.Assign(0);
107 std::transform(config.gain[1], config.gain[1] + state.gain[1].size(), state.gain[1].begin(), 108 std::transform(config.gain[1], config.gain[1] + state.gain[1].size(), state.gain[1].begin(),
108 [](const auto& coeff) { return static_cast<float>(coeff); }); 109 [](const auto& coeff) { return static_cast<float>(coeff); });
109 LOG_TRACE(Audio_DSP, "source_id=%zu gain 1 update", source_id); 110 LOG_TRACE(Audio_DSP, "source_id=%zu gain 1 update", source_id);
110 } 111 }
111 112
112 if (config.gain_2_dirty) { 113 if (config.gain_2_dirty) {
113 config.gain_2_dirty.Assign(0); 114 config.gain_2_dirty.Assign(0);
114 std::transform(config.gain[2], config.gain[2] + state.gain[2].size(), state.gain[2].begin(), 115 std::transform(config.gain[2], config.gain[2] + state.gain[2].size(), state.gain[2].begin(),
115 [](const auto& coeff) { return static_cast<float>(coeff); }); 116 [](const auto& coeff) { return static_cast<float>(coeff); });
116 LOG_TRACE(Audio_DSP, "source_id=%zu gain 2 update", source_id); 117 LOG_TRACE(Audio_DSP, "source_id=%zu gain 2 update", source_id);
117 } 118 }
118 119
119 if (config.filters_enabled_dirty) { 120 if (config.filters_enabled_dirty) {
120 config.filters_enabled_dirty.Assign(0); 121 config.filters_enabled_dirty.Assign(0);
121 state.filters.Enable(config.simple_filter_enabled.ToBool(), config.biquad_filter_enabled.ToBool()); 122 state.filters.Enable(config.simple_filter_enabled.ToBool(),
122 LOG_TRACE(Audio_DSP, "source_id=%zu enable_simple=%hu enable_biquad=%hu", 123 config.biquad_filter_enabled.ToBool());
123 source_id, config.simple_filter_enabled.Value(), config.biquad_filter_enabled.Value()); 124 LOG_TRACE(Audio_DSP, "source_id=%zu enable_simple=%hu enable_biquad=%hu", source_id,
125 config.simple_filter_enabled.Value(), config.biquad_filter_enabled.Value());
124 } 126 }
125 127
126 if (config.simple_filter_dirty) { 128 if (config.simple_filter_dirty) {
@@ -138,19 +140,22 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_l
138 if (config.interpolation_dirty) { 140 if (config.interpolation_dirty) {
139 config.interpolation_dirty.Assign(0); 141 config.interpolation_dirty.Assign(0);
140 state.interpolation_mode = config.interpolation_mode; 142 state.interpolation_mode = config.interpolation_mode;
141 LOG_TRACE(Audio_DSP, "source_id=%zu interpolation_mode=%zu", source_id, static_cast<size_t>(state.interpolation_mode)); 143 LOG_TRACE(Audio_DSP, "source_id=%zu interpolation_mode=%zu", source_id,
144 static_cast<size_t>(state.interpolation_mode));
142 } 145 }
143 146
144 if (config.format_dirty || config.embedded_buffer_dirty) { 147 if (config.format_dirty || config.embedded_buffer_dirty) {
145 config.format_dirty.Assign(0); 148 config.format_dirty.Assign(0);
146 state.format = config.format; 149 state.format = config.format;
147 LOG_TRACE(Audio_DSP, "source_id=%zu format=%zu", source_id, static_cast<size_t>(state.format)); 150 LOG_TRACE(Audio_DSP, "source_id=%zu format=%zu", source_id,
151 static_cast<size_t>(state.format));
148 } 152 }
149 153
150 if (config.mono_or_stereo_dirty || config.embedded_buffer_dirty) { 154 if (config.mono_or_stereo_dirty || config.embedded_buffer_dirty) {
151 config.mono_or_stereo_dirty.Assign(0); 155 config.mono_or_stereo_dirty.Assign(0);
152 state.mono_or_stereo = config.mono_or_stereo; 156 state.mono_or_stereo = config.mono_or_stereo;
153 LOG_TRACE(Audio_DSP, "source_id=%zu mono_or_stereo=%zu", source_id, static_cast<size_t>(state.mono_or_stereo)); 157 LOG_TRACE(Audio_DSP, "source_id=%zu mono_or_stereo=%zu", source_id,
158 static_cast<size_t>(state.mono_or_stereo));
154 } 159 }
155 160
156 if (config.embedded_buffer_dirty) { 161 if (config.embedded_buffer_dirty) {
@@ -159,15 +164,16 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_l
159 config.physical_address, 164 config.physical_address,
160 config.length, 165 config.length,
161 static_cast<u8>(config.adpcm_ps), 166 static_cast<u8>(config.adpcm_ps),
162 { config.adpcm_yn[0], config.adpcm_yn[1] }, 167 {config.adpcm_yn[0], config.adpcm_yn[1]},
163 config.adpcm_dirty.ToBool(), 168 config.adpcm_dirty.ToBool(),
164 config.is_looping.ToBool(), 169 config.is_looping.ToBool(),
165 config.buffer_id, 170 config.buffer_id,
166 state.mono_or_stereo, 171 state.mono_or_stereo,
167 state.format, 172 state.format,
168 false 173 false,
169 }); 174 });
170 LOG_TRACE(Audio_DSP, "enqueuing embedded addr=0x%08x len=%u id=%hu", config.physical_address, config.length, config.buffer_id); 175 LOG_TRACE(Audio_DSP, "enqueuing embedded addr=0x%08x len=%u id=%hu",
176 config.physical_address, config.length, config.buffer_id);
171 } 177 }
172 178
173 if (config.buffer_queue_dirty) { 179 if (config.buffer_queue_dirty) {
@@ -179,15 +185,16 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_l
179 b.physical_address, 185 b.physical_address,
180 b.length, 186 b.length,
181 static_cast<u8>(b.adpcm_ps), 187 static_cast<u8>(b.adpcm_ps),
182 { b.adpcm_yn[0], b.adpcm_yn[1] }, 188 {b.adpcm_yn[0], b.adpcm_yn[1]},
183 b.adpcm_dirty != 0, 189 b.adpcm_dirty != 0,
184 b.is_looping != 0, 190 b.is_looping != 0,
185 b.buffer_id, 191 b.buffer_id,
186 state.mono_or_stereo, 192 state.mono_or_stereo,
187 state.format, 193 state.format,
188 true 194 true,
189 }); 195 });
190 LOG_TRACE(Audio_DSP, "enqueuing queued %zu addr=0x%08x len=%u id=%hu", i, b.physical_address, b.length, b.buffer_id); 196 LOG_TRACE(Audio_DSP, "enqueuing queued %zu addr=0x%08x len=%u id=%hu", i,
197 b.physical_address, b.length, b.buffer_id);
191 } 198 }
192 } 199 }
193 config.buffers_dirty = 0; 200 config.buffers_dirty = 0;
@@ -218,10 +225,13 @@ void Source::GenerateFrame() {
218 break; 225 break;
219 } 226 }
220 227
221 const size_t size_to_copy = std::min(state.current_buffer.size(), current_frame.size() - frame_position); 228 const size_t size_to_copy =
229 std::min(state.current_buffer.size(), current_frame.size() - frame_position);
222 230
223 std::copy(state.current_buffer.begin(), state.current_buffer.begin() + size_to_copy, current_frame.begin() + frame_position); 231 std::copy(state.current_buffer.begin(), state.current_buffer.begin() + size_to_copy,
224 state.current_buffer.erase(state.current_buffer.begin(), state.current_buffer.begin() + size_to_copy); 232 current_frame.begin() + frame_position);
233 state.current_buffer.erase(state.current_buffer.begin(),
234 state.current_buffer.begin() + size_to_copy);
225 235
226 frame_position += size_to_copy; 236 frame_position += size_to_copy;
227 state.next_sample_number += static_cast<u32>(size_to_copy); 237 state.next_sample_number += static_cast<u32>(size_to_copy);
@@ -230,9 +240,9 @@ void Source::GenerateFrame() {
230 state.filters.ProcessFrame(current_frame); 240 state.filters.ProcessFrame(current_frame);
231} 241}
232 242
233
234bool Source::DequeueBuffer() { 243bool Source::DequeueBuffer() {
235 ASSERT_MSG(state.current_buffer.empty(), "Shouldn't dequeue; we still have data in current_buffer"); 244 ASSERT_MSG(state.current_buffer.empty(),
245 "Shouldn't dequeue; we still have data in current_buffer");
236 246
237 if (state.input_queue.empty()) 247 if (state.input_queue.empty())
238 return false; 248 return false;
@@ -261,29 +271,34 @@ bool Source::DequeueBuffer() {
261 break; 271 break;
262 case Format::ADPCM: 272 case Format::ADPCM:
263 DEBUG_ASSERT(num_channels == 1); 273 DEBUG_ASSERT(num_channels == 1);
264 state.current_buffer = Codec::DecodeADPCM(memory, buf.length, state.adpcm_coeffs, state.adpcm_state); 274 state.current_buffer =
275 Codec::DecodeADPCM(memory, buf.length, state.adpcm_coeffs, state.adpcm_state);
265 break; 276 break;
266 default: 277 default:
267 UNIMPLEMENTED(); 278 UNIMPLEMENTED();
268 break; 279 break;
269 } 280 }
270 } else { 281 } else {
271 LOG_WARNING(Audio_DSP, "source_id=%zu buffer_id=%hu length=%u: Invalid physical address 0x%08X", 282 LOG_WARNING(Audio_DSP,
272 source_id, buf.buffer_id, buf.length, buf.physical_address); 283 "source_id=%zu buffer_id=%hu length=%u: Invalid physical address 0x%08X",
284 source_id, buf.buffer_id, buf.length, buf.physical_address);
273 state.current_buffer.clear(); 285 state.current_buffer.clear();
274 return true; 286 return true;
275 } 287 }
276 288
277 switch (state.interpolation_mode) { 289 switch (state.interpolation_mode) {
278 case InterpolationMode::None: 290 case InterpolationMode::None:
279 state.current_buffer = AudioInterp::None(state.interp_state, state.current_buffer, state.rate_multiplier); 291 state.current_buffer =
292 AudioInterp::None(state.interp_state, state.current_buffer, state.rate_multiplier);
280 break; 293 break;
281 case InterpolationMode::Linear: 294 case InterpolationMode::Linear:
282 state.current_buffer = AudioInterp::Linear(state.interp_state, state.current_buffer, state.rate_multiplier); 295 state.current_buffer =
296 AudioInterp::Linear(state.interp_state, state.current_buffer, state.rate_multiplier);
283 break; 297 break;
284 case InterpolationMode::Polyphase: 298 case InterpolationMode::Polyphase:
285 // TODO(merry): Implement polyphase interpolation 299 // TODO(merry): Implement polyphase interpolation
286 state.current_buffer = AudioInterp::Linear(state.interp_state, state.current_buffer, state.rate_multiplier); 300 state.current_buffer =
301 AudioInterp::Linear(state.interp_state, state.current_buffer, state.rate_multiplier);
287 break; 302 break;
288 default: 303 default:
289 UNIMPLEMENTED(); 304 UNIMPLEMENTED();
@@ -296,7 +311,8 @@ bool Source::DequeueBuffer() {
296 state.buffer_update = buf.from_queue; 311 state.buffer_update = buf.from_queue;
297 312
298 LOG_TRACE(Audio_DSP, "source_id=%zu buffer_id=%hu from_queue=%s current_buffer.size()=%zu", 313 LOG_TRACE(Audio_DSP, "source_id=%zu buffer_id=%hu from_queue=%s current_buffer.size()=%zu",
299 source_id, buf.buffer_id, buf.from_queue ? "true" : "false", state.current_buffer.size()); 314 source_id, buf.buffer_id, buf.from_queue ? "true" : "false",
315 state.current_buffer.size());
300 return true; 316 return true;
301} 317}
302 318
diff --git a/src/audio_core/hle/source.h b/src/audio_core/hle/source.h
index 7ee08d424..3d725f2a3 100644
--- a/src/audio_core/hle/source.h
+++ b/src/audio_core/hle/source.h
@@ -7,13 +7,11 @@
7#include <array> 7#include <array>
8#include <queue> 8#include <queue>
9#include <vector> 9#include <vector>
10
11#include "audio_core/codec.h" 10#include "audio_core/codec.h"
12#include "audio_core/hle/common.h" 11#include "audio_core/hle/common.h"
13#include "audio_core/hle/dsp.h" 12#include "audio_core/hle/dsp.h"
14#include "audio_core/hle/filter.h" 13#include "audio_core/hle/filter.h"
15#include "audio_core/interpolate.h" 14#include "audio_core/interpolate.h"
16
17#include "common/common_types.h" 15#include "common/common_types.h"
18 16
19namespace DSP { 17namespace DSP {
@@ -40,13 +38,17 @@ public:
40 /** 38 /**
41 * This is called once every audio frame. This performs per-source processing every frame. 39 * This is called once every audio frame. This performs per-source processing every frame.
42 * @param config The new configuration we've got for this Source from the application. 40 * @param config The new configuration we've got for this Source from the application.
43 * @param adpcm_coeffs ADPCM coefficients to use if config tells us to use them (may contain invalid values otherwise). 41 * @param adpcm_coeffs ADPCM coefficients to use if config tells us to use them (may contain
44 * @return The current status of this Source. This is given back to the emulated application via SharedMemory. 42 * invalid values otherwise).
43 * @return The current status of this Source. This is given back to the emulated application via
44 * SharedMemory.
45 */ 45 */
46 SourceStatus::Status Tick(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]); 46 SourceStatus::Status Tick(SourceConfiguration::Configuration& config,
47 const s16_le (&adpcm_coeffs)[16]);
47 48
48 /** 49 /**
49 * Mix this source's output into dest, using the gains for the `intermediate_mix_id`-th intermediate mixer. 50 * Mix this source's output into dest, using the gains for the `intermediate_mix_id`-th
51 * intermediate mixer.
50 * @param dest The QuadFrame32 to mix into. 52 * @param dest The QuadFrame32 to mix into.
51 * @param intermediate_mix_id The id of the intermediate mix whose gains we are using. 53 * @param intermediate_mix_id The id of the intermediate mix whose gains we are using.
52 */ 54 */
@@ -77,7 +79,7 @@ private:
77 }; 79 };
78 80
79 struct BufferOrder { 81 struct BufferOrder {
80 bool operator() (const Buffer& a, const Buffer& b) const { 82 bool operator()(const Buffer& a, const Buffer& b) const {
81 // Lower buffer_id comes first. 83 // Lower buffer_id comes first.
82 return a.buffer_id > b.buffer_id; 84 return a.buffer_id > b.buffer_id;
83 } 85 }
@@ -134,7 +136,8 @@ private:
134 void ParseConfig(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]); 136 void ParseConfig(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]);
135 /// INTERNAL: Generate the current audio output for this frame based on our internal state. 137 /// INTERNAL: Generate the current audio output for this frame based on our internal state.
136 void GenerateFrame(); 138 void GenerateFrame();
137 /// INTERNAL: Dequeues a buffer and does preprocessing on it (decoding, resampling). Puts it into current_buffer. 139 /// INTERNAL: Dequeues a buffer and does preprocessing on it (decoding, resampling). Puts it
140 /// into current_buffer.
138 bool DequeueBuffer(); 141 bool DequeueBuffer();
139 /// INTERNAL: Generates a SourceStatus::Status based on our internal state. 142 /// INTERNAL: Generates a SourceStatus::Status based on our internal state.
140 SourceStatus::Status GetCurrentStatus(); 143 SourceStatus::Status GetCurrentStatus();
diff --git a/src/audio_core/interpolate.cpp b/src/audio_core/interpolate.cpp
index fcd3aa066..8a5d4181a 100644
--- a/src/audio_core/interpolate.cpp
+++ b/src/audio_core/interpolate.cpp
@@ -3,7 +3,6 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "audio_core/interpolate.h" 5#include "audio_core/interpolate.h"
6
7#include "common/assert.h" 6#include "common/assert.h"
8#include "common/math_util.h" 7#include "common/math_util.h"
9 8
@@ -17,7 +16,8 @@ constexpr u64 scale_mask = scale_factor - 1;
17/// Here we step over the input in steps of rate_multiplier, until we consume all of the input. 16/// Here we step over the input in steps of rate_multiplier, until we consume all of the input.
18/// Three adjacent samples are passed to fn each step. 17/// Three adjacent samples are passed to fn each step.
19template <typename Function> 18template <typename Function>
20static StereoBuffer16 StepOverSamples(State& state, const StereoBuffer16& input, float rate_multiplier, Function fn) { 19static StereoBuffer16 StepOverSamples(State& state, const StereoBuffer16& input,
20 float rate_multiplier, Function fn) {
21 ASSERT(rate_multiplier > 0); 21 ASSERT(rate_multiplier > 0);
22 22
23 if (input.size() < 2) 23 if (input.size() < 2)
@@ -63,23 +63,24 @@ static StereoBuffer16 StepOverSamples(State& state, const StereoBuffer16& input,
63} 63}
64 64
65StereoBuffer16 None(State& state, const StereoBuffer16& input, float rate_multiplier) { 65StereoBuffer16 None(State& state, const StereoBuffer16& input, float rate_multiplier) {
66 return StepOverSamples(state, input, rate_multiplier, [](u64 fraction, const auto& x0, const auto& x1, const auto& x2) { 66 return StepOverSamples(
67 return x0; 67 state, input, rate_multiplier,
68 }); 68 [](u64 fraction, const auto& x0, const auto& x1, const auto& x2) { return x0; });
69} 69}
70 70
71StereoBuffer16 Linear(State& state, const StereoBuffer16& input, float rate_multiplier) { 71StereoBuffer16 Linear(State& state, const StereoBuffer16& input, float rate_multiplier) {
72 // Note on accuracy: Some values that this produces are +/- 1 from the actual firmware. 72 // Note on accuracy: Some values that this produces are +/- 1 from the actual firmware.
73 return StepOverSamples(state, input, rate_multiplier, [](u64 fraction, const auto& x0, const auto& x1, const auto& x2) { 73 return StepOverSamples(state, input, rate_multiplier,
74 // This is a saturated subtraction. (Verified by black-box fuzzing.) 74 [](u64 fraction, const auto& x0, const auto& x1, const auto& x2) {
75 s64 delta0 = MathUtil::Clamp<s64>(x1[0] - x0[0], -32768, 32767); 75 // This is a saturated subtraction. (Verified by black-box fuzzing.)
76 s64 delta1 = MathUtil::Clamp<s64>(x1[1] - x0[1], -32768, 32767); 76 s64 delta0 = MathUtil::Clamp<s64>(x1[0] - x0[0], -32768, 32767);
77 77 s64 delta1 = MathUtil::Clamp<s64>(x1[1] - x0[1], -32768, 32767);
78 return std::array<s16, 2> { 78
79 static_cast<s16>(x0[0] + fraction * delta0 / scale_factor), 79 return std::array<s16, 2>{
80 static_cast<s16>(x0[1] + fraction * delta1 / scale_factor) 80 static_cast<s16>(x0[0] + fraction * delta0 / scale_factor),
81 }; 81 static_cast<s16>(x0[1] + fraction * delta1 / scale_factor),
82 }); 82 };
83 });
83} 84}
84 85
85} // namespace AudioInterp 86} // namespace AudioInterp
diff --git a/src/audio_core/interpolate.h b/src/audio_core/interpolate.h
index a4c0a453d..dd06fdda9 100644
--- a/src/audio_core/interpolate.h
+++ b/src/audio_core/interpolate.h
@@ -6,7 +6,6 @@
6 6
7#include <array> 7#include <array>
8#include <vector> 8#include <vector>
9
10#include "common/common_types.h" 9#include "common/common_types.h"
11 10
12namespace AudioInterp { 11namespace AudioInterp {
@@ -24,7 +23,8 @@ struct State {
24 * No interpolation. This is equivalent to a zero-order hold. There is a two-sample predelay. 23 * No interpolation. This is equivalent to a zero-order hold. There is a two-sample predelay.
25 * @param input Input buffer. 24 * @param input Input buffer.
26 * @param rate_multiplier Stretch factor. Must be a positive non-zero value. 25 * @param rate_multiplier Stretch factor. Must be a positive non-zero value.
27 * rate_multiplier > 1.0 performs decimation and rate_multipler < 1.0 performs upsampling. 26 * rate_multiplier > 1.0 performs decimation and rate_multipler < 1.0
27 * performs upsampling.
28 * @return The resampled audio buffer. 28 * @return The resampled audio buffer.
29 */ 29 */
30StereoBuffer16 None(State& state, const StereoBuffer16& input, float rate_multiplier); 30StereoBuffer16 None(State& state, const StereoBuffer16& input, float rate_multiplier);
@@ -33,7 +33,8 @@ StereoBuffer16 None(State& state, const StereoBuffer16& input, float rate_multip
33 * Linear interpolation. This is equivalent to a first-order hold. There is a two-sample predelay. 33 * Linear interpolation. This is equivalent to a first-order hold. There is a two-sample predelay.
34 * @param input Input buffer. 34 * @param input Input buffer.
35 * @param rate_multiplier Stretch factor. Must be a positive non-zero value. 35 * @param rate_multiplier Stretch factor. Must be a positive non-zero value.
36 * rate_multiplier > 1.0 performs decimation and rate_multipler < 1.0 performs upsampling. 36 * rate_multiplier > 1.0 performs decimation and rate_multipler < 1.0
37 * performs upsampling.
37 * @return The resampled audio buffer. 38 * @return The resampled audio buffer.
38 */ 39 */
39StereoBuffer16 Linear(State& state, const StereoBuffer16& input, float rate_multiplier); 40StereoBuffer16 Linear(State& state, const StereoBuffer16& input, float rate_multiplier);
diff --git a/src/audio_core/null_sink.h b/src/audio_core/null_sink.h
index 9931c4778..e7668438c 100644
--- a/src/audio_core/null_sink.h
+++ b/src/audio_core/null_sink.h
@@ -5,7 +5,6 @@
5#pragma once 5#pragma once
6 6
7#include <cstddef> 7#include <cstddef>
8
9#include "audio_core/audio_core.h" 8#include "audio_core/audio_core.h"
10#include "audio_core/sink.h" 9#include "audio_core/sink.h"
11 10
diff --git a/src/audio_core/sdl2_sink.cpp b/src/audio_core/sdl2_sink.cpp
index 1d7912715..75cc0d6dd 100644
--- a/src/audio_core/sdl2_sink.cpp
+++ b/src/audio_core/sdl2_sink.cpp
@@ -3,16 +3,13 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <list> 5#include <list>
6#include <numeric>
6#include <vector> 7#include <vector>
7
8#include <SDL.h> 8#include <SDL.h>
9
10#include "audio_core/audio_core.h" 9#include "audio_core/audio_core.h"
11#include "audio_core/sdl2_sink.h" 10#include "audio_core/sdl2_sink.h"
12
13#include "common/assert.h" 11#include "common/assert.h"
14#include "common/logging/log.h" 12#include "common/logging/log.h"
15#include <numeric>
16 13
17namespace AudioCore { 14namespace AudioCore {
18 15
@@ -45,7 +42,8 @@ SDL2Sink::SDL2Sink() : impl(std::make_unique<Impl>()) {
45 SDL_AudioSpec obtained_audiospec; 42 SDL_AudioSpec obtained_audiospec;
46 SDL_zero(obtained_audiospec); 43 SDL_zero(obtained_audiospec);
47 44
48 impl->audio_device_id = SDL_OpenAudioDevice(nullptr, false, &desired_audiospec, &obtained_audiospec, 0); 45 impl->audio_device_id =
46 SDL_OpenAudioDevice(nullptr, false, &desired_audiospec, &obtained_audiospec, 0);
49 if (impl->audio_device_id <= 0) { 47 if (impl->audio_device_id <= 0) {
50 LOG_CRITICAL(Audio_Sink, "SDL_OpenAudioDevice failed"); 48 LOG_CRITICAL(Audio_Sink, "SDL_OpenAudioDevice failed");
51 return; 49 return;
@@ -86,11 +84,12 @@ size_t SDL2Sink::SamplesInQueue() const {
86 84
87 SDL_LockAudioDevice(impl->audio_device_id); 85 SDL_LockAudioDevice(impl->audio_device_id);
88 86
89 size_t total_size = std::accumulate(impl->queue.begin(), impl->queue.end(), static_cast<size_t>(0), 87 size_t total_size = std::accumulate(impl->queue.begin(), impl->queue.end(),
90 [](size_t sum, const auto& buffer) { 88 static_cast<size_t>(0), [](size_t sum, const auto& buffer) {
91 // Division by two because each stereo sample is made of two s16. 89 // Division by two because each stereo sample is made of
92 return sum + buffer.size() / 2; 90 // two s16.
93 }); 91 return sum + buffer.size() / 2;
92 });
94 93
95 SDL_UnlockAudioDevice(impl->audio_device_id); 94 SDL_UnlockAudioDevice(impl->audio_device_id);
96 95
@@ -100,7 +99,8 @@ size_t SDL2Sink::SamplesInQueue() const {
100void SDL2Sink::Impl::Callback(void* impl_, u8* buffer, int buffer_size_in_bytes) { 99void SDL2Sink::Impl::Callback(void* impl_, u8* buffer, int buffer_size_in_bytes) {
101 Impl* impl = reinterpret_cast<Impl*>(impl_); 100 Impl* impl = reinterpret_cast<Impl*>(impl_);
102 101
103 size_t remaining_size = static_cast<size_t>(buffer_size_in_bytes) / sizeof(s16); // Keep track of size in 16-bit increments. 102 size_t remaining_size = static_cast<size_t>(buffer_size_in_bytes) /
103 sizeof(s16); // Keep track of size in 16-bit increments.
104 104
105 while (remaining_size > 0 && !impl->queue.empty()) { 105 while (remaining_size > 0 && !impl->queue.empty()) {
106 if (impl->queue.front().size() <= remaining_size) { 106 if (impl->queue.front().size() <= remaining_size) {
@@ -111,7 +111,8 @@ void SDL2Sink::Impl::Callback(void* impl_, u8* buffer, int buffer_size_in_bytes)
111 } else { 111 } else {
112 memcpy(buffer, impl->queue.front().data(), remaining_size * sizeof(s16)); 112 memcpy(buffer, impl->queue.front().data(), remaining_size * sizeof(s16));
113 buffer += remaining_size * sizeof(s16); 113 buffer += remaining_size * sizeof(s16);
114 impl->queue.front().erase(impl->queue.front().begin(), impl->queue.front().begin() + remaining_size); 114 impl->queue.front().erase(impl->queue.front().begin(),
115 impl->queue.front().begin() + remaining_size);
115 remaining_size = 0; 116 remaining_size = 0;
116 } 117 }
117 } 118 }
diff --git a/src/audio_core/sdl2_sink.h b/src/audio_core/sdl2_sink.h
index b13827214..ccd0f7c7e 100644
--- a/src/audio_core/sdl2_sink.h
+++ b/src/audio_core/sdl2_sink.h
@@ -6,7 +6,6 @@
6 6
7#include <cstddef> 7#include <cstddef>
8#include <memory> 8#include <memory>
9
10#include "audio_core/sink.h" 9#include "audio_core/sink.h"
11 10
12namespace AudioCore { 11namespace AudioCore {
diff --git a/src/audio_core/sink.h b/src/audio_core/sink.h
index a06fc3dcc..08f3bab5b 100644
--- a/src/audio_core/sink.h
+++ b/src/audio_core/sink.h
@@ -5,20 +5,21 @@
5#pragma once 5#pragma once
6 6
7#include <vector> 7#include <vector>
8
9#include "common/common_types.h" 8#include "common/common_types.h"
10 9
11namespace AudioCore { 10namespace AudioCore {
12 11
13/** 12/**
14 * This class is an interface for an audio sink. An audio sink accepts samples in stereo signed PCM16 format to be output. 13 * This class is an interface for an audio sink. An audio sink accepts samples in stereo signed
15 * Sinks *do not* handle resampling and expect the correct sample rate. They are dumb outputs. 14 * PCM16 format to be output. Sinks *do not* handle resampling and expect the correct sample rate.
15 * They are dumb outputs.
16 */ 16 */
17class Sink { 17class Sink {
18public: 18public:
19 virtual ~Sink() = default; 19 virtual ~Sink() = default;
20 20
21 /// The native rate of this sink. The sink expects to be fed samples that respect this. (Units: samples/sec) 21 /// The native rate of this sink. The sink expects to be fed samples that respect this. (Units:
22 /// samples/sec)
22 virtual unsigned int GetNativeSampleRate() const = 0; 23 virtual unsigned int GetNativeSampleRate() const = 0;
23 24
24 /** 25 /**
diff --git a/src/audio_core/sink_details.cpp b/src/audio_core/sink_details.cpp
index ba5e83d17..95ccc9e9d 100644
--- a/src/audio_core/sink_details.cpp
+++ b/src/audio_core/sink_details.cpp
@@ -4,10 +4,8 @@
4 4
5#include <memory> 5#include <memory>
6#include <vector> 6#include <vector>
7
8#include "audio_core/null_sink.h" 7#include "audio_core/null_sink.h"
9#include "audio_core/sink_details.h" 8#include "audio_core/sink_details.h"
10
11#ifdef HAVE_SDL2 9#ifdef HAVE_SDL2
12#include "audio_core/sdl2_sink.h" 10#include "audio_core/sdl2_sink.h"
13#endif 11#endif
@@ -17,9 +15,9 @@ namespace AudioCore {
17// g_sink_details is ordered in terms of desirability, with the best choice at the top. 15// g_sink_details is ordered in terms of desirability, with the best choice at the top.
18const std::vector<SinkDetails> g_sink_details = { 16const std::vector<SinkDetails> g_sink_details = {
19#ifdef HAVE_SDL2 17#ifdef HAVE_SDL2
20 { "sdl2", []() { return std::make_unique<SDL2Sink>(); } }, 18 {"sdl2", []() { return std::make_unique<SDL2Sink>(); }},
21#endif 19#endif
22 { "null", []() { return std::make_unique<NullSink>(); } }, 20 {"null", []() { return std::make_unique<NullSink>(); }},
23}; 21};
24 22
25} // namespace AudioCore 23} // namespace AudioCore
diff --git a/src/audio_core/time_stretch.cpp b/src/audio_core/time_stretch.cpp
index ea38f40d0..53cb64655 100644
--- a/src/audio_core/time_stretch.cpp
+++ b/src/audio_core/time_stretch.cpp
@@ -5,12 +5,9 @@
5#include <chrono> 5#include <chrono>
6#include <cmath> 6#include <cmath>
7#include <vector> 7#include <vector>
8
9#include <SoundTouch.h> 8#include <SoundTouch.h>
10
11#include "audio_core/audio_core.h" 9#include "audio_core/audio_core.h"
12#include "audio_core/time_stretch.h" 10#include "audio_core/time_stretch.h"
13
14#include "common/common_types.h" 11#include "common/common_types.h"
15#include "common/logging/log.h" 12#include "common/logging/log.h"
16#include "common/math_util.h" 13#include "common/math_util.h"
@@ -26,8 +23,8 @@ static double ClampRatio(double ratio) {
26 return MathUtil::Clamp(ratio, MIN_RATIO, MAX_RATIO); 23 return MathUtil::Clamp(ratio, MIN_RATIO, MAX_RATIO);
27} 24}
28 25
29constexpr double MIN_DELAY_TIME = 0.05; // Units: seconds 26constexpr double MIN_DELAY_TIME = 0.05; // Units: seconds
30constexpr double MAX_DELAY_TIME = 0.25; // Units: seconds 27constexpr double MAX_DELAY_TIME = 0.25; // Units: seconds
31constexpr size_t DROP_FRAMES_SAMPLE_DELAY = 16000; // Units: samples 28constexpr size_t DROP_FRAMES_SAMPLE_DELAY = 16000; // Units: samples
32 29
33constexpr double SMOOTHING_FACTOR = 0.007; 30constexpr double SMOOTHING_FACTOR = 0.007;
@@ -48,7 +45,8 @@ std::vector<s16> TimeStretcher::Process(size_t samples_in_queue) {
48 45
49 double ratio = CalculateCurrentRatio(); 46 double ratio = CalculateCurrentRatio();
50 ratio = CorrectForUnderAndOverflow(ratio, samples_in_queue); 47 ratio = CorrectForUnderAndOverflow(ratio, samples_in_queue);
51 impl->smoothed_ratio = (1.0 - SMOOTHING_FACTOR) * impl->smoothed_ratio + SMOOTHING_FACTOR * ratio; 48 impl->smoothed_ratio =
49 (1.0 - SMOOTHING_FACTOR) * impl->smoothed_ratio + SMOOTHING_FACTOR * ratio;
52 impl->smoothed_ratio = ClampRatio(impl->smoothed_ratio); 50 impl->smoothed_ratio = ClampRatio(impl->smoothed_ratio);
53 51
54 // SoundTouch's tempo definition the inverse of our ratio definition. 52 // SoundTouch's tempo definition the inverse of our ratio definition.
@@ -100,7 +98,8 @@ double TimeStretcher::CalculateCurrentRatio() {
100 const steady_clock::time_point now = steady_clock::now(); 98 const steady_clock::time_point now = steady_clock::now();
101 const std::chrono::duration<double> duration = now - impl->frame_timer; 99 const std::chrono::duration<double> duration = now - impl->frame_timer;
102 100
103 const double expected_time = static_cast<double>(impl->samples_queued) / static_cast<double>(native_sample_rate); 101 const double expected_time =
102 static_cast<double>(impl->samples_queued) / static_cast<double>(native_sample_rate);
104 const double actual_time = duration.count(); 103 const double actual_time = duration.count();
105 104
106 double ratio; 105 double ratio;
diff --git a/src/audio_core/time_stretch.h b/src/audio_core/time_stretch.h
index 1fde3f72a..fa81718ed 100644
--- a/src/audio_core/time_stretch.h
+++ b/src/audio_core/time_stretch.h
@@ -5,7 +5,6 @@
5#include <cstddef> 5#include <cstddef>
6#include <memory> 6#include <memory>
7#include <vector> 7#include <vector>
8
9#include "common/common_types.h" 8#include "common/common_types.h"
10 9
11namespace AudioCore { 10namespace AudioCore {
@@ -37,7 +36,8 @@ public:
37 /** 36 /**
38 * Does audio stretching and produces the time-stretched samples. 37 * Does audio stretching and produces the time-stretched samples.
39 * Timer calculations use sample_delay to determine how much of a margin we have. 38 * Timer calculations use sample_delay to determine how much of a margin we have.
40 * @param sample_delay How many samples are buffered downstream of this module and haven't been played yet. 39 * @param sample_delay How many samples are buffered downstream of this module and haven't been
40 * played yet.
41 * @return Samples to play in interleaved stereo PCM16 format. 41 * @return Samples to play in interleaved stereo PCM16 format.
42 */ 42 */
43 std::vector<s16> Process(size_t sample_delay); 43 std::vector<s16> Process(size_t sample_delay);
@@ -48,7 +48,8 @@ private:
48 48
49 /// INTERNAL: ratio = wallclock time / emulated time 49 /// INTERNAL: ratio = wallclock time / emulated time
50 double CalculateCurrentRatio(); 50 double CalculateCurrentRatio();
51 /// INTERNAL: If we have too many or too few samples downstream, nudge ratio in the appropriate direction. 51 /// INTERNAL: If we have too many or too few samples downstream, nudge ratio in the appropriate
52 /// direction.
52 double CorrectForUnderAndOverflow(double ratio, size_t sample_delay) const; 53 double CorrectForUnderAndOverflow(double ratio, size_t sample_delay) const;
53 /// INTERNAL: Gets the time-stretched samples from SoundTouch. 54 /// INTERNAL: Gets the time-stretched samples from SoundTouch.
54 std::vector<s16> GetSamples(); 55 std::vector<s16> GetSamples();