diff options
| author | 2023-08-31 15:09:15 +0100 | |
|---|---|---|
| committer | 2023-09-16 11:56:25 -0400 | |
| commit | 67e2d5c28b8423c4f3f1d5b00f87325684158a6f (patch) | |
| tree | e419a2bb6c064ddc69a49046705b6187772fee48 /src/audio_core/opus | |
| parent | Merge pull request #11519 from german77/system-policy (diff) | |
| download | yuzu-67e2d5c28b8423c4f3f1d5b00f87325684158a6f.tar.gz yuzu-67e2d5c28b8423c4f3f1d5b00f87325684158a6f.tar.xz yuzu-67e2d5c28b8423c4f3f1d5b00f87325684158a6f.zip | |
Reimplement HardwareOpus
Diffstat (limited to 'src/audio_core/opus')
| -rw-r--r-- | src/audio_core/opus/decoder.cpp | 179 | ||||
| -rw-r--r-- | src/audio_core/opus/decoder.h | 53 | ||||
| -rw-r--r-- | src/audio_core/opus/decoder_manager.cpp | 102 | ||||
| -rw-r--r-- | src/audio_core/opus/decoder_manager.h | 38 | ||||
| -rw-r--r-- | src/audio_core/opus/hardware_opus.cpp | 241 | ||||
| -rw-r--r-- | src/audio_core/opus/hardware_opus.h | 45 | ||||
| -rw-r--r-- | src/audio_core/opus/parameters.h | 54 |
7 files changed, 712 insertions, 0 deletions
diff --git a/src/audio_core/opus/decoder.cpp b/src/audio_core/opus/decoder.cpp new file mode 100644 index 000000000..5b23fce14 --- /dev/null +++ b/src/audio_core/opus/decoder.cpp | |||
| @@ -0,0 +1,179 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "audio_core/opus/decoder.h" | ||
| 5 | #include "audio_core/opus/hardware_opus.h" | ||
| 6 | #include "audio_core/opus/parameters.h" | ||
| 7 | #include "common/alignment.h" | ||
| 8 | #include "common/swap.h" | ||
| 9 | #include "core/core.h" | ||
| 10 | |||
| 11 | namespace AudioCore::OpusDecoder { | ||
| 12 | using namespace Service::Audio; | ||
| 13 | namespace { | ||
| 14 | OpusPacketHeader ReverseHeader(OpusPacketHeader header) { | ||
| 15 | OpusPacketHeader out; | ||
| 16 | out.size = Common::swap32(header.size); | ||
| 17 | out.final_range = Common::swap32(header.final_range); | ||
| 18 | return out; | ||
| 19 | } | ||
| 20 | } // namespace | ||
| 21 | |||
| 22 | OpusDecoder::OpusDecoder(Core::System& system_, HardwareOpus& hardware_opus_) | ||
| 23 | : system{system_}, hardware_opus{hardware_opus_} {} | ||
| 24 | |||
| 25 | OpusDecoder::~OpusDecoder() { | ||
| 26 | if (decode_object_initialized) { | ||
| 27 | hardware_opus.ShutdownDecodeObject(shared_buffer.get(), shared_buffer_size); | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | Result OpusDecoder::Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory, | ||
| 32 | u64 transfer_memory_size) { | ||
| 33 | auto frame_size{params.use_large_frame_size ? 5760 : 1920}; | ||
| 34 | shared_buffer_size = transfer_memory_size; | ||
| 35 | shared_buffer = std::make_unique<u8[]>(shared_buffer_size); | ||
| 36 | shared_memory_mapped = true; | ||
| 37 | |||
| 38 | buffer_size = | ||
| 39 | Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 16); | ||
| 40 | |||
| 41 | out_data = {shared_buffer.get() + shared_buffer_size - buffer_size, buffer_size}; | ||
| 42 | size_t in_data_size{0x600u}; | ||
| 43 | in_data = {out_data.data() - in_data_size, in_data_size}; | ||
| 44 | |||
| 45 | ON_RESULT_FAILURE { | ||
| 46 | if (shared_memory_mapped) { | ||
| 47 | shared_memory_mapped = false; | ||
| 48 | ASSERT(R_SUCCEEDED(hardware_opus.UnmapMemory(shared_buffer.get(), shared_buffer_size))); | ||
| 49 | } | ||
| 50 | }; | ||
| 51 | |||
| 52 | R_TRY(hardware_opus.InitializeDecodeObject(params.sample_rate, params.channel_count, | ||
| 53 | shared_buffer.get(), shared_buffer_size)); | ||
| 54 | |||
| 55 | sample_rate = params.sample_rate; | ||
| 56 | channel_count = params.channel_count; | ||
| 57 | use_large_frame_size = params.use_large_frame_size; | ||
| 58 | decode_object_initialized = true; | ||
| 59 | R_SUCCEED(); | ||
| 60 | } | ||
| 61 | |||
| 62 | Result OpusDecoder::Initialize(OpusMultiStreamParametersEx& params, | ||
| 63 | Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size) { | ||
| 64 | auto frame_size{params.use_large_frame_size ? 5760 : 1920}; | ||
| 65 | shared_buffer_size = transfer_memory_size; | ||
| 66 | shared_buffer = std::make_unique<u8[]>(shared_buffer_size); | ||
| 67 | shared_memory_mapped = true; | ||
| 68 | |||
| 69 | buffer_size = | ||
| 70 | Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 16); | ||
| 71 | |||
| 72 | out_data = {shared_buffer.get() + shared_buffer_size - buffer_size, buffer_size}; | ||
| 73 | size_t in_data_size{Common::AlignUp(1500ull * params.total_stream_count, 64u)}; | ||
| 74 | in_data = {out_data.data() - in_data_size, in_data_size}; | ||
| 75 | |||
| 76 | ON_RESULT_FAILURE { | ||
| 77 | if (shared_memory_mapped) { | ||
| 78 | shared_memory_mapped = false; | ||
| 79 | ASSERT(R_SUCCEEDED(hardware_opus.UnmapMemory(shared_buffer.get(), shared_buffer_size))); | ||
| 80 | } | ||
| 81 | }; | ||
| 82 | |||
| 83 | R_TRY(hardware_opus.InitializeMultiStreamDecodeObject( | ||
| 84 | params.sample_rate, params.channel_count, params.total_stream_count, | ||
| 85 | params.stereo_stream_count, params.mappings.data(), shared_buffer.get(), | ||
| 86 | shared_buffer_size)); | ||
| 87 | |||
| 88 | sample_rate = params.sample_rate; | ||
| 89 | channel_count = params.channel_count; | ||
| 90 | total_stream_count = params.total_stream_count; | ||
| 91 | stereo_stream_count = params.stereo_stream_count; | ||
| 92 | use_large_frame_size = params.use_large_frame_size; | ||
| 93 | decode_object_initialized = true; | ||
| 94 | R_SUCCEED(); | ||
| 95 | } | ||
| 96 | |||
| 97 | Result OpusDecoder::DecodeInterleaved(u32* out_data_size, u64* out_time_taken, | ||
| 98 | u32* out_sample_count, std::span<const u8> input_data, | ||
| 99 | std::span<u8> output_data, bool reset) { | ||
| 100 | u32 out_samples; | ||
| 101 | u64 time_taken{}; | ||
| 102 | |||
| 103 | R_UNLESS(input_data.size_bytes() > sizeof(OpusPacketHeader), ResultInputDataTooSmall); | ||
| 104 | |||
| 105 | auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())}; | ||
| 106 | OpusPacketHeader header{ReverseHeader(*header_p)}; | ||
| 107 | |||
| 108 | R_UNLESS(in_data.size_bytes() >= header.size && | ||
| 109 | header.size + sizeof(OpusPacketHeader) <= input_data.size_bytes(), | ||
| 110 | ResultBufferTooSmall); | ||
| 111 | |||
| 112 | if (!shared_memory_mapped) { | ||
| 113 | R_TRY(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size)); | ||
| 114 | shared_memory_mapped = true; | ||
| 115 | } | ||
| 116 | |||
| 117 | std::memcpy(in_data.data(), input_data.data() + sizeof(OpusPacketHeader), header.size); | ||
| 118 | |||
| 119 | R_TRY(hardware_opus.DecodeInterleaved(out_samples, out_data.data(), out_data.size_bytes(), | ||
| 120 | channel_count, in_data.data(), header.size, | ||
| 121 | shared_buffer.get(), time_taken, reset)); | ||
| 122 | |||
| 123 | std::memcpy(output_data.data(), out_data.data(), out_samples * channel_count * sizeof(s16)); | ||
| 124 | |||
| 125 | *out_data_size = header.size + sizeof(OpusPacketHeader); | ||
| 126 | *out_sample_count = out_samples; | ||
| 127 | if (out_time_taken) { | ||
| 128 | *out_time_taken = time_taken / 1000; | ||
| 129 | } | ||
| 130 | R_SUCCEED(); | ||
| 131 | } | ||
| 132 | |||
| 133 | Result OpusDecoder::SetContext([[maybe_unused]] std::span<const u8> context) { | ||
| 134 | R_SUCCEED_IF(shared_memory_mapped); | ||
| 135 | shared_memory_mapped = true; | ||
| 136 | R_RETURN(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size)); | ||
| 137 | } | ||
| 138 | |||
| 139 | Result OpusDecoder::DecodeInterleavedForMultiStream(u32* out_data_size, u64* out_time_taken, | ||
| 140 | u32* out_sample_count, | ||
| 141 | std::span<const u8> input_data, | ||
| 142 | std::span<u8> output_data, bool reset) { | ||
| 143 | u32 out_samples; | ||
| 144 | u64 time_taken{}; | ||
| 145 | |||
| 146 | R_UNLESS(input_data.size_bytes() > sizeof(OpusPacketHeader), ResultInputDataTooSmall); | ||
| 147 | |||
| 148 | auto* header_p{reinterpret_cast<const OpusPacketHeader*>(input_data.data())}; | ||
| 149 | OpusPacketHeader header{ReverseHeader(*header_p)}; | ||
| 150 | |||
| 151 | LOG_ERROR(Service_Audio, "header size 0x{:X} input data size 0x{:X} in_data size 0x{:X}", | ||
| 152 | header.size, input_data.size_bytes(), in_data.size_bytes()); | ||
| 153 | |||
| 154 | R_UNLESS(in_data.size_bytes() >= header.size && | ||
| 155 | header.size + sizeof(OpusPacketHeader) <= input_data.size_bytes(), | ||
| 156 | ResultBufferTooSmall); | ||
| 157 | |||
| 158 | if (!shared_memory_mapped) { | ||
| 159 | R_TRY(hardware_opus.MapMemory(shared_buffer.get(), shared_buffer_size)); | ||
| 160 | shared_memory_mapped = true; | ||
| 161 | } | ||
| 162 | |||
| 163 | std::memcpy(in_data.data(), input_data.data() + sizeof(OpusPacketHeader), header.size); | ||
| 164 | |||
| 165 | R_TRY(hardware_opus.DecodeInterleavedForMultiStream( | ||
| 166 | out_samples, out_data.data(), out_data.size_bytes(), channel_count, in_data.data(), | ||
| 167 | header.size, shared_buffer.get(), time_taken, reset)); | ||
| 168 | |||
| 169 | std::memcpy(output_data.data(), out_data.data(), out_samples * channel_count * sizeof(s16)); | ||
| 170 | |||
| 171 | *out_data_size = header.size + sizeof(OpusPacketHeader); | ||
| 172 | *out_sample_count = out_samples; | ||
| 173 | if (out_time_taken) { | ||
| 174 | *out_time_taken = time_taken / 1000; | ||
| 175 | } | ||
| 176 | R_SUCCEED(); | ||
| 177 | } | ||
| 178 | |||
| 179 | } // namespace AudioCore::OpusDecoder | ||
diff --git a/src/audio_core/opus/decoder.h b/src/audio_core/opus/decoder.h new file mode 100644 index 000000000..d08d8a4a4 --- /dev/null +++ b/src/audio_core/opus/decoder.h | |||
| @@ -0,0 +1,53 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 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/opus/parameters.h" | ||
| 9 | #include "common/common_types.h" | ||
| 10 | #include "core/hle/kernel/k_transfer_memory.h" | ||
| 11 | #include "core/hle/service/audio/errors.h" | ||
| 12 | |||
| 13 | namespace Core { | ||
| 14 | class System; | ||
| 15 | } | ||
| 16 | |||
| 17 | namespace AudioCore::OpusDecoder { | ||
| 18 | class HardwareOpus; | ||
| 19 | |||
| 20 | class OpusDecoder { | ||
| 21 | public: | ||
| 22 | explicit OpusDecoder(Core::System& system, HardwareOpus& hardware_opus_); | ||
| 23 | ~OpusDecoder(); | ||
| 24 | |||
| 25 | Result Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory, | ||
| 26 | u64 transfer_memory_size); | ||
| 27 | Result Initialize(OpusMultiStreamParametersEx& params, Kernel::KTransferMemory* transfer_memory, | ||
| 28 | u64 transfer_memory_size); | ||
| 29 | Result DecodeInterleaved(u32* out_data_size, u64* out_time_taken, u32* out_sample_count, | ||
| 30 | std::span<const u8> input_data, std::span<u8> output_data, bool reset); | ||
| 31 | Result SetContext([[maybe_unused]] std::span<const u8> context); | ||
| 32 | Result DecodeInterleavedForMultiStream(u32* out_data_size, u64* out_time_taken, | ||
| 33 | u32* out_sample_count, std::span<const u8> input_data, | ||
| 34 | std::span<u8> output_data, bool reset); | ||
| 35 | |||
| 36 | private: | ||
| 37 | Core::System& system; | ||
| 38 | HardwareOpus& hardware_opus; | ||
| 39 | std::unique_ptr<u8[]> shared_buffer{}; | ||
| 40 | u64 shared_buffer_size; | ||
| 41 | std::span<u8> in_data{}; | ||
| 42 | std::span<u8> out_data{}; | ||
| 43 | u64 buffer_size{}; | ||
| 44 | s32 sample_rate{}; | ||
| 45 | s32 channel_count{}; | ||
| 46 | bool use_large_frame_size{false}; | ||
| 47 | s32 total_stream_count{}; | ||
| 48 | s32 stereo_stream_count{}; | ||
| 49 | bool shared_memory_mapped{false}; | ||
| 50 | bool decode_object_initialized{false}; | ||
| 51 | }; | ||
| 52 | |||
| 53 | } // namespace AudioCore::OpusDecoder | ||
diff --git a/src/audio_core/opus/decoder_manager.cpp b/src/audio_core/opus/decoder_manager.cpp new file mode 100644 index 000000000..4a5382973 --- /dev/null +++ b/src/audio_core/opus/decoder_manager.cpp | |||
| @@ -0,0 +1,102 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "audio_core/adsp/apps/opus/opus_decoder.h" | ||
| 5 | #include "audio_core/opus/decoder_manager.h" | ||
| 6 | #include "common/alignment.h" | ||
| 7 | #include "core/core.h" | ||
| 8 | |||
| 9 | namespace AudioCore::OpusDecoder { | ||
| 10 | using namespace Service::Audio; | ||
| 11 | |||
| 12 | namespace { | ||
| 13 | bool IsValidChannelCount(u32 channel_count) { | ||
| 14 | return channel_count == 1 || channel_count == 2; | ||
| 15 | } | ||
| 16 | |||
| 17 | bool IsValidMultiStreamChannelCount(u32 channel_count) { | ||
| 18 | return channel_count > 0 && channel_count <= OpusStreamCountMax; | ||
| 19 | } | ||
| 20 | |||
| 21 | bool IsValidSampleRate(u32 sample_rate) { | ||
| 22 | return sample_rate == 8'000 || sample_rate == 12'000 || sample_rate == 16'000 || | ||
| 23 | sample_rate == 24'000 || sample_rate == 48'000; | ||
| 24 | } | ||
| 25 | |||
| 26 | bool IsValidStreamCount(u32 channel_count, u32 total_stream_count, u32 stereo_stream_count) { | ||
| 27 | return total_stream_count > 0 && stereo_stream_count > 0 && | ||
| 28 | stereo_stream_count <= total_stream_count && | ||
| 29 | total_stream_count + stereo_stream_count <= channel_count; | ||
| 30 | } | ||
| 31 | |||
| 32 | } // namespace | ||
| 33 | |||
| 34 | OpusDecoderManager::OpusDecoderManager(Core::System& system_) | ||
| 35 | : system{system_}, hardware_opus{system} { | ||
| 36 | for (u32 i = 0; i < MaxChannels; i++) { | ||
| 37 | required_workbuffer_sizes[i] = hardware_opus.GetWorkBufferSize(1 + i); | ||
| 38 | } | ||
| 39 | } | ||
| 40 | |||
| 41 | Result OpusDecoderManager::GetWorkBufferSize(OpusParameters& params, u64& out_size) { | ||
| 42 | OpusParametersEx ex{ | ||
| 43 | .sample_rate = params.sample_rate, | ||
| 44 | .channel_count = params.channel_count, | ||
| 45 | .use_large_frame_size = false, | ||
| 46 | }; | ||
| 47 | R_RETURN(GetWorkBufferSizeExEx(ex, out_size)); | ||
| 48 | } | ||
| 49 | |||
| 50 | Result OpusDecoderManager::GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size) { | ||
| 51 | R_RETURN(GetWorkBufferSizeExEx(params, out_size)); | ||
| 52 | } | ||
| 53 | |||
| 54 | Result OpusDecoderManager::GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size) { | ||
| 55 | R_UNLESS(IsValidChannelCount(params.channel_count), ResultInvalidOpusChannelCount); | ||
| 56 | R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate); | ||
| 57 | |||
| 58 | auto work_buffer_size{required_workbuffer_sizes[params.channel_count - 1]}; | ||
| 59 | auto frame_size{params.use_large_frame_size ? 5760 : 1920}; | ||
| 60 | work_buffer_size += | ||
| 61 | Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 64); | ||
| 62 | out_size = work_buffer_size + 0x600; | ||
| 63 | R_SUCCEED(); | ||
| 64 | } | ||
| 65 | |||
| 66 | Result OpusDecoderManager::GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params, | ||
| 67 | u64& out_size) { | ||
| 68 | OpusMultiStreamParametersEx ex{ | ||
| 69 | .sample_rate = params.sample_rate, | ||
| 70 | .channel_count = params.channel_count, | ||
| 71 | .total_stream_count = params.total_stream_count, | ||
| 72 | .stereo_stream_count = params.stereo_stream_count, | ||
| 73 | .use_large_frame_size = false, | ||
| 74 | .mappings = {}, | ||
| 75 | }; | ||
| 76 | R_RETURN(GetWorkBufferSizeForMultiStreamExEx(ex, out_size)); | ||
| 77 | } | ||
| 78 | |||
| 79 | Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params, | ||
| 80 | u64& out_size) { | ||
| 81 | R_RETURN(GetWorkBufferSizeForMultiStreamExEx(params, out_size)); | ||
| 82 | } | ||
| 83 | |||
| 84 | Result OpusDecoderManager::GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params, | ||
| 85 | u64& out_size) { | ||
| 86 | R_UNLESS(IsValidMultiStreamChannelCount(params.channel_count), ResultInvalidOpusChannelCount); | ||
| 87 | R_UNLESS(IsValidSampleRate(params.sample_rate), ResultInvalidOpusSampleRate); | ||
| 88 | R_UNLESS(IsValidStreamCount(params.channel_count, params.total_stream_count, | ||
| 89 | params.stereo_stream_count), | ||
| 90 | ResultInvalidOpusSampleRate); | ||
| 91 | |||
| 92 | auto work_buffer_size{hardware_opus.GetWorkBufferSizeForMultiStream( | ||
| 93 | params.total_stream_count, params.stereo_stream_count)}; | ||
| 94 | auto frame_size{params.use_large_frame_size ? 5760 : 1920}; | ||
| 95 | work_buffer_size += Common::AlignUp(1500 * params.total_stream_count, 64); | ||
| 96 | work_buffer_size += | ||
| 97 | Common::AlignUp((frame_size * params.channel_count) / (48'000 / params.sample_rate), 64); | ||
| 98 | out_size = work_buffer_size; | ||
| 99 | R_SUCCEED(); | ||
| 100 | } | ||
| 101 | |||
| 102 | } // namespace AudioCore::OpusDecoder | ||
diff --git a/src/audio_core/opus/decoder_manager.h b/src/audio_core/opus/decoder_manager.h new file mode 100644 index 000000000..466e1967b --- /dev/null +++ b/src/audio_core/opus/decoder_manager.h | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "audio_core/opus/hardware_opus.h" | ||
| 7 | #include "audio_core/opus/parameters.h" | ||
| 8 | #include "common/common_types.h" | ||
| 9 | #include "core/hle/service/audio/errors.h" | ||
| 10 | |||
| 11 | namespace Core { | ||
| 12 | class System; | ||
| 13 | } | ||
| 14 | |||
| 15 | namespace AudioCore::OpusDecoder { | ||
| 16 | |||
| 17 | class OpusDecoderManager { | ||
| 18 | public: | ||
| 19 | OpusDecoderManager(Core::System& system); | ||
| 20 | |||
| 21 | HardwareOpus& GetHardwareOpus() { | ||
| 22 | return hardware_opus; | ||
| 23 | } | ||
| 24 | |||
| 25 | Result GetWorkBufferSize(OpusParameters& params, u64& out_size); | ||
| 26 | Result GetWorkBufferSizeEx(OpusParametersEx& params, u64& out_size); | ||
| 27 | Result GetWorkBufferSizeExEx(OpusParametersEx& params, u64& out_size); | ||
| 28 | Result GetWorkBufferSizeForMultiStream(OpusMultiStreamParameters& params, u64& out_size); | ||
| 29 | Result GetWorkBufferSizeForMultiStreamEx(OpusMultiStreamParametersEx& params, u64& out_size); | ||
| 30 | Result GetWorkBufferSizeForMultiStreamExEx(OpusMultiStreamParametersEx& params, u64& out_size); | ||
| 31 | |||
| 32 | private: | ||
| 33 | Core::System& system; | ||
| 34 | HardwareOpus hardware_opus; | ||
| 35 | std::array<u64, MaxChannels> required_workbuffer_sizes{}; | ||
| 36 | }; | ||
| 37 | |||
| 38 | } // namespace AudioCore::OpusDecoder | ||
diff --git a/src/audio_core/opus/hardware_opus.cpp b/src/audio_core/opus/hardware_opus.cpp new file mode 100644 index 000000000..d6544dcb0 --- /dev/null +++ b/src/audio_core/opus/hardware_opus.cpp | |||
| @@ -0,0 +1,241 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <array> | ||
| 5 | |||
| 6 | #include "audio_core/audio_core.h" | ||
| 7 | #include "audio_core/opus/hardware_opus.h" | ||
| 8 | #include "core/core.h" | ||
| 9 | |||
| 10 | namespace AudioCore::OpusDecoder { | ||
| 11 | namespace { | ||
| 12 | using namespace Service::Audio; | ||
| 13 | |||
| 14 | static constexpr Result ResultCodeFromLibOpusErrorCode(u64 error_code) { | ||
| 15 | s32 error{static_cast<s32>(error_code)}; | ||
| 16 | ASSERT(error <= OPUS_OK); | ||
| 17 | switch (error) { | ||
| 18 | case OPUS_ALLOC_FAIL: | ||
| 19 | R_THROW(ResultLibOpusAllocFail); | ||
| 20 | case OPUS_INVALID_STATE: | ||
| 21 | R_THROW(ResultLibOpusInvalidState); | ||
| 22 | case OPUS_UNIMPLEMENTED: | ||
| 23 | R_THROW(ResultLibOpusUnimplemented); | ||
| 24 | case OPUS_INVALID_PACKET: | ||
| 25 | R_THROW(ResultLibOpusInvalidPacket); | ||
| 26 | case OPUS_INTERNAL_ERROR: | ||
| 27 | R_THROW(ResultLibOpusInternalError); | ||
| 28 | case OPUS_BUFFER_TOO_SMALL: | ||
| 29 | R_THROW(ResultBufferTooSmall); | ||
| 30 | case OPUS_BAD_ARG: | ||
| 31 | R_THROW(ResultLibOpusBadArg); | ||
| 32 | case OPUS_OK: | ||
| 33 | R_RETURN(ResultSuccess); | ||
| 34 | } | ||
| 35 | UNREACHABLE(); | ||
| 36 | } | ||
| 37 | |||
| 38 | } // namespace | ||
| 39 | |||
| 40 | HardwareOpus::HardwareOpus(Core::System& system_) | ||
| 41 | : system{system_}, opus_decoder{system.AudioCore().ADSP().OpusDecoder()} { | ||
| 42 | opus_decoder.SetSharedMemory(shared_memory); | ||
| 43 | } | ||
| 44 | |||
| 45 | u64 HardwareOpus::GetWorkBufferSize(u32 channel) { | ||
| 46 | if (!opus_decoder.IsRunning()) { | ||
| 47 | return 0; | ||
| 48 | } | ||
| 49 | std::scoped_lock l{mutex}; | ||
| 50 | shared_memory.host_send_data[0] = channel; | ||
| 51 | opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::GetWorkBufferSize); | ||
| 52 | auto msg = opus_decoder.Receive(ADSP::Direction::Host); | ||
| 53 | if (msg != ADSP::OpusDecoder::Message::GetWorkBufferSizeOK) { | ||
| 54 | LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", | ||
| 55 | ADSP::OpusDecoder::Message::GetWorkBufferSizeOK, msg); | ||
| 56 | return 0; | ||
| 57 | } | ||
| 58 | return shared_memory.dsp_return_data[0]; | ||
| 59 | } | ||
| 60 | |||
| 61 | u64 HardwareOpus::GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count) { | ||
| 62 | std::scoped_lock l{mutex}; | ||
| 63 | shared_memory.host_send_data[0] = total_stream_count; | ||
| 64 | shared_memory.host_send_data[1] = stereo_stream_count; | ||
| 65 | opus_decoder.Send(ADSP::Direction::DSP, | ||
| 66 | ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStream); | ||
| 67 | auto msg = opus_decoder.Receive(ADSP::Direction::Host); | ||
| 68 | if (msg != ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStreamOK) { | ||
| 69 | LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", | ||
| 70 | ADSP::OpusDecoder::Message::GetWorkBufferSizeForMultiStreamOK, msg); | ||
| 71 | return 0; | ||
| 72 | } | ||
| 73 | return shared_memory.dsp_return_data[0]; | ||
| 74 | } | ||
| 75 | |||
| 76 | Result HardwareOpus::InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer, | ||
| 77 | u64 buffer_size) { | ||
| 78 | std::scoped_lock l{mutex}; | ||
| 79 | shared_memory.host_send_data[0] = (u64)buffer; | ||
| 80 | shared_memory.host_send_data[1] = buffer_size; | ||
| 81 | shared_memory.host_send_data[2] = sample_rate; | ||
| 82 | shared_memory.host_send_data[3] = channel_count; | ||
| 83 | |||
| 84 | opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::InitializeDecodeObject); | ||
| 85 | auto msg = opus_decoder.Receive(ADSP::Direction::Host); | ||
| 86 | if (msg != ADSP::OpusDecoder::Message::InitializeDecodeObjectOK) { | ||
| 87 | LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", | ||
| 88 | ADSP::OpusDecoder::Message::InitializeDecodeObjectOK, msg); | ||
| 89 | R_THROW(ResultInvalidOpusDSPReturnCode); | ||
| 90 | } | ||
| 91 | |||
| 92 | R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); | ||
| 93 | } | ||
| 94 | |||
| 95 | Result HardwareOpus::InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count, | ||
| 96 | u32 total_stream_count, | ||
| 97 | u32 stereo_stream_count, void* mappings, | ||
| 98 | void* buffer, u64 buffer_size) { | ||
| 99 | std::scoped_lock l{mutex}; | ||
| 100 | shared_memory.host_send_data[0] = (u64)buffer; | ||
| 101 | shared_memory.host_send_data[1] = buffer_size; | ||
| 102 | shared_memory.host_send_data[2] = sample_rate; | ||
| 103 | shared_memory.host_send_data[3] = channel_count; | ||
| 104 | shared_memory.host_send_data[4] = total_stream_count; | ||
| 105 | shared_memory.host_send_data[5] = stereo_stream_count; | ||
| 106 | |||
| 107 | ASSERT(channel_count <= MaxChannels); | ||
| 108 | std::memcpy(shared_memory.channel_mapping.data(), mappings, channel_count * sizeof(u8)); | ||
| 109 | |||
| 110 | opus_decoder.Send(ADSP::Direction::DSP, | ||
| 111 | ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObject); | ||
| 112 | auto msg = opus_decoder.Receive(ADSP::Direction::Host); | ||
| 113 | if (msg != ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObjectOK) { | ||
| 114 | LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", | ||
| 115 | ADSP::OpusDecoder::Message::InitializeMultiStreamDecodeObjectOK, msg); | ||
| 116 | R_THROW(ResultInvalidOpusDSPReturnCode); | ||
| 117 | } | ||
| 118 | |||
| 119 | R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); | ||
| 120 | } | ||
| 121 | |||
| 122 | Result HardwareOpus::ShutdownDecodeObject(void* buffer, u64 buffer_size) { | ||
| 123 | std::scoped_lock l{mutex}; | ||
| 124 | shared_memory.host_send_data[0] = (u64)buffer; | ||
| 125 | shared_memory.host_send_data[1] = buffer_size; | ||
| 126 | |||
| 127 | opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::ShutdownDecodeObject); | ||
| 128 | auto msg = opus_decoder.Receive(ADSP::Direction::Host); | ||
| 129 | ASSERT_MSG(msg == ADSP::OpusDecoder::Message::ShutdownDecodeObjectOK, | ||
| 130 | "Expected Opus shutdown code {}, got {}", | ||
| 131 | ADSP::OpusDecoder::Message::ShutdownDecodeObjectOK, msg); | ||
| 132 | |||
| 133 | R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); | ||
| 134 | } | ||
| 135 | |||
| 136 | Result HardwareOpus::ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_size) { | ||
| 137 | std::scoped_lock l{mutex}; | ||
| 138 | shared_memory.host_send_data[0] = (u64)buffer; | ||
| 139 | shared_memory.host_send_data[1] = buffer_size; | ||
| 140 | |||
| 141 | opus_decoder.Send(ADSP::Direction::DSP, | ||
| 142 | ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObject); | ||
| 143 | auto msg = opus_decoder.Receive(ADSP::Direction::Host); | ||
| 144 | ASSERT_MSG(msg == ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObjectOK, | ||
| 145 | "Expected Opus shutdown code {}, got {}", | ||
| 146 | ADSP::OpusDecoder::Message::ShutdownMultiStreamDecodeObjectOK, msg); | ||
| 147 | |||
| 148 | R_RETURN(ResultCodeFromLibOpusErrorCode(shared_memory.dsp_return_data[0])); | ||
| 149 | } | ||
| 150 | |||
| 151 | Result HardwareOpus::DecodeInterleaved(u32& out_sample_count, void* output_data, | ||
| 152 | u64 output_data_size, u32 channel_count, void* input_data, | ||
| 153 | u64 input_data_size, void* buffer, u64& out_time_taken, | ||
| 154 | bool reset) { | ||
| 155 | std::scoped_lock l{mutex}; | ||
| 156 | shared_memory.host_send_data[0] = (u64)buffer; | ||
| 157 | shared_memory.host_send_data[1] = (u64)input_data; | ||
| 158 | shared_memory.host_send_data[2] = input_data_size; | ||
| 159 | shared_memory.host_send_data[3] = (u64)output_data; | ||
| 160 | shared_memory.host_send_data[4] = output_data_size; | ||
| 161 | shared_memory.host_send_data[5] = 0; | ||
| 162 | shared_memory.host_send_data[6] = reset; | ||
| 163 | |||
| 164 | opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::DecodeInterleaved); | ||
| 165 | auto msg = opus_decoder.Receive(ADSP::Direction::Host); | ||
| 166 | if (msg != ADSP::OpusDecoder::Message::DecodeInterleavedOK) { | ||
| 167 | LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", | ||
| 168 | ADSP::OpusDecoder::Message::DecodeInterleavedOK, msg); | ||
| 169 | R_THROW(ResultInvalidOpusDSPReturnCode); | ||
| 170 | } | ||
| 171 | |||
| 172 | auto error_code{static_cast<s32>(shared_memory.dsp_return_data[0])}; | ||
| 173 | if (error_code == OPUS_OK) { | ||
| 174 | out_sample_count = static_cast<u32>(shared_memory.dsp_return_data[1]); | ||
| 175 | out_time_taken = 1000 * shared_memory.dsp_return_data[2]; | ||
| 176 | } | ||
| 177 | R_RETURN(ResultCodeFromLibOpusErrorCode(error_code)); | ||
| 178 | } | ||
| 179 | |||
| 180 | Result HardwareOpus::DecodeInterleavedForMultiStream(u32& out_sample_count, void* output_data, | ||
| 181 | u64 output_data_size, u32 channel_count, | ||
| 182 | void* input_data, u64 input_data_size, | ||
| 183 | void* buffer, u64& out_time_taken, | ||
| 184 | bool reset) { | ||
| 185 | std::scoped_lock l{mutex}; | ||
| 186 | shared_memory.host_send_data[0] = (u64)buffer; | ||
| 187 | shared_memory.host_send_data[1] = (u64)input_data; | ||
| 188 | shared_memory.host_send_data[2] = input_data_size; | ||
| 189 | shared_memory.host_send_data[3] = (u64)output_data; | ||
| 190 | shared_memory.host_send_data[4] = output_data_size; | ||
| 191 | shared_memory.host_send_data[5] = 0; | ||
| 192 | shared_memory.host_send_data[6] = reset; | ||
| 193 | |||
| 194 | opus_decoder.Send(ADSP::Direction::DSP, | ||
| 195 | ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStream); | ||
| 196 | auto msg = opus_decoder.Receive(ADSP::Direction::Host); | ||
| 197 | if (msg != ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK) { | ||
| 198 | LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", | ||
| 199 | ADSP::OpusDecoder::Message::DecodeInterleavedForMultiStreamOK, msg); | ||
| 200 | R_THROW(ResultInvalidOpusDSPReturnCode); | ||
| 201 | } | ||
| 202 | |||
| 203 | auto error_code{static_cast<s32>(shared_memory.dsp_return_data[0])}; | ||
| 204 | if (error_code == OPUS_OK) { | ||
| 205 | out_sample_count = static_cast<u32>(shared_memory.dsp_return_data[1]); | ||
| 206 | out_time_taken = 1000 * shared_memory.dsp_return_data[2]; | ||
| 207 | } | ||
| 208 | R_RETURN(ResultCodeFromLibOpusErrorCode(error_code)); | ||
| 209 | } | ||
| 210 | |||
| 211 | Result HardwareOpus::MapMemory(void* buffer, u64 buffer_size) { | ||
| 212 | std::scoped_lock l{mutex}; | ||
| 213 | shared_memory.host_send_data[0] = (u64)buffer; | ||
| 214 | shared_memory.host_send_data[1] = buffer_size; | ||
| 215 | |||
| 216 | opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::MapMemory); | ||
| 217 | auto msg = opus_decoder.Receive(ADSP::Direction::Host); | ||
| 218 | if (msg != ADSP::OpusDecoder::Message::MapMemoryOK) { | ||
| 219 | LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", | ||
| 220 | ADSP::OpusDecoder::Message::MapMemoryOK, msg); | ||
| 221 | R_THROW(ResultInvalidOpusDSPReturnCode); | ||
| 222 | } | ||
| 223 | R_SUCCEED(); | ||
| 224 | } | ||
| 225 | |||
| 226 | Result HardwareOpus::UnmapMemory(void* buffer, u64 buffer_size) { | ||
| 227 | std::scoped_lock l{mutex}; | ||
| 228 | shared_memory.host_send_data[0] = (u64)buffer; | ||
| 229 | shared_memory.host_send_data[1] = buffer_size; | ||
| 230 | |||
| 231 | opus_decoder.Send(ADSP::Direction::DSP, ADSP::OpusDecoder::Message::UnmapMemory); | ||
| 232 | auto msg = opus_decoder.Receive(ADSP::Direction::Host); | ||
| 233 | if (msg != ADSP::OpusDecoder::Message::UnmapMemoryOK) { | ||
| 234 | LOG_ERROR(Service_Audio, "OpusDecoder returned invalid message. Expected {} got {}", | ||
| 235 | ADSP::OpusDecoder::Message::UnmapMemoryOK, msg); | ||
| 236 | R_THROW(ResultInvalidOpusDSPReturnCode); | ||
| 237 | } | ||
| 238 | R_SUCCEED(); | ||
| 239 | } | ||
| 240 | |||
| 241 | } // namespace AudioCore::OpusDecoder | ||
diff --git a/src/audio_core/opus/hardware_opus.h b/src/audio_core/opus/hardware_opus.h new file mode 100644 index 000000000..7013a6b40 --- /dev/null +++ b/src/audio_core/opus/hardware_opus.h | |||
| @@ -0,0 +1,45 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <mutex> | ||
| 7 | #include <opus.h> | ||
| 8 | |||
| 9 | #include "audio_core/adsp/apps/opus/opus_decoder.h" | ||
| 10 | #include "audio_core/adsp/apps/opus/shared_memory.h" | ||
| 11 | #include "audio_core/adsp/mailbox.h" | ||
| 12 | #include "core/hle/service/audio/errors.h" | ||
| 13 | |||
| 14 | namespace AudioCore::OpusDecoder { | ||
| 15 | class HardwareOpus { | ||
| 16 | public: | ||
| 17 | HardwareOpus(Core::System& system); | ||
| 18 | |||
| 19 | u64 GetWorkBufferSize(u32 channel); | ||
| 20 | u64 GetWorkBufferSizeForMultiStream(u32 total_stream_count, u32 stereo_stream_count); | ||
| 21 | |||
| 22 | Result InitializeDecodeObject(u32 sample_rate, u32 channel_count, void* buffer, | ||
| 23 | u64 buffer_size); | ||
| 24 | Result InitializeMultiStreamDecodeObject(u32 sample_rate, u32 channel_count, | ||
| 25 | u32 totaL_stream_count, u32 stereo_stream_count, | ||
| 26 | void* mappings, void* buffer, u64 buffer_size); | ||
| 27 | Result ShutdownDecodeObject(void* buffer, u64 buffer_size); | ||
| 28 | Result ShutdownMultiStreamDecodeObject(void* buffer, u64 buffer_size); | ||
| 29 | Result DecodeInterleaved(u32& out_sample_count, void* output_data, u64 output_data_size, | ||
| 30 | u32 channel_count, void* input_data, u64 input_data_size, void* buffer, | ||
| 31 | u64& out_time_taken, bool reset); | ||
| 32 | Result DecodeInterleavedForMultiStream(u32& out_sample_count, void* output_data, | ||
| 33 | u64 output_data_size, u32 channel_count, | ||
| 34 | void* input_data, u64 input_data_size, void* buffer, | ||
| 35 | u64& out_time_taken, bool reset); | ||
| 36 | Result MapMemory(void* buffer, u64 buffer_size); | ||
| 37 | Result UnmapMemory(void* buffer, u64 buffer_size); | ||
| 38 | |||
| 39 | private: | ||
| 40 | Core::System& system; | ||
| 41 | std::mutex mutex; | ||
| 42 | ADSP::OpusDecoder::OpusDecoder& opus_decoder; | ||
| 43 | ADSP::OpusDecoder::SharedMemory shared_memory; | ||
| 44 | }; | ||
| 45 | } // namespace AudioCore::OpusDecoder | ||
diff --git a/src/audio_core/opus/parameters.h b/src/audio_core/opus/parameters.h new file mode 100644 index 000000000..4c54b2825 --- /dev/null +++ b/src/audio_core/opus/parameters.h | |||
| @@ -0,0 +1,54 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/common_funcs.h" | ||
| 7 | #include "common/common_types.h" | ||
| 8 | |||
| 9 | namespace AudioCore::OpusDecoder { | ||
| 10 | constexpr size_t OpusStreamCountMax = 255; | ||
| 11 | constexpr size_t MaxChannels = 2; | ||
| 12 | |||
| 13 | struct OpusParameters { | ||
| 14 | /* 0x00 */ u32 sample_rate; | ||
| 15 | /* 0x04 */ u32 channel_count; | ||
| 16 | }; // size = 0x8 | ||
| 17 | static_assert(sizeof(OpusParameters) == 0x8, "OpusParameters has the wrong size!"); | ||
| 18 | |||
| 19 | struct OpusParametersEx { | ||
| 20 | /* 0x00 */ u32 sample_rate; | ||
| 21 | /* 0x04 */ u32 channel_count; | ||
| 22 | /* 0x08 */ bool use_large_frame_size; | ||
| 23 | /* 0x09 */ INSERT_PADDING_BYTES(7); | ||
| 24 | }; // size = 0x10 | ||
| 25 | static_assert(sizeof(OpusParametersEx) == 0x10, "OpusParametersEx has the wrong size!"); | ||
| 26 | |||
| 27 | struct OpusMultiStreamParameters { | ||
| 28 | /* 0x00 */ u32 sample_rate; | ||
| 29 | /* 0x04 */ u32 channel_count; | ||
| 30 | /* 0x08 */ u32 total_stream_count; | ||
| 31 | /* 0x0C */ u32 stereo_stream_count; | ||
| 32 | /* 0x10 */ std::array<u8, OpusStreamCountMax + 1> mappings; | ||
| 33 | }; // size = 0x110 | ||
| 34 | static_assert(sizeof(OpusMultiStreamParameters) == 0x110, | ||
| 35 | "OpusMultiStreamParameters has the wrong size!"); | ||
| 36 | |||
| 37 | struct OpusMultiStreamParametersEx { | ||
| 38 | /* 0x00 */ u32 sample_rate; | ||
| 39 | /* 0x04 */ u32 channel_count; | ||
| 40 | /* 0x08 */ u32 total_stream_count; | ||
| 41 | /* 0x0C */ u32 stereo_stream_count; | ||
| 42 | /* 0x10 */ bool use_large_frame_size; | ||
| 43 | /* 0x11 */ INSERT_PADDING_BYTES(7); | ||
| 44 | /* 0x18 */ std::array<u8, OpusStreamCountMax + 1> mappings; | ||
| 45 | }; // size = 0x118 | ||
| 46 | static_assert(sizeof(OpusMultiStreamParametersEx) == 0x118, | ||
| 47 | "OpusMultiStreamParametersEx has the wrong size!"); | ||
| 48 | |||
| 49 | struct OpusPacketHeader { | ||
| 50 | /* 0x00 */ u32 size; | ||
| 51 | /* 0x04 */ u32 final_range; | ||
| 52 | }; // size = 0x8 | ||
| 53 | static_assert(sizeof(OpusPacketHeader) == 0x8, "OpusPacketHeader has the wrong size!"); | ||
| 54 | } // namespace AudioCore::OpusDecoder | ||