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