diff options
Diffstat (limited to 'src/audio_core/common')
| -rw-r--r-- | src/audio_core/common/audio_renderer_parameter.h | 60 | ||||
| -rw-r--r-- | src/audio_core/common/common.h | 138 | ||||
| -rw-r--r-- | src/audio_core/common/feature_support.h | 105 | ||||
| -rw-r--r-- | src/audio_core/common/wave_buffer.h | 35 | ||||
| -rw-r--r-- | src/audio_core/common/workbuffer_allocator.h | 100 |
5 files changed, 438 insertions, 0 deletions
diff --git a/src/audio_core/common/audio_renderer_parameter.h b/src/audio_core/common/audio_renderer_parameter.h new file mode 100644 index 000000000..2f62c383b --- /dev/null +++ b/src/audio_core/common/audio_renderer_parameter.h | |||
| @@ -0,0 +1,60 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <span> | ||
| 7 | |||
| 8 | #include "audio_core/renderer/behavior/behavior_info.h" | ||
| 9 | #include "audio_core/renderer/memory/memory_pool_info.h" | ||
| 10 | #include "audio_core/renderer/upsampler/upsampler_manager.h" | ||
| 11 | #include "common/common_types.h" | ||
| 12 | |||
| 13 | namespace AudioCore { | ||
| 14 | /** | ||
| 15 | * Execution mode of the audio renderer. | ||
| 16 | * Only Auto is currently supported. | ||
| 17 | */ | ||
| 18 | enum class ExecutionMode : u8 { | ||
| 19 | Auto, | ||
| 20 | Manual, | ||
| 21 | }; | ||
| 22 | |||
| 23 | /** | ||
| 24 | * Parameters from the game, passed to the audio renderer for initialisation. | ||
| 25 | */ | ||
| 26 | struct AudioRendererParameterInternal { | ||
| 27 | /* 0x00 */ u32 sample_rate; | ||
| 28 | /* 0x04 */ u32 sample_count; | ||
| 29 | /* 0x08 */ u32 mixes; | ||
| 30 | /* 0x0C */ u32 sub_mixes; | ||
| 31 | /* 0x10 */ u32 voices; | ||
| 32 | /* 0x14 */ u32 sinks; | ||
| 33 | /* 0x18 */ u32 effects; | ||
| 34 | /* 0x1C */ u32 perf_frames; | ||
| 35 | /* 0x20 */ u16 voice_drop_enabled; | ||
| 36 | /* 0x22 */ u8 rendering_device; | ||
| 37 | /* 0x23 */ ExecutionMode execution_mode; | ||
| 38 | /* 0x24 */ u32 splitter_infos; | ||
| 39 | /* 0x28 */ s32 splitter_destinations; | ||
| 40 | /* 0x2C */ u32 external_context_size; | ||
| 41 | /* 0x30 */ u32 revision; | ||
| 42 | /* 0x34 */ char unk34[0x4]; | ||
| 43 | }; | ||
| 44 | static_assert(sizeof(AudioRendererParameterInternal) == 0x38, | ||
| 45 | "AudioRendererParameterInternal has the wrong size!"); | ||
| 46 | |||
| 47 | /** | ||
| 48 | * Context for rendering, contains a bunch of useful fields for the command generator. | ||
| 49 | */ | ||
| 50 | struct AudioRendererSystemContext { | ||
| 51 | s32 session_id; | ||
| 52 | s8 channels; | ||
| 53 | s16 mix_buffer_count; | ||
| 54 | AudioRenderer::BehaviorInfo* behavior; | ||
| 55 | std::span<s32> depop_buffer; | ||
| 56 | AudioRenderer::UpsamplerManager* upsampler_manager; | ||
| 57 | AudioRenderer::MemoryPoolInfo* memory_pool_info; | ||
| 58 | }; | ||
| 59 | |||
| 60 | } // namespace AudioCore | ||
diff --git a/src/audio_core/common/common.h b/src/audio_core/common/common.h new file mode 100644 index 000000000..6abd9be45 --- /dev/null +++ b/src/audio_core/common/common.h | |||
| @@ -0,0 +1,138 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <numeric> | ||
| 7 | #include <span> | ||
| 8 | |||
| 9 | #include "common/assert.h" | ||
| 10 | #include "common/common_funcs.h" | ||
| 11 | #include "common/common_types.h" | ||
| 12 | |||
| 13 | namespace AudioCore { | ||
| 14 | using CpuAddr = std::uintptr_t; | ||
| 15 | |||
| 16 | enum class PlayState : u8 { | ||
| 17 | Started, | ||
| 18 | Stopped, | ||
| 19 | Paused, | ||
| 20 | }; | ||
| 21 | |||
| 22 | enum class SrcQuality : u8 { | ||
| 23 | Medium, | ||
| 24 | High, | ||
| 25 | Low, | ||
| 26 | }; | ||
| 27 | |||
| 28 | enum class SampleFormat : u8 { | ||
| 29 | Invalid, | ||
| 30 | PcmInt8, | ||
| 31 | PcmInt16, | ||
| 32 | PcmInt24, | ||
| 33 | PcmInt32, | ||
| 34 | PcmFloat, | ||
| 35 | Adpcm, | ||
| 36 | }; | ||
| 37 | |||
| 38 | enum class SessionTypes { | ||
| 39 | AudioIn, | ||
| 40 | AudioOut, | ||
| 41 | FinalOutputRecorder, | ||
| 42 | }; | ||
| 43 | |||
| 44 | enum class Channels : u32 { | ||
| 45 | FrontLeft, | ||
| 46 | FrontRight, | ||
| 47 | Center, | ||
| 48 | LFE, | ||
| 49 | BackLeft, | ||
| 50 | BackRight, | ||
| 51 | }; | ||
| 52 | |||
| 53 | // These are used by Delay, Reverb and I3dl2Reverb prior to Revision 11. | ||
| 54 | enum class OldChannels : u32 { | ||
| 55 | FrontLeft, | ||
| 56 | FrontRight, | ||
| 57 | BackLeft, | ||
| 58 | BackRight, | ||
| 59 | Center, | ||
| 60 | LFE, | ||
| 61 | }; | ||
| 62 | |||
| 63 | constexpr u32 BufferCount = 32; | ||
| 64 | |||
| 65 | constexpr u32 MaxRendererSessions = 2; | ||
| 66 | constexpr u32 TargetSampleCount = 240; | ||
| 67 | constexpr u32 TargetSampleRate = 48'000; | ||
| 68 | constexpr u32 MaxChannels = 6; | ||
| 69 | constexpr u32 MaxMixBuffers = 24; | ||
| 70 | constexpr u32 MaxWaveBuffers = 4; | ||
| 71 | constexpr s32 LowestVoicePriority = 0xFF; | ||
| 72 | constexpr s32 HighestVoicePriority = 0; | ||
| 73 | constexpr u32 BufferAlignment = 0x40; | ||
| 74 | constexpr u32 WorkbufferAlignment = 0x1000; | ||
| 75 | constexpr s32 FinalMixId = 0; | ||
| 76 | constexpr s32 InvalidDistanceFromFinalMix = std::numeric_limits<s32>::min(); | ||
| 77 | constexpr s32 UnusedSplitterId = -1; | ||
| 78 | constexpr s32 UnusedMixId = std::numeric_limits<s32>::max(); | ||
| 79 | constexpr u32 InvalidNodeId = 0xF0000000; | ||
| 80 | constexpr s32 InvalidProcessOrder = -1; | ||
| 81 | constexpr u32 MaxBiquadFilters = 2; | ||
| 82 | constexpr u32 MaxEffects = 256; | ||
| 83 | |||
| 84 | constexpr bool IsChannelCountValid(u16 channel_count) { | ||
| 85 | return channel_count <= 6 && | ||
| 86 | (channel_count == 1 || channel_count == 2 || channel_count == 4 || channel_count == 6); | ||
| 87 | } | ||
| 88 | |||
| 89 | constexpr void UseOldChannelMapping(std::span<s16> inputs, std::span<s16> outputs) { | ||
| 90 | constexpr auto old_center{static_cast<u32>(OldChannels::Center)}; | ||
| 91 | constexpr auto new_center{static_cast<u32>(Channels::Center)}; | ||
| 92 | constexpr auto old_lfe{static_cast<u32>(OldChannels::LFE)}; | ||
| 93 | constexpr auto new_lfe{static_cast<u32>(Channels::LFE)}; | ||
| 94 | |||
| 95 | auto center{inputs[old_center]}; | ||
| 96 | auto lfe{inputs[old_lfe]}; | ||
| 97 | inputs[old_center] = inputs[new_center]; | ||
| 98 | inputs[old_lfe] = inputs[new_lfe]; | ||
| 99 | inputs[new_center] = center; | ||
| 100 | inputs[new_lfe] = lfe; | ||
| 101 | |||
| 102 | center = outputs[old_center]; | ||
| 103 | lfe = outputs[old_lfe]; | ||
| 104 | outputs[old_center] = outputs[new_center]; | ||
| 105 | outputs[old_lfe] = outputs[new_lfe]; | ||
| 106 | outputs[new_center] = center; | ||
| 107 | outputs[new_lfe] = lfe; | ||
| 108 | } | ||
| 109 | |||
| 110 | constexpr u32 GetSplitterInParamHeaderMagic() { | ||
| 111 | return Common::MakeMagic('S', 'N', 'D', 'H'); | ||
| 112 | } | ||
| 113 | |||
| 114 | constexpr u32 GetSplitterInfoMagic() { | ||
| 115 | return Common::MakeMagic('S', 'N', 'D', 'I'); | ||
| 116 | } | ||
| 117 | |||
| 118 | constexpr u32 GetSplitterSendDataMagic() { | ||
| 119 | return Common::MakeMagic('S', 'N', 'D', 'D'); | ||
| 120 | } | ||
| 121 | |||
| 122 | constexpr size_t GetSampleFormatByteSize(SampleFormat format) { | ||
| 123 | switch (format) { | ||
| 124 | case SampleFormat::PcmInt8: | ||
| 125 | return 1; | ||
| 126 | case SampleFormat::PcmInt16: | ||
| 127 | return 2; | ||
| 128 | case SampleFormat::PcmInt24: | ||
| 129 | return 3; | ||
| 130 | case SampleFormat::PcmInt32: | ||
| 131 | case SampleFormat::PcmFloat: | ||
| 132 | return 4; | ||
| 133 | default: | ||
| 134 | return 2; | ||
| 135 | } | ||
| 136 | } | ||
| 137 | |||
| 138 | } // namespace AudioCore | ||
diff --git a/src/audio_core/common/feature_support.h b/src/audio_core/common/feature_support.h new file mode 100644 index 000000000..55c9e690d --- /dev/null +++ b/src/audio_core/common/feature_support.h | |||
| @@ -0,0 +1,105 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <map> | ||
| 7 | #include <ranges> | ||
| 8 | #include <tuple> | ||
| 9 | |||
| 10 | #include "common/assert.h" | ||
| 11 | #include "common/common_funcs.h" | ||
| 12 | #include "common/common_types.h" | ||
| 13 | |||
| 14 | namespace AudioCore { | ||
| 15 | constexpr u32 CurrentRevision = 11; | ||
| 16 | |||
| 17 | enum class SupportTags { | ||
| 18 | CommandProcessingTimeEstimatorVersion4, | ||
| 19 | CommandProcessingTimeEstimatorVersion3, | ||
| 20 | CommandProcessingTimeEstimatorVersion2, | ||
| 21 | MultiTapBiquadFilterProcessing, | ||
| 22 | EffectInfoVer2, | ||
| 23 | WaveBufferVer2, | ||
| 24 | BiquadFilterFloatProcessing, | ||
| 25 | VolumeMixParameterPrecisionQ23, | ||
| 26 | MixInParameterDirtyOnlyUpdate, | ||
| 27 | BiquadFilterEffectStateClearBugFix, | ||
| 28 | VoicePlayedSampleCountResetAtLoopPoint, | ||
| 29 | VoicePitchAndSrcSkipped, | ||
| 30 | SplitterBugFix, | ||
| 31 | FlushVoiceWaveBuffers, | ||
| 32 | ElapsedFrameCount, | ||
| 33 | AudioRendererVariadicCommandBufferSize, | ||
| 34 | PerformanceMetricsDataFormatVersion2, | ||
| 35 | AudioRendererProcessingTimeLimit80Percent, | ||
| 36 | AudioRendererProcessingTimeLimit75Percent, | ||
| 37 | AudioRendererProcessingTimeLimit70Percent, | ||
| 38 | AdpcmLoopContextBugFix, | ||
| 39 | Splitter, | ||
| 40 | LongSizePreDelay, | ||
| 41 | AudioUsbDeviceOutput, | ||
| 42 | DeviceApiVersion2, | ||
| 43 | DelayChannelMappingChange, | ||
| 44 | ReverbChannelMappingChange, | ||
| 45 | I3dl2ReverbChannelMappingChange, | ||
| 46 | |||
| 47 | // Not a real tag, just here to get the count. | ||
| 48 | Size | ||
| 49 | }; | ||
| 50 | |||
| 51 | constexpr u32 GetRevisionNum(u32 user_revision) { | ||
| 52 | if (user_revision >= 0x100) { | ||
| 53 | user_revision -= Common::MakeMagic('R', 'E', 'V', '0'); | ||
| 54 | user_revision >>= 24; | ||
| 55 | } | ||
| 56 | return user_revision; | ||
| 57 | }; | ||
| 58 | |||
| 59 | constexpr bool CheckFeatureSupported(SupportTags tag, u32 user_revision) { | ||
| 60 | constexpr std::array<std::pair<SupportTags, u32>, static_cast<u32>(SupportTags::Size)> features{ | ||
| 61 | { | ||
| 62 | {SupportTags::AudioRendererProcessingTimeLimit70Percent, 1}, | ||
| 63 | {SupportTags::Splitter, 2}, | ||
| 64 | {SupportTags::AdpcmLoopContextBugFix, 2}, | ||
| 65 | {SupportTags::LongSizePreDelay, 3}, | ||
| 66 | {SupportTags::AudioUsbDeviceOutput, 4}, | ||
| 67 | {SupportTags::AudioRendererProcessingTimeLimit75Percent, 4}, | ||
| 68 | {SupportTags::VoicePlayedSampleCountResetAtLoopPoint, 5}, | ||
| 69 | {SupportTags::VoicePitchAndSrcSkipped, 5}, | ||
| 70 | {SupportTags::SplitterBugFix, 5}, | ||
| 71 | {SupportTags::FlushVoiceWaveBuffers, 5}, | ||
| 72 | {SupportTags::ElapsedFrameCount, 5}, | ||
| 73 | {SupportTags::AudioRendererProcessingTimeLimit80Percent, 5}, | ||
| 74 | {SupportTags::AudioRendererVariadicCommandBufferSize, 5}, | ||
| 75 | {SupportTags::PerformanceMetricsDataFormatVersion2, 5}, | ||
| 76 | {SupportTags::CommandProcessingTimeEstimatorVersion2, 5}, | ||
| 77 | {SupportTags::BiquadFilterEffectStateClearBugFix, 6}, | ||
| 78 | {SupportTags::BiquadFilterFloatProcessing, 7}, | ||
| 79 | {SupportTags::VolumeMixParameterPrecisionQ23, 7}, | ||
| 80 | {SupportTags::MixInParameterDirtyOnlyUpdate, 7}, | ||
| 81 | {SupportTags::WaveBufferVer2, 8}, | ||
| 82 | {SupportTags::CommandProcessingTimeEstimatorVersion3, 8}, | ||
| 83 | {SupportTags::EffectInfoVer2, 9}, | ||
| 84 | {SupportTags::CommandProcessingTimeEstimatorVersion4, 10}, | ||
| 85 | {SupportTags::MultiTapBiquadFilterProcessing, 10}, | ||
| 86 | {SupportTags::DelayChannelMappingChange, 11}, | ||
| 87 | {SupportTags::ReverbChannelMappingChange, 11}, | ||
| 88 | {SupportTags::I3dl2ReverbChannelMappingChange, 11}, | ||
| 89 | }}; | ||
| 90 | |||
| 91 | const auto& feature = | ||
| 92 | std::ranges::find_if(features, [tag](const auto& entry) { return entry.first == tag; }); | ||
| 93 | if (feature == features.cend()) { | ||
| 94 | LOG_ERROR(Service_Audio, "Invalid SupportTag {}!", static_cast<u32>(tag)); | ||
| 95 | return false; | ||
| 96 | } | ||
| 97 | user_revision = GetRevisionNum(user_revision); | ||
| 98 | return (*feature).second <= user_revision; | ||
| 99 | } | ||
| 100 | |||
| 101 | constexpr bool CheckValidRevision(u32 user_revision) { | ||
| 102 | return GetRevisionNum(user_revision) <= CurrentRevision; | ||
| 103 | }; | ||
| 104 | |||
| 105 | } // namespace AudioCore | ||
diff --git a/src/audio_core/common/wave_buffer.h b/src/audio_core/common/wave_buffer.h new file mode 100644 index 000000000..fc478ef79 --- /dev/null +++ b/src/audio_core/common/wave_buffer.h | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/common_types.h" | ||
| 7 | |||
| 8 | namespace AudioCore { | ||
| 9 | |||
| 10 | struct WaveBufferVersion1 { | ||
| 11 | CpuAddr buffer; | ||
| 12 | u64 buffer_size; | ||
| 13 | u32 start_offset; | ||
| 14 | u32 end_offset; | ||
| 15 | bool loop; | ||
| 16 | bool stream_ended; | ||
| 17 | CpuAddr context; | ||
| 18 | u64 context_size; | ||
| 19 | }; | ||
| 20 | |||
| 21 | struct WaveBufferVersion2 { | ||
| 22 | CpuAddr buffer; | ||
| 23 | CpuAddr context; | ||
| 24 | u64 buffer_size; | ||
| 25 | u64 context_size; | ||
| 26 | u32 start_offset; | ||
| 27 | u32 end_offset; | ||
| 28 | u32 loop_start_offset; | ||
| 29 | u32 loop_end_offset; | ||
| 30 | s32 loop_count; | ||
| 31 | bool loop; | ||
| 32 | bool stream_ended; | ||
| 33 | }; | ||
| 34 | |||
| 35 | } // namespace AudioCore | ||
diff --git a/src/audio_core/common/workbuffer_allocator.h b/src/audio_core/common/workbuffer_allocator.h new file mode 100644 index 000000000..fb89f97fe --- /dev/null +++ b/src/audio_core/common/workbuffer_allocator.h | |||
| @@ -0,0 +1,100 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <span> | ||
| 7 | |||
| 8 | #include "common/alignment.h" | ||
| 9 | #include "common/assert.h" | ||
| 10 | #include "common/common_types.h" | ||
| 11 | |||
| 12 | namespace AudioCore { | ||
| 13 | /** | ||
| 14 | * Responsible for allocating up a workbuffer into multiple pieces. | ||
| 15 | * Takes in a buffer and size (it does not own them), and allocates up the buffer via Allocate. | ||
| 16 | */ | ||
| 17 | class WorkbufferAllocator { | ||
| 18 | public: | ||
| 19 | explicit WorkbufferAllocator(std::span<u8> buffer_, u64 size_) | ||
| 20 | : buffer{reinterpret_cast<u64>(buffer_.data())}, size{size_} {} | ||
| 21 | |||
| 22 | /** | ||
| 23 | * Allocate the given count of T elements, aligned to alignment. | ||
| 24 | * | ||
| 25 | * @param count - The number of elements to allocate. | ||
| 26 | * @param alignment - The required starting alignment. | ||
| 27 | * @return Non-owning container of allocated elements. | ||
| 28 | */ | ||
| 29 | template <typename T> | ||
| 30 | std::span<T> Allocate(u64 count, u64 alignment) { | ||
| 31 | u64 out{0}; | ||
| 32 | u64 byte_size{count * sizeof(T)}; | ||
| 33 | |||
| 34 | if (byte_size > 0) { | ||
| 35 | auto current{buffer + offset}; | ||
| 36 | auto aligned_buffer{Common::AlignUp(current, alignment)}; | ||
| 37 | if (aligned_buffer + byte_size <= buffer + size) { | ||
| 38 | out = aligned_buffer; | ||
| 39 | offset = byte_size - buffer + aligned_buffer; | ||
| 40 | } else { | ||
| 41 | LOG_ERROR( | ||
| 42 | Service_Audio, | ||
| 43 | "Allocated buffer was too small to hold new alloc.\nAllocator size={:08X}, " | ||
| 44 | "offset={:08X}.\nAttempting to allocate {:08X} with alignment={:02X}", | ||
| 45 | size, offset, byte_size, alignment); | ||
| 46 | count = 0; | ||
| 47 | } | ||
| 48 | } | ||
| 49 | |||
| 50 | return std::span<T>(reinterpret_cast<T*>(out), count); | ||
| 51 | } | ||
| 52 | |||
| 53 | /** | ||
| 54 | * Align the current offset to the given alignment. | ||
| 55 | * | ||
| 56 | * @param alignment - The required starting alignment. | ||
| 57 | */ | ||
| 58 | void Align(u64 alignment) { | ||
| 59 | auto current{buffer + offset}; | ||
| 60 | auto aligned_buffer{Common::AlignUp(current, alignment)}; | ||
| 61 | offset = 0 - buffer + aligned_buffer; | ||
| 62 | } | ||
| 63 | |||
| 64 | /** | ||
| 65 | * Get the current buffer offset. | ||
| 66 | * | ||
| 67 | * @return The current allocating offset. | ||
| 68 | */ | ||
| 69 | u64 GetCurrentOffset() const { | ||
| 70 | return offset; | ||
| 71 | } | ||
| 72 | |||
| 73 | /** | ||
| 74 | * Get the current buffer size. | ||
| 75 | * | ||
| 76 | * @return The size of the current buffer. | ||
| 77 | */ | ||
| 78 | u64 GetSize() const { | ||
| 79 | return size; | ||
| 80 | } | ||
| 81 | |||
| 82 | /** | ||
| 83 | * Get the remaining size that can be allocated. | ||
| 84 | * | ||
| 85 | * @return The remaining size left in the buffer. | ||
| 86 | */ | ||
| 87 | u64 GetRemainingSize() const { | ||
| 88 | return size - offset; | ||
| 89 | } | ||
| 90 | |||
| 91 | private: | ||
| 92 | /// The buffer into which we are allocating. | ||
| 93 | u64 buffer; | ||
| 94 | /// Size of the buffer we're allocating to. | ||
| 95 | u64 size; | ||
| 96 | /// Current offset into the buffer, an error will be thrown if it exceeds size. | ||
| 97 | u64 offset{}; | ||
| 98 | }; | ||
| 99 | |||
| 100 | } // namespace AudioCore | ||