diff options
| author | 2016-09-21 11:29:48 -0700 | |
|---|---|---|
| committer | 2016-09-21 11:29:48 -0700 | |
| commit | d5d2ca8058a0f1c00ab7ca9fe2c058ba47546c0a (patch) | |
| tree | 8a22ca73ff838f3f0090b29a548ae81087fc90ed /src/audio_core | |
| parent | README: Specify master branch for Travis CI badge (diff) | |
| parent | Fix Travis clang-format check (diff) | |
| download | yuzu-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.cpp | 20 | ||||
| -rw-r--r-- | src/audio_core/audio_core.h | 2 | ||||
| -rw-r--r-- | src/audio_core/codec.cpp | 27 | ||||
| -rw-r--r-- | src/audio_core/codec.h | 11 | ||||
| -rw-r--r-- | src/audio_core/hle/common.h | 12 | ||||
| -rw-r--r-- | src/audio_core/hle/dsp.cpp | 17 | ||||
| -rw-r--r-- | src/audio_core/hle/dsp.h | 148 | ||||
| -rw-r--r-- | src/audio_core/hle/filter.cpp | 10 | ||||
| -rw-r--r-- | src/audio_core/hle/filter.h | 14 | ||||
| -rw-r--r-- | src/audio_core/hle/mixers.cpp | 67 | ||||
| -rw-r--r-- | src/audio_core/hle/mixers.h | 10 | ||||
| -rw-r--r-- | src/audio_core/hle/pipe.cpp | 23 | ||||
| -rw-r--r-- | src/audio_core/hle/pipe.h | 12 | ||||
| -rw-r--r-- | src/audio_core/hle/source.cpp | 86 | ||||
| -rw-r--r-- | src/audio_core/hle/source.h | 19 | ||||
| -rw-r--r-- | src/audio_core/interpolate.cpp | 31 | ||||
| -rw-r--r-- | src/audio_core/interpolate.h | 7 | ||||
| -rw-r--r-- | src/audio_core/null_sink.h | 1 | ||||
| -rw-r--r-- | src/audio_core/sdl2_sink.cpp | 25 | ||||
| -rw-r--r-- | src/audio_core/sdl2_sink.h | 1 | ||||
| -rw-r--r-- | src/audio_core/sink.h | 9 | ||||
| -rw-r--r-- | src/audio_core/sink_details.cpp | 6 | ||||
| -rw-r--r-- | src/audio_core/time_stretch.cpp | 13 | ||||
| -rw-r--r-- | src/audio_core/time_stretch.h | 7 |
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 | ||
| 44 | void AddAddressSpace(Kernel::VMManager& address_space) { | 42 | void 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 | ||
| 13 | namespace AudioCore { | 13 | namespace AudioCore { |
| 14 | 14 | ||
| 15 | constexpr int native_sample_rate = 32728; ///< 32kHz | 15 | constexpr int native_sample_rate = 32728; ///< 32kHz |
| 16 | 16 | ||
| 17 | /// Initialise Audio Core | 17 | /// Initialise Audio Core |
| 18 | void Init(); | 18 | void 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 | ||
| 16 | namespace Codec { | 14 | namespace Codec { |
| 17 | 15 | ||
| 18 | StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count, const std::array<s16, 16>& adpcm_coeff, ADPCMState& state) { | 16 | StereoBuffer16 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 | ||
| 85 | StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data, const size_t sample_count) { | 87 | StereoBuffer16 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 | ||
| 104 | StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data, const size_t sample_count) { | 107 | StereoBuffer16 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 | ||
| 12 | namespace Codec { | 11 | namespace 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 | */ |
| 32 | StereoBuffer16 DecodeADPCM(const u8* const data, const size_t sample_count, const std::array<s16, 16>& adpcm_coeff, ADPCMState& state); | 31 | StereoBuffer16 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 | */ |
| 40 | StereoBuffer16 DecodePCM8(const unsigned num_channels, const u8* const data, const size_t sample_count); | 40 | StereoBuffer16 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 | */ |
| 48 | StereoBuffer16 DecodePCM16(const unsigned num_channels, const u8* const data, const size_t sample_count); | 49 | StereoBuffer16 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 | ||
| 12 | namespace DSP { | 11 | namespace DSP { |
| 13 | namespace HLE { | 12 | namespace HLE { |
| 14 | 13 | ||
| 15 | constexpr int num_sources = 24; | 14 | constexpr int num_sources = 24; |
| 16 | constexpr int samples_per_frame = 160; ///< Samples per audio frame at native sample rate | 15 | constexpr 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. |
| 19 | using StereoFrame16 = std::array<std::array<s16, 2>, samples_per_frame>; | 18 | using StereoFrame16 = std::array<std::array<s16, 2>, samples_per_frame>; |
| 20 | 19 | ||
| 21 | /// The DSP is quadraphonic internally. | 20 | /// The DSP is quadraphonic internally. |
| 22 | using QuadFrame32 = std::array<std::array<s32, 4>, samples_per_frame>; | 21 | using 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 | */ |
| 28 | template<typename FrameT, typename FilterT> | 27 | template <typename FrameT, typename FilterT> |
| 29 | void FilterFrame(FrameT& frame, FilterT& filter) { | 28 | void 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 | ||
| 49 | static std::array<Source, num_sources> sources = { | 48 | static 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 | }; |
| 55 | static Mixers mixers; | 53 | static 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() { | |||
| 152 | bool Tick() { | 152 | bool 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; | |||
| 23 | namespace DSP { | 21 | namespace DSP { |
| 24 | namespace HLE { | 22 | namespace 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 | ||
| 36 | constexpr VAddr region0_base = 0x1FF50000; | 34 | constexpr VAddr region0_base = 0x1FF50000; |
| 37 | constexpr VAddr region1_base = 0x1FF70000; | 35 | constexpr 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 | |||
| 59 | private: | 58 | private: |
| 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 | ||
| 123 | struct SourceConfiguration { | 125 | struct 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 | ||
| 314 | struct SourceStatus { | 320 | struct 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); | |||
| 506 | extern std::array<SharedMemory, 2> g_regions; | 515 | extern 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. |
| 509 | static_assert(offsetof(SharedMemory, frame_counter) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | 518 | static_assert(offsetof(SharedMemory, frame_counter) % 2 == 0, |
| 510 | static_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"); |
| 511 | static_assert(offsetof(SharedMemory, source_statuses) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | 520 | static_assert(offsetof(SharedMemory, source_configurations) % 2 == 0, |
| 512 | static_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"); |
| 513 | static_assert(offsetof(SharedMemory, dsp_configuration) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | 522 | static_assert(offsetof(SharedMemory, source_statuses) % 2 == 0, |
| 514 | static_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"); |
| 515 | static_assert(offsetof(SharedMemory, final_samples) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | 524 | static_assert(offsetof(SharedMemory, adpcm_coefficients) % 2 == 0, |
| 516 | static_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"); |
| 517 | static_assert(offsetof(SharedMemory, compressor) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | 526 | static_assert(offsetof(SharedMemory, dsp_configuration) % 2 == 0, |
| 518 | static_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"); |
| 519 | static_assert(offsetof(SharedMemory, unknown10) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | 528 | static_assert(offsetof(SharedMemory, dsp_status) % 2 == 0, |
| 520 | static_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"); |
| 521 | static_assert(offsetof(SharedMemory, unknown12) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | 530 | static_assert(offsetof(SharedMemory, final_samples) % 2 == 0, |
| 522 | static_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"); |
| 523 | static_assert(offsetof(SharedMemory, unknown14) % 2 == 0, "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | 532 | static_assert(offsetof(SharedMemory, intermediate_mix_samples) % 2 == 0, |
| 533 | "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | ||
| 534 | static_assert(offsetof(SharedMemory, compressor) % 2 == 0, | ||
| 535 | "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | ||
| 536 | static_assert(offsetof(SharedMemory, dsp_debug) % 2 == 0, | ||
| 537 | "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | ||
| 538 | static_assert(offsetof(SharedMemory, unknown10) % 2 == 0, | ||
| 539 | "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | ||
| 540 | static_assert(offsetof(SharedMemory, unknown11) % 2 == 0, | ||
| 541 | "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | ||
| 542 | static_assert(offsetof(SharedMemory, unknown12) % 2 == 0, | ||
| 543 | "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | ||
| 544 | static_assert(offsetof(SharedMemory, unknown13) % 2 == 0, | ||
| 545 | "Structures in DSP::HLE::SharedMemory must be 2-byte aligned"); | ||
| 546 | static_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 | ||
| 62 | void SourceFilters::SimpleFilter::Configure(SourceConfiguration::Configuration::SimpleFilter config) { | 60 | void 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 | ||
| 91 | void SourceFilters::BiquadFilter::Configure(SourceConfiguration::Configuration::BiquadFilter config) { | 91 | void 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 | ||
| 14 | namespace DSP { | 12 | namespace 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. |
| 18 | class SourceFilters final { | 16 | class SourceFilters final { |
| 19 | public: | 17 | public: |
| 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 | ||
| 23 | DspStatus Mixers::Tick(DspConfiguration& config, | 22 | DspStatus 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 | ||
| 97 | static std::array<s16, 2> AddAndClampToS16(const std::array<s16, 2>& a, const std::array<s16, 2>& b) { | 95 | static 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 | ||
| 104 | void Mixers::DownmixAndMixIntoCurrentFrame(float gain, const QuadFrame32& samples) { | 101 | void 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 | ||
| 137 | void Mixers::AuxReturn(const IntermediateMixSamples& read_samples) { | 140 | void 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 | ||
| 157 | void Mixers::AuxSend(IntermediateMixSamples& write_samples, const std::array<QuadFrame32, 3>& input) { | 163 | void 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 | ||
| 17 | namespace DSP { | 14 | namespace 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 | ||
| 12 | namespace DSP { | 11 | namespace 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 | }; |
| 24 | constexpr size_t NUM_DSP_PIPE = 8; | 23 | constexpr 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); | |||
| 52 | enum class DspState { | 53 | enum 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 |
| 58 | DspState GetDspState(); | 60 | DspState 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 | ||
| 18 | namespace DSP { | 15 | namespace DSP { |
| 19 | namespace HLE { | 16 | namespace HLE { |
| 20 | 17 | ||
| 21 | SourceStatus::Status Source::Tick(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]) { | 18 | SourceStatus::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 | ||
| 50 | void Source::ParseConfig(SourceConfiguration::Configuration& config, const s16_le (&adpcm_coeffs)[16]) { | 48 | void 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 | |||
| 234 | bool Source::DequeueBuffer() { | 243 | bool 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 | ||
| 19 | namespace DSP { | 17 | namespace 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. |
| 19 | template <typename Function> | 18 | template <typename Function> |
| 20 | static StereoBuffer16 StepOverSamples(State& state, const StereoBuffer16& input, float rate_multiplier, Function fn) { | 19 | static 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 | ||
| 65 | StereoBuffer16 None(State& state, const StereoBuffer16& input, float rate_multiplier) { | 65 | StereoBuffer16 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 | ||
| 71 | StereoBuffer16 Linear(State& state, const StereoBuffer16& input, float rate_multiplier) { | 71 | StereoBuffer16 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 | ||
| 12 | namespace AudioInterp { | 11 | namespace 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 | */ |
| 30 | StereoBuffer16 None(State& state, const StereoBuffer16& input, float rate_multiplier); | 30 | StereoBuffer16 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 | */ |
| 39 | StereoBuffer16 Linear(State& state, const StereoBuffer16& input, float rate_multiplier); | 40 | StereoBuffer16 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 | ||
| 17 | namespace AudioCore { | 14 | namespace 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 { | |||
| 100 | void SDL2Sink::Impl::Callback(void* impl_, u8* buffer, int buffer_size_in_bytes) { | 99 | void 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 | ||
| 12 | namespace AudioCore { | 11 | namespace 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 | ||
| 11 | namespace AudioCore { | 10 | namespace 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 | */ |
| 17 | class Sink { | 17 | class Sink { |
| 18 | public: | 18 | public: |
| 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. |
| 18 | const std::vector<SinkDetails> g_sink_details = { | 16 | const 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 | ||
| 29 | constexpr double MIN_DELAY_TIME = 0.05; // Units: seconds | 26 | constexpr double MIN_DELAY_TIME = 0.05; // Units: seconds |
| 30 | constexpr double MAX_DELAY_TIME = 0.25; // Units: seconds | 27 | constexpr double MAX_DELAY_TIME = 0.25; // Units: seconds |
| 31 | constexpr size_t DROP_FRAMES_SAMPLE_DELAY = 16000; // Units: samples | 28 | constexpr size_t DROP_FRAMES_SAMPLE_DELAY = 16000; // Units: samples |
| 32 | 29 | ||
| 33 | constexpr double SMOOTHING_FACTOR = 0.007; | 30 | constexpr 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 | ||
| 11 | namespace AudioCore { | 10 | namespace 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(); |