diff options
| author | 2023-08-31 15:09:15 +0100 | |
|---|---|---|
| committer | 2023-09-16 11:56:25 -0400 | |
| commit | 67e2d5c28b8423c4f3f1d5b00f87325684158a6f (patch) | |
| tree | e419a2bb6c064ddc69a49046705b6187772fee48 /src/core | |
| 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/core')
| -rw-r--r-- | src/core/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/core/hle/result.h | 2 | ||||
| -rw-r--r-- | src/core/hle/service/audio/errors.h | 12 | ||||
| -rw-r--r-- | src/core/hle/service/audio/hwopus.cpp | 722 | ||||
| -rw-r--r-- | src/core/hle/service/audio/hwopus.h | 25 |
5 files changed, 428 insertions, 335 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 30d2f7df6..b2dc71d4c 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -890,7 +890,7 @@ endif() | |||
| 890 | create_target_directory_groups(core) | 890 | create_target_directory_groups(core) |
| 891 | 891 | ||
| 892 | target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core nx_tzdb) | 892 | target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core nx_tzdb) |
| 893 | target_link_libraries(core PUBLIC Boost::headers PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::opus renderdoc) | 893 | target_link_libraries(core PUBLIC Boost::headers PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls renderdoc) |
| 894 | if (MINGW) | 894 | if (MINGW) |
| 895 | target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY}) | 895 | target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY}) |
| 896 | endif() | 896 | endif() |
diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 92a1439eb..dd0b27f47 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h | |||
| @@ -62,7 +62,7 @@ enum class ErrorModule : u32 { | |||
| 62 | XCD = 108, | 62 | XCD = 108, |
| 63 | TMP451 = 109, | 63 | TMP451 = 109, |
| 64 | NIFM = 110, | 64 | NIFM = 110, |
| 65 | Hwopus = 111, | 65 | HwOpus = 111, |
| 66 | LSM6DS3 = 112, | 66 | LSM6DS3 = 112, |
| 67 | Bluetooth = 113, | 67 | Bluetooth = 113, |
| 68 | VI = 114, | 68 | VI = 114, |
diff --git a/src/core/hle/service/audio/errors.h b/src/core/hle/service/audio/errors.h index 3d3d3d97a..c41345f7e 100644 --- a/src/core/hle/service/audio/errors.h +++ b/src/core/hle/service/audio/errors.h | |||
| @@ -20,4 +20,16 @@ constexpr Result ResultNotSupported{ErrorModule::Audio, 513}; | |||
| 20 | constexpr Result ResultInvalidHandle{ErrorModule::Audio, 1536}; | 20 | constexpr Result ResultInvalidHandle{ErrorModule::Audio, 1536}; |
| 21 | constexpr Result ResultInvalidRevision{ErrorModule::Audio, 1537}; | 21 | constexpr Result ResultInvalidRevision{ErrorModule::Audio, 1537}; |
| 22 | 22 | ||
| 23 | constexpr Result ResultLibOpusAllocFail{ErrorModule::HwOpus, 7}; | ||
| 24 | constexpr Result ResultInputDataTooSmall{ErrorModule::HwOpus, 8}; | ||
| 25 | constexpr Result ResultLibOpusInvalidState{ErrorModule::HwOpus, 6}; | ||
| 26 | constexpr Result ResultLibOpusUnimplemented{ErrorModule::HwOpus, 5}; | ||
| 27 | constexpr Result ResultLibOpusInvalidPacket{ErrorModule::HwOpus, 17}; | ||
| 28 | constexpr Result ResultLibOpusInternalError{ErrorModule::HwOpus, 4}; | ||
| 29 | constexpr Result ResultBufferTooSmall{ErrorModule::HwOpus, 3}; | ||
| 30 | constexpr Result ResultLibOpusBadArg{ErrorModule::HwOpus, 2}; | ||
| 31 | constexpr Result ResultInvalidOpusDSPReturnCode{ErrorModule::HwOpus, 259}; | ||
| 32 | constexpr Result ResultInvalidOpusSampleRate{ErrorModule::HwOpus, 1001}; | ||
| 33 | constexpr Result ResultInvalidOpusChannelCount{ErrorModule::HwOpus, 1002}; | ||
| 34 | |||
| 23 | } // namespace Service::Audio | 35 | } // namespace Service::Audio |
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp index 1557e6088..6a7bf9416 100644 --- a/src/core/hle/service/audio/hwopus.cpp +++ b/src/core/hle/service/audio/hwopus.cpp | |||
| @@ -1,420 +1,506 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <chrono> | ||
| 5 | #include <cstring> | ||
| 6 | #include <memory> | 4 | #include <memory> |
| 7 | #include <vector> | 5 | #include <vector> |
| 8 | 6 | ||
| 9 | #include <opus.h> | 7 | #include "audio_core/opus/decoder.h" |
| 10 | #include <opus_multistream.h> | 8 | #include "audio_core/opus/parameters.h" |
| 11 | |||
| 12 | #include "common/assert.h" | 9 | #include "common/assert.h" |
| 13 | #include "common/logging/log.h" | 10 | #include "common/logging/log.h" |
| 14 | #include "common/scratch_buffer.h" | 11 | #include "common/scratch_buffer.h" |
| 12 | #include "core/core.h" | ||
| 15 | #include "core/hle/service/audio/hwopus.h" | 13 | #include "core/hle/service/audio/hwopus.h" |
| 16 | #include "core/hle/service/ipc_helpers.h" | 14 | #include "core/hle/service/ipc_helpers.h" |
| 17 | 15 | ||
| 18 | namespace Service::Audio { | 16 | namespace Service::Audio { |
| 19 | namespace { | 17 | using namespace AudioCore::OpusDecoder; |
| 20 | struct OpusDeleter { | 18 | |
| 21 | void operator()(OpusMSDecoder* ptr) const { | 19 | class IHardwareOpusDecoder final : public ServiceFramework<IHardwareOpusDecoder> { |
| 22 | opus_multistream_decoder_destroy(ptr); | 20 | public: |
| 21 | explicit IHardwareOpusDecoder(Core::System& system_, HardwareOpus& hardware_opus) | ||
| 22 | : ServiceFramework{system_, "IHardwareOpusDecoder"}, | ||
| 23 | impl{std::make_unique<AudioCore::OpusDecoder::OpusDecoder>(system_, hardware_opus)} { | ||
| 24 | // clang-format off | ||
| 25 | static const FunctionInfo functions[] = { | ||
| 26 | {0, &IHardwareOpusDecoder::DecodeInterleavedOld, "DecodeInterleavedOld"}, | ||
| 27 | {1, &IHardwareOpusDecoder::SetContext, "SetContext"}, | ||
| 28 | {2, &IHardwareOpusDecoder::DecodeInterleavedForMultiStreamOld, "DecodeInterleavedForMultiStreamOld"}, | ||
| 29 | {3, &IHardwareOpusDecoder::SetContextForMultiStream, "SetContextForMultiStream"}, | ||
| 30 | {4, &IHardwareOpusDecoder::DecodeInterleavedWithPerfOld, "DecodeInterleavedWithPerfOld"}, | ||
| 31 | {5, &IHardwareOpusDecoder::DecodeInterleavedForMultiStreamWithPerfOld, "DecodeInterleavedForMultiStreamWithPerfOld"}, | ||
| 32 | {6, &IHardwareOpusDecoder::DecodeInterleavedWithPerfAndResetOld, "DecodeInterleavedWithPerfAndResetOld"}, | ||
| 33 | {7, &IHardwareOpusDecoder::DecodeInterleavedForMultiStreamWithPerfAndResetOld, "DecodeInterleavedForMultiStreamWithPerfAndResetOld"}, | ||
| 34 | {8, &IHardwareOpusDecoder::DecodeInterleaved, "DecodeInterleaved"}, | ||
| 35 | {9, &IHardwareOpusDecoder::DecodeInterleavedForMultiStream, "DecodeInterleavedForMultiStream"}, | ||
| 36 | }; | ||
| 37 | // clang-format on | ||
| 38 | |||
| 39 | RegisterHandlers(functions); | ||
| 23 | } | 40 | } |
| 24 | }; | ||
| 25 | 41 | ||
| 26 | using OpusDecoderPtr = std::unique_ptr<OpusMSDecoder, OpusDeleter>; | 42 | Result Initialize(OpusParametersEx& params, Kernel::KTransferMemory* transfer_memory, |
| 43 | u64 transfer_memory_size) { | ||
| 44 | return impl->Initialize(params, transfer_memory, transfer_memory_size); | ||
| 45 | } | ||
| 27 | 46 | ||
| 28 | struct OpusPacketHeader { | 47 | Result Initialize(OpusMultiStreamParametersEx& params, Kernel::KTransferMemory* transfer_memory, |
| 29 | // Packet size in bytes. | 48 | u64 transfer_memory_size) { |
| 30 | u32_be size; | 49 | return impl->Initialize(params, transfer_memory, transfer_memory_size); |
| 31 | // Indicates the final range of the codec's entropy coder. | 50 | } |
| 32 | u32_be final_range; | ||
| 33 | }; | ||
| 34 | static_assert(sizeof(OpusPacketHeader) == 0x8, "OpusHeader is an invalid size"); | ||
| 35 | 51 | ||
| 36 | class OpusDecoderState { | 52 | private: |
| 37 | public: | 53 | void DecodeInterleavedOld(HLERequestContext& ctx) { |
| 38 | /// Describes extra behavior that may be asked of the decoding context. | 54 | IPC::RequestParser rp{ctx}; |
| 39 | enum class ExtraBehavior { | ||
| 40 | /// No extra behavior. | ||
| 41 | None, | ||
| 42 | 55 | ||
| 43 | /// Resets the decoder context back to a freshly initialized state. | 56 | auto input_data{ctx.ReadBuffer(0)}; |
| 44 | ResetContext, | 57 | output_data.resize_destructive(ctx.GetWriteBufferSize()); |
| 45 | }; | ||
| 46 | 58 | ||
| 47 | enum class PerfTime { | 59 | u32 size{}; |
| 48 | Disabled, | 60 | u32 sample_count{}; |
| 49 | Enabled, | 61 | auto result = |
| 50 | }; | 62 | impl->DecodeInterleaved(&size, nullptr, &sample_count, input_data, output_data, false); |
| 51 | 63 | ||
| 52 | explicit OpusDecoderState(OpusDecoderPtr decoder_, u32 sample_rate_, u32 channel_count_) | 64 | LOG_DEBUG(Service_Audio, "bytes read 0x{:X} samples generated {}", size, sample_count); |
| 53 | : decoder{std::move(decoder_)}, sample_rate{sample_rate_}, channel_count{channel_count_} {} | 65 | |
| 54 | 66 | ctx.WriteBuffer(output_data); | |
| 55 | // Decodes interleaved Opus packets. Optionally allows reporting time taken to | 67 | |
| 56 | // perform the decoding, as well as any relevant extra behavior. | 68 | IPC::ResponseBuilder rb{ctx, 4}; |
| 57 | void DecodeInterleaved(HLERequestContext& ctx, PerfTime perf_time, | 69 | rb.Push(result); |
| 58 | ExtraBehavior extra_behavior) { | 70 | rb.Push(size); |
| 59 | if (perf_time == PerfTime::Disabled) { | 71 | rb.Push(sample_count); |
| 60 | DecodeInterleavedHelper(ctx, nullptr, extra_behavior); | ||
| 61 | } else { | ||
| 62 | u64 performance = 0; | ||
| 63 | DecodeInterleavedHelper(ctx, &performance, extra_behavior); | ||
| 64 | } | ||
| 65 | } | 72 | } |
| 66 | 73 | ||
| 67 | private: | 74 | void SetContext(HLERequestContext& ctx) { |
| 68 | void DecodeInterleavedHelper(HLERequestContext& ctx, u64* performance, | 75 | IPC::RequestParser rp{ctx}; |
| 69 | ExtraBehavior extra_behavior) { | 76 | |
| 70 | u32 consumed = 0; | 77 | LOG_DEBUG(Service_Audio, "called"); |
| 71 | u32 sample_count = 0; | 78 | |
| 72 | samples.resize_destructive(ctx.GetWriteBufferNumElements<opus_int16>()); | 79 | auto input_data{ctx.ReadBuffer(0)}; |
| 73 | 80 | auto result = impl->SetContext(input_data); | |
| 74 | if (extra_behavior == ExtraBehavior::ResetContext) { | 81 | |
| 75 | ResetDecoderContext(); | 82 | IPC::ResponseBuilder rb{ctx, 2}; |
| 76 | } | 83 | rb.Push(result); |
| 77 | 84 | } | |
| 78 | if (!DecodeOpusData(consumed, sample_count, ctx.ReadBuffer(), samples, performance)) { | 85 | |
| 79 | LOG_ERROR(Audio, "Failed to decode opus data"); | 86 | void DecodeInterleavedForMultiStreamOld(HLERequestContext& ctx) { |
| 80 | IPC::ResponseBuilder rb{ctx, 2}; | 87 | IPC::RequestParser rp{ctx}; |
| 81 | // TODO(ogniK): Use correct error code | 88 | |
| 82 | rb.Push(ResultUnknown); | 89 | auto input_data{ctx.ReadBuffer(0)}; |
| 83 | return; | 90 | output_data.resize_destructive(ctx.GetWriteBufferSize()); |
| 84 | } | 91 | |
| 85 | 92 | u32 size{}; | |
| 86 | const u32 param_size = performance != nullptr ? 6 : 4; | 93 | u32 sample_count{}; |
| 87 | IPC::ResponseBuilder rb{ctx, param_size}; | 94 | auto result = impl->DecodeInterleavedForMultiStream(&size, nullptr, &sample_count, |
| 88 | rb.Push(ResultSuccess); | 95 | input_data, output_data, false); |
| 89 | rb.Push<u32>(consumed); | 96 | |
| 90 | rb.Push<u32>(sample_count); | 97 | LOG_DEBUG(Service_Audio, "bytes read 0x{:X} samples generated {}", size, sample_count); |
| 91 | if (performance) { | 98 | |
| 92 | rb.Push<u64>(*performance); | 99 | ctx.WriteBuffer(output_data); |
| 93 | } | 100 | |
| 94 | ctx.WriteBuffer(samples); | 101 | IPC::ResponseBuilder rb{ctx, 4}; |
| 102 | rb.Push(result); | ||
| 103 | rb.Push(size); | ||
| 104 | rb.Push(sample_count); | ||
| 95 | } | 105 | } |
| 96 | 106 | ||
| 97 | bool DecodeOpusData(u32& consumed, u32& sample_count, std::span<const u8> input, | 107 | void SetContextForMultiStream(HLERequestContext& ctx) { |
| 98 | std::span<opus_int16> output, u64* out_performance_time) const { | 108 | IPC::RequestParser rp{ctx}; |
| 99 | const auto start_time = std::chrono::steady_clock::now(); | 109 | |
| 100 | const std::size_t raw_output_sz = output.size() * sizeof(opus_int16); | 110 | LOG_DEBUG(Service_Audio, "called"); |
| 101 | if (sizeof(OpusPacketHeader) > input.size()) { | 111 | |
| 102 | LOG_ERROR(Audio, "Input is smaller than the header size, header_sz={}, input_sz={}", | 112 | auto input_data{ctx.ReadBuffer(0)}; |
| 103 | sizeof(OpusPacketHeader), input.size()); | 113 | auto result = impl->SetContext(input_data); |
| 104 | return false; | 114 | |
| 105 | } | 115 | IPC::ResponseBuilder rb{ctx, 2}; |
| 106 | 116 | rb.Push(result); | |
| 107 | OpusPacketHeader hdr{}; | ||
| 108 | std::memcpy(&hdr, input.data(), sizeof(OpusPacketHeader)); | ||
| 109 | if (sizeof(OpusPacketHeader) + static_cast<u32>(hdr.size) > input.size()) { | ||
| 110 | LOG_ERROR(Audio, "Input does not fit in the opus header size. data_sz={}, input_sz={}", | ||
| 111 | sizeof(OpusPacketHeader) + static_cast<u32>(hdr.size), input.size()); | ||
| 112 | return false; | ||
| 113 | } | ||
| 114 | |||
| 115 | const auto frame = input.data() + sizeof(OpusPacketHeader); | ||
| 116 | const auto decoded_sample_count = opus_packet_get_nb_samples( | ||
| 117 | frame, static_cast<opus_int32>(input.size() - sizeof(OpusPacketHeader)), | ||
| 118 | static_cast<opus_int32>(sample_rate)); | ||
| 119 | if (decoded_sample_count * channel_count * sizeof(u16) > raw_output_sz) { | ||
| 120 | LOG_ERROR( | ||
| 121 | Audio, | ||
| 122 | "Decoded data does not fit into the output data, decoded_sz={}, raw_output_sz={}", | ||
| 123 | decoded_sample_count * channel_count * sizeof(u16), raw_output_sz); | ||
| 124 | return false; | ||
| 125 | } | ||
| 126 | |||
| 127 | const int frame_size = (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count)); | ||
| 128 | const auto out_sample_count = | ||
| 129 | opus_multistream_decode(decoder.get(), frame, hdr.size, output.data(), frame_size, 0); | ||
| 130 | if (out_sample_count < 0) { | ||
| 131 | LOG_ERROR(Audio, | ||
| 132 | "Incorrect sample count received from opus_decode, " | ||
| 133 | "output_sample_count={}, frame_size={}, data_sz_from_hdr={}", | ||
| 134 | out_sample_count, frame_size, static_cast<u32>(hdr.size)); | ||
| 135 | return false; | ||
| 136 | } | ||
| 137 | |||
| 138 | const auto end_time = std::chrono::steady_clock::now() - start_time; | ||
| 139 | sample_count = out_sample_count; | ||
| 140 | consumed = static_cast<u32>(sizeof(OpusPacketHeader) + hdr.size); | ||
| 141 | if (out_performance_time != nullptr) { | ||
| 142 | *out_performance_time = | ||
| 143 | std::chrono::duration_cast<std::chrono::milliseconds>(end_time).count(); | ||
| 144 | } | ||
| 145 | |||
| 146 | return true; | ||
| 147 | } | 117 | } |
| 148 | 118 | ||
| 149 | void ResetDecoderContext() { | 119 | void DecodeInterleavedWithPerfOld(HLERequestContext& ctx) { |
| 150 | ASSERT(decoder != nullptr); | 120 | IPC::RequestParser rp{ctx}; |
| 121 | |||
| 122 | auto input_data{ctx.ReadBuffer(0)}; | ||
| 123 | output_data.resize_destructive(ctx.GetWriteBufferSize()); | ||
| 124 | |||
| 125 | u32 size{}; | ||
| 126 | u32 sample_count{}; | ||
| 127 | u64 time_taken{}; | ||
| 128 | auto result = impl->DecodeInterleaved(&size, &time_taken, &sample_count, input_data, | ||
| 129 | output_data, false); | ||
| 130 | |||
| 131 | LOG_DEBUG(Service_Audio, "bytes read 0x{:X} samples generated {} time taken {}", size, | ||
| 132 | sample_count, time_taken); | ||
| 151 | 133 | ||
| 152 | opus_multistream_decoder_ctl(decoder.get(), OPUS_RESET_STATE); | 134 | ctx.WriteBuffer(output_data); |
| 135 | |||
| 136 | IPC::ResponseBuilder rb{ctx, 6}; | ||
| 137 | rb.Push(result); | ||
| 138 | rb.Push(size); | ||
| 139 | rb.Push(sample_count); | ||
| 140 | rb.Push(time_taken); | ||
| 153 | } | 141 | } |
| 154 | 142 | ||
| 155 | OpusDecoderPtr decoder; | 143 | void DecodeInterleavedForMultiStreamWithPerfOld(HLERequestContext& ctx) { |
| 156 | u32 sample_rate; | 144 | IPC::RequestParser rp{ctx}; |
| 157 | u32 channel_count; | ||
| 158 | Common::ScratchBuffer<opus_int16> samples; | ||
| 159 | }; | ||
| 160 | 145 | ||
| 161 | class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> { | 146 | auto input_data{ctx.ReadBuffer(0)}; |
| 162 | public: | 147 | output_data.resize_destructive(ctx.GetWriteBufferSize()); |
| 163 | explicit IHardwareOpusDecoderManager(Core::System& system_, OpusDecoderState decoder_state_) | ||
| 164 | : ServiceFramework{system_, "IHardwareOpusDecoderManager"}, decoder_state{ | ||
| 165 | std::move(decoder_state_)} { | ||
| 166 | // clang-format off | ||
| 167 | static const FunctionInfo functions[] = { | ||
| 168 | {0, &IHardwareOpusDecoderManager::DecodeInterleavedOld, "DecodeInterleavedOld"}, | ||
| 169 | {1, nullptr, "SetContext"}, | ||
| 170 | {2, nullptr, "DecodeInterleavedForMultiStreamOld"}, | ||
| 171 | {3, nullptr, "SetContextForMultiStream"}, | ||
| 172 | {4, &IHardwareOpusDecoderManager::DecodeInterleavedWithPerfOld, "DecodeInterleavedWithPerfOld"}, | ||
| 173 | {5, nullptr, "DecodeInterleavedForMultiStreamWithPerfOld"}, | ||
| 174 | {6, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleavedWithPerfAndResetOld"}, | ||
| 175 | {7, nullptr, "DecodeInterleavedForMultiStreamWithPerfAndResetOld"}, | ||
| 176 | {8, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"}, | ||
| 177 | {9, &IHardwareOpusDecoderManager::DecodeInterleavedForMultiStream, "DecodeInterleavedForMultiStream"}, | ||
| 178 | }; | ||
| 179 | // clang-format on | ||
| 180 | 148 | ||
| 181 | RegisterHandlers(functions); | 149 | u32 size{}; |
| 150 | u32 sample_count{}; | ||
| 151 | u64 time_taken{}; | ||
| 152 | auto result = impl->DecodeInterleavedForMultiStream(&size, &time_taken, &sample_count, | ||
| 153 | input_data, output_data, false); | ||
| 154 | |||
| 155 | LOG_DEBUG(Service_Audio, "bytes read 0x{:X} samples generated {} time taken {}", size, | ||
| 156 | sample_count, time_taken); | ||
| 157 | |||
| 158 | ctx.WriteBuffer(output_data); | ||
| 159 | |||
| 160 | IPC::ResponseBuilder rb{ctx, 6}; | ||
| 161 | rb.Push(result); | ||
| 162 | rb.Push(size); | ||
| 163 | rb.Push(sample_count); | ||
| 164 | rb.Push(time_taken); | ||
| 182 | } | 165 | } |
| 183 | 166 | ||
| 184 | private: | 167 | void DecodeInterleavedWithPerfAndResetOld(HLERequestContext& ctx) { |
| 185 | void DecodeInterleavedOld(HLERequestContext& ctx) { | 168 | IPC::RequestParser rp{ctx}; |
| 186 | LOG_DEBUG(Audio, "called"); | 169 | |
| 170 | auto reset{rp.Pop<bool>()}; | ||
| 171 | |||
| 172 | auto input_data{ctx.ReadBuffer(0)}; | ||
| 173 | output_data.resize_destructive(ctx.GetWriteBufferSize()); | ||
| 174 | |||
| 175 | u32 size{}; | ||
| 176 | u32 sample_count{}; | ||
| 177 | u64 time_taken{}; | ||
| 178 | auto result = impl->DecodeInterleaved(&size, &time_taken, &sample_count, input_data, | ||
| 179 | output_data, reset); | ||
| 180 | |||
| 181 | LOG_DEBUG(Service_Audio, "reset {} bytes read 0x{:X} samples generated {} time taken {}", | ||
| 182 | reset, size, sample_count, time_taken); | ||
| 183 | |||
| 184 | ctx.WriteBuffer(output_data); | ||
| 187 | 185 | ||
| 188 | decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Disabled, | 186 | IPC::ResponseBuilder rb{ctx, 6}; |
| 189 | OpusDecoderState::ExtraBehavior::None); | 187 | rb.Push(result); |
| 188 | rb.Push(size); | ||
| 189 | rb.Push(sample_count); | ||
| 190 | rb.Push(time_taken); | ||
| 190 | } | 191 | } |
| 191 | 192 | ||
| 192 | void DecodeInterleavedWithPerfOld(HLERequestContext& ctx) { | 193 | void DecodeInterleavedForMultiStreamWithPerfAndResetOld(HLERequestContext& ctx) { |
| 193 | LOG_DEBUG(Audio, "called"); | 194 | IPC::RequestParser rp{ctx}; |
| 195 | |||
| 196 | auto reset{rp.Pop<bool>()}; | ||
| 197 | |||
| 198 | auto input_data{ctx.ReadBuffer(0)}; | ||
| 199 | output_data.resize_destructive(ctx.GetWriteBufferSize()); | ||
| 200 | |||
| 201 | u32 size{}; | ||
| 202 | u32 sample_count{}; | ||
| 203 | u64 time_taken{}; | ||
| 204 | auto result = impl->DecodeInterleavedForMultiStream(&size, &time_taken, &sample_count, | ||
| 205 | input_data, output_data, reset); | ||
| 194 | 206 | ||
| 195 | decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled, | 207 | LOG_DEBUG(Service_Audio, "reset {} bytes read 0x{:X} samples generated {} time taken {}", |
| 196 | OpusDecoderState::ExtraBehavior::None); | 208 | reset, size, sample_count, time_taken); |
| 209 | |||
| 210 | ctx.WriteBuffer(output_data); | ||
| 211 | |||
| 212 | IPC::ResponseBuilder rb{ctx, 6}; | ||
| 213 | rb.Push(result); | ||
| 214 | rb.Push(size); | ||
| 215 | rb.Push(sample_count); | ||
| 216 | rb.Push(time_taken); | ||
| 197 | } | 217 | } |
| 198 | 218 | ||
| 199 | void DecodeInterleaved(HLERequestContext& ctx) { | 219 | void DecodeInterleaved(HLERequestContext& ctx) { |
| 200 | LOG_DEBUG(Audio, "called"); | ||
| 201 | |||
| 202 | IPC::RequestParser rp{ctx}; | 220 | IPC::RequestParser rp{ctx}; |
| 203 | const auto extra_behavior = rp.Pop<bool>() ? OpusDecoderState::ExtraBehavior::ResetContext | ||
| 204 | : OpusDecoderState::ExtraBehavior::None; | ||
| 205 | 221 | ||
| 206 | decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled, extra_behavior); | 222 | auto reset{rp.Pop<bool>()}; |
| 223 | |||
| 224 | auto input_data{ctx.ReadBuffer(0)}; | ||
| 225 | output_data.resize_destructive(ctx.GetWriteBufferSize()); | ||
| 226 | |||
| 227 | u32 size{}; | ||
| 228 | u32 sample_count{}; | ||
| 229 | u64 time_taken{}; | ||
| 230 | auto result = impl->DecodeInterleaved(&size, &time_taken, &sample_count, input_data, | ||
| 231 | output_data, reset); | ||
| 232 | |||
| 233 | LOG_DEBUG(Service_Audio, "reset {} bytes read 0x{:X} samples generated {} time taken {}", | ||
| 234 | reset, size, sample_count, time_taken); | ||
| 235 | |||
| 236 | ctx.WriteBuffer(output_data); | ||
| 237 | |||
| 238 | IPC::ResponseBuilder rb{ctx, 6}; | ||
| 239 | rb.Push(result); | ||
| 240 | rb.Push(size); | ||
| 241 | rb.Push(sample_count); | ||
| 242 | rb.Push(time_taken); | ||
| 207 | } | 243 | } |
| 208 | 244 | ||
| 209 | void DecodeInterleavedForMultiStream(HLERequestContext& ctx) { | 245 | void DecodeInterleavedForMultiStream(HLERequestContext& ctx) { |
| 210 | LOG_DEBUG(Audio, "called"); | ||
| 211 | |||
| 212 | IPC::RequestParser rp{ctx}; | 246 | IPC::RequestParser rp{ctx}; |
| 213 | const auto extra_behavior = rp.Pop<bool>() ? OpusDecoderState::ExtraBehavior::ResetContext | ||
| 214 | : OpusDecoderState::ExtraBehavior::None; | ||
| 215 | 247 | ||
| 216 | decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled, extra_behavior); | 248 | auto reset{rp.Pop<bool>()}; |
| 249 | |||
| 250 | auto input_data{ctx.ReadBuffer(0)}; | ||
| 251 | output_data.resize_destructive(ctx.GetWriteBufferSize()); | ||
| 252 | |||
| 253 | u32 size{}; | ||
| 254 | u32 sample_count{}; | ||
| 255 | u64 time_taken{}; | ||
| 256 | auto result = impl->DecodeInterleavedForMultiStream(&size, &time_taken, &sample_count, | ||
| 257 | input_data, output_data, reset); | ||
| 258 | |||
| 259 | LOG_DEBUG(Service_Audio, "reset {} bytes read 0x{:X} samples generated {} time taken {}", | ||
| 260 | reset, size, sample_count, time_taken); | ||
| 261 | |||
| 262 | ctx.WriteBuffer(output_data); | ||
| 263 | |||
| 264 | IPC::ResponseBuilder rb{ctx, 6}; | ||
| 265 | rb.Push(result); | ||
| 266 | rb.Push(size); | ||
| 267 | rb.Push(sample_count); | ||
| 268 | rb.Push(time_taken); | ||
| 217 | } | 269 | } |
| 218 | 270 | ||
| 219 | OpusDecoderState decoder_state; | 271 | std::unique_ptr<AudioCore::OpusDecoder::OpusDecoder> impl; |
| 272 | Common::ScratchBuffer<u8> output_data; | ||
| 220 | }; | 273 | }; |
| 221 | 274 | ||
| 222 | std::size_t WorkerBufferSize(u32 channel_count) { | 275 | void HwOpus::OpenHardwareOpusDecoder(HLERequestContext& ctx) { |
| 223 | ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); | 276 | IPC::RequestParser rp{ctx}; |
| 224 | constexpr int num_streams = 1; | ||
| 225 | const int num_stereo_streams = channel_count == 2 ? 1 : 0; | ||
| 226 | return opus_multistream_decoder_get_size(num_streams, num_stereo_streams); | ||
| 227 | } | ||
| 228 | 277 | ||
| 229 | // Creates the mapping table that maps the input channels to the particular | 278 | auto params = rp.PopRaw<OpusParameters>(); |
| 230 | // output channels. In the stereo case, we map the left and right input channels | 279 | auto transfer_memory_size{rp.Pop<u32>()}; |
| 231 | // to the left and right output channels respectively. | 280 | auto transfer_memory_handle{ctx.GetCopyHandle(0)}; |
| 232 | // | 281 | auto transfer_memory{ |
| 233 | // However, in the monophonic case, we only map the one available channel | 282 | system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( |
| 234 | // to the sole output channel. We specify 255 for the would-be right channel | 283 | transfer_memory_handle)}; |
| 235 | // as this is a special value defined by Opus to indicate to the decoder to | 284 | |
| 236 | // ignore that channel. | 285 | LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} transfer_memory_size 0x{:X}", |
| 237 | std::array<u8, 2> CreateMappingTable(u32 channel_count) { | 286 | params.sample_rate, params.channel_count, transfer_memory_size); |
| 238 | if (channel_count == 2) { | ||
| 239 | return {{0, 1}}; | ||
| 240 | } | ||
| 241 | 287 | ||
| 242 | return {{0, 255}}; | 288 | auto decoder{std::make_shared<IHardwareOpusDecoder>(system, impl.GetHardwareOpus())}; |
| 289 | |||
| 290 | OpusParametersEx ex{ | ||
| 291 | .sample_rate = params.sample_rate, | ||
| 292 | .channel_count = params.channel_count, | ||
| 293 | .use_large_frame_size = false, | ||
| 294 | }; | ||
| 295 | auto result = decoder->Initialize(ex, transfer_memory.GetPointerUnsafe(), transfer_memory_size); | ||
| 296 | |||
| 297 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 298 | rb.Push(result); | ||
| 299 | rb.PushIpcInterface(decoder); | ||
| 243 | } | 300 | } |
| 244 | } // Anonymous namespace | ||
| 245 | 301 | ||
| 246 | void HwOpus::GetWorkBufferSize(HLERequestContext& ctx) { | 302 | void HwOpus::GetWorkBufferSize(HLERequestContext& ctx) { |
| 247 | IPC::RequestParser rp{ctx}; | 303 | IPC::RequestParser rp{ctx}; |
| 248 | const auto sample_rate = rp.Pop<u32>(); | 304 | auto params = rp.PopRaw<OpusParameters>(); |
| 249 | const auto channel_count = rp.Pop<u32>(); | ||
| 250 | 305 | ||
| 251 | LOG_DEBUG(Audio, "called with sample_rate={}, channel_count={}", sample_rate, channel_count); | 306 | u64 size{}; |
| 307 | auto result = impl.GetWorkBufferSize(params, size); | ||
| 252 | 308 | ||
| 253 | ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || | 309 | LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} -- returned size 0x{:X}", |
| 254 | sample_rate == 12000 || sample_rate == 8000, | 310 | params.sample_rate, params.channel_count, size); |
| 255 | "Invalid sample rate"); | ||
| 256 | ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); | ||
| 257 | 311 | ||
| 258 | const u32 worker_buffer_sz = static_cast<u32>(WorkerBufferSize(channel_count)); | 312 | IPC::ResponseBuilder rb{ctx, 4}; |
| 259 | LOG_DEBUG(Audio, "worker_buffer_sz={}", worker_buffer_sz); | 313 | rb.Push(result); |
| 260 | 314 | rb.Push(size); | |
| 261 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 262 | rb.Push(ResultSuccess); | ||
| 263 | rb.Push<u32>(worker_buffer_sz); | ||
| 264 | } | 315 | } |
| 265 | 316 | ||
| 266 | void HwOpus::GetWorkBufferSizeEx(HLERequestContext& ctx) { | 317 | void HwOpus::OpenHardwareOpusDecoderForMultiStream(HLERequestContext& ctx) { |
| 267 | GetWorkBufferSize(ctx); | 318 | IPC::RequestParser rp{ctx}; |
| 319 | |||
| 320 | auto input{ctx.ReadBuffer()}; | ||
| 321 | OpusMultiStreamParameters params; | ||
| 322 | std::memcpy(¶ms, input.data(), sizeof(OpusMultiStreamParameters)); | ||
| 323 | |||
| 324 | auto transfer_memory_size{rp.Pop<u32>()}; | ||
| 325 | auto transfer_memory_handle{ctx.GetCopyHandle(0)}; | ||
| 326 | auto transfer_memory{ | ||
| 327 | system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( | ||
| 328 | transfer_memory_handle)}; | ||
| 329 | |||
| 330 | LOG_DEBUG(Service_Audio, | ||
| 331 | "sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} " | ||
| 332 | "transfer_memory_size 0x{:X}", | ||
| 333 | params.sample_rate, params.channel_count, params.total_stream_count, | ||
| 334 | params.stereo_stream_count, transfer_memory_size); | ||
| 335 | |||
| 336 | auto decoder{std::make_shared<IHardwareOpusDecoder>(system, impl.GetHardwareOpus())}; | ||
| 337 | |||
| 338 | OpusMultiStreamParametersEx ex{ | ||
| 339 | .sample_rate = params.sample_rate, | ||
| 340 | .channel_count = params.channel_count, | ||
| 341 | .total_stream_count = params.total_stream_count, | ||
| 342 | .stereo_stream_count = params.stereo_stream_count, | ||
| 343 | .use_large_frame_size = false, | ||
| 344 | .mappings{}, | ||
| 345 | }; | ||
| 346 | std::memcpy(ex.mappings.data(), params.mappings.data(), sizeof(params.mappings)); | ||
| 347 | auto result = decoder->Initialize(ex, transfer_memory.GetPointerUnsafe(), transfer_memory_size); | ||
| 348 | |||
| 349 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 350 | rb.Push(result); | ||
| 351 | rb.PushIpcInterface(decoder); | ||
| 268 | } | 352 | } |
| 269 | 353 | ||
| 270 | void HwOpus::GetWorkBufferSizeExEx(HLERequestContext& ctx) { | 354 | void HwOpus::GetWorkBufferSizeForMultiStream(HLERequestContext& ctx) { |
| 271 | GetWorkBufferSizeEx(ctx); | 355 | IPC::RequestParser rp{ctx}; |
| 356 | |||
| 357 | auto input{ctx.ReadBuffer()}; | ||
| 358 | OpusMultiStreamParameters params; | ||
| 359 | std::memcpy(¶ms, input.data(), sizeof(OpusMultiStreamParameters)); | ||
| 360 | |||
| 361 | u64 size{}; | ||
| 362 | auto result = impl.GetWorkBufferSizeForMultiStream(params, size); | ||
| 363 | |||
| 364 | LOG_DEBUG(Service_Audio, "size 0x{:X}", size); | ||
| 365 | |||
| 366 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 367 | rb.Push(result); | ||
| 368 | rb.Push(size); | ||
| 272 | } | 369 | } |
| 273 | 370 | ||
| 274 | void HwOpus::GetWorkBufferSizeForMultiStreamEx(HLERequestContext& ctx) { | 371 | void HwOpus::OpenHardwareOpusDecoderEx(HLERequestContext& ctx) { |
| 275 | OpusMultiStreamParametersEx param; | 372 | IPC::RequestParser rp{ctx}; |
| 276 | std::memcpy(¶m, ctx.ReadBuffer().data(), ctx.GetReadBufferSize()); | ||
| 277 | 373 | ||
| 278 | const auto sample_rate = param.sample_rate; | 374 | auto params = rp.PopRaw<OpusParametersEx>(); |
| 279 | const auto channel_count = param.channel_count; | 375 | auto transfer_memory_size{rp.Pop<u32>()}; |
| 280 | const auto number_streams = param.number_streams; | 376 | auto transfer_memory_handle{ctx.GetCopyHandle(0)}; |
| 281 | const auto number_stereo_streams = param.number_stereo_streams; | 377 | auto transfer_memory{ |
| 378 | system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( | ||
| 379 | transfer_memory_handle)}; | ||
| 282 | 380 | ||
| 283 | LOG_DEBUG( | 381 | LOG_DEBUG(Service_Audio, "sample_rate {} channel_count {} transfer_memory_size 0x{:X}", |
| 284 | Audio, | 382 | params.sample_rate, params.channel_count, transfer_memory_size); |
| 285 | "called with sample_rate={}, channel_count={}, number_streams={}, number_stereo_streams={}", | ||
| 286 | sample_rate, channel_count, number_streams, number_stereo_streams); | ||
| 287 | 383 | ||
| 288 | ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || | 384 | auto decoder{std::make_shared<IHardwareOpusDecoder>(system, impl.GetHardwareOpus())}; |
| 289 | sample_rate == 12000 || sample_rate == 8000, | ||
| 290 | "Invalid sample rate"); | ||
| 291 | 385 | ||
| 292 | const u32 worker_buffer_sz = | 386 | auto result = |
| 293 | static_cast<u32>(opus_multistream_decoder_get_size(number_streams, number_stereo_streams)); | 387 | decoder->Initialize(params, transfer_memory.GetPointerUnsafe(), transfer_memory_size); |
| 294 | 388 | ||
| 295 | IPC::ResponseBuilder rb{ctx, 3}; | 389 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 296 | rb.Push(ResultSuccess); | 390 | rb.Push(result); |
| 297 | rb.Push<u32>(worker_buffer_sz); | 391 | rb.PushIpcInterface(decoder); |
| 298 | } | 392 | } |
| 299 | 393 | ||
| 300 | void HwOpus::OpenHardwareOpusDecoder(HLERequestContext& ctx) { | 394 | void HwOpus::GetWorkBufferSizeEx(HLERequestContext& ctx) { |
| 301 | IPC::RequestParser rp{ctx}; | 395 | IPC::RequestParser rp{ctx}; |
| 302 | const auto sample_rate = rp.Pop<u32>(); | 396 | auto params = rp.PopRaw<OpusParametersEx>(); |
| 303 | const auto channel_count = rp.Pop<u32>(); | ||
| 304 | const auto buffer_sz = rp.Pop<u32>(); | ||
| 305 | |||
| 306 | LOG_DEBUG(Audio, "called sample_rate={}, channel_count={}, buffer_size={}", sample_rate, | ||
| 307 | channel_count, buffer_sz); | ||
| 308 | |||
| 309 | ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || | ||
| 310 | sample_rate == 12000 || sample_rate == 8000, | ||
| 311 | "Invalid sample rate"); | ||
| 312 | ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); | ||
| 313 | |||
| 314 | const std::size_t worker_sz = WorkerBufferSize(channel_count); | ||
| 315 | ASSERT_MSG(buffer_sz >= worker_sz, "Worker buffer too large"); | ||
| 316 | |||
| 317 | const int num_stereo_streams = channel_count == 2 ? 1 : 0; | ||
| 318 | const auto mapping_table = CreateMappingTable(channel_count); | ||
| 319 | |||
| 320 | int error = 0; | ||
| 321 | OpusDecoderPtr decoder{ | ||
| 322 | opus_multistream_decoder_create(sample_rate, static_cast<int>(channel_count), 1, | ||
| 323 | num_stereo_streams, mapping_table.data(), &error)}; | ||
| 324 | if (error != OPUS_OK || decoder == nullptr) { | ||
| 325 | LOG_ERROR(Audio, "Failed to create Opus decoder (error={}).", error); | ||
| 326 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 327 | // TODO(ogniK): Use correct error code | ||
| 328 | rb.Push(ResultUnknown); | ||
| 329 | return; | ||
| 330 | } | ||
| 331 | 397 | ||
| 332 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 398 | u64 size{}; |
| 333 | rb.Push(ResultSuccess); | 399 | auto result = impl.GetWorkBufferSizeEx(params, size); |
| 334 | rb.PushIpcInterface<IHardwareOpusDecoderManager>( | 400 | |
| 335 | system, OpusDecoderState{std::move(decoder), sample_rate, channel_count}); | 401 | LOG_DEBUG(Service_Audio, "size 0x{:X}", size); |
| 402 | |||
| 403 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 404 | rb.Push(result); | ||
| 405 | rb.Push(size); | ||
| 336 | } | 406 | } |
| 337 | 407 | ||
| 338 | void HwOpus::OpenHardwareOpusDecoderEx(HLERequestContext& ctx) { | 408 | void HwOpus::OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx) { |
| 339 | IPC::RequestParser rp{ctx}; | 409 | IPC::RequestParser rp{ctx}; |
| 340 | const auto sample_rate = rp.Pop<u32>(); | ||
| 341 | const auto channel_count = rp.Pop<u32>(); | ||
| 342 | 410 | ||
| 343 | LOG_DEBUG(Audio, "called sample_rate={}, channel_count={}", sample_rate, channel_count); | 411 | auto input{ctx.ReadBuffer()}; |
| 412 | OpusMultiStreamParametersEx params; | ||
| 413 | std::memcpy(¶ms, input.data(), sizeof(OpusMultiStreamParametersEx)); | ||
| 414 | |||
| 415 | auto transfer_memory_size{rp.Pop<u32>()}; | ||
| 416 | auto transfer_memory_handle{ctx.GetCopyHandle(0)}; | ||
| 417 | auto transfer_memory{ | ||
| 418 | system.ApplicationProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>( | ||
| 419 | transfer_memory_handle)}; | ||
| 344 | 420 | ||
| 345 | ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || | 421 | LOG_DEBUG(Service_Audio, |
| 346 | sample_rate == 12000 || sample_rate == 8000, | 422 | "sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} " |
| 347 | "Invalid sample rate"); | 423 | "use_large_frame_size {}" |
| 348 | ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); | 424 | "transfer_memory_size 0x{:X}", |
| 425 | params.sample_rate, params.channel_count, params.total_stream_count, | ||
| 426 | params.stereo_stream_count, params.use_large_frame_size, transfer_memory_size); | ||
| 349 | 427 | ||
| 350 | const int num_stereo_streams = channel_count == 2 ? 1 : 0; | 428 | auto decoder{std::make_shared<IHardwareOpusDecoder>(system, impl.GetHardwareOpus())}; |
| 351 | const auto mapping_table = CreateMappingTable(channel_count); | ||
| 352 | 429 | ||
| 353 | int error = 0; | 430 | auto result = |
| 354 | OpusDecoderPtr decoder{ | 431 | decoder->Initialize(params, transfer_memory.GetPointerUnsafe(), transfer_memory_size); |
| 355 | opus_multistream_decoder_create(sample_rate, static_cast<int>(channel_count), 1, | ||
| 356 | num_stereo_streams, mapping_table.data(), &error)}; | ||
| 357 | if (error != OPUS_OK || decoder == nullptr) { | ||
| 358 | LOG_ERROR(Audio, "Failed to create Opus decoder (error={}).", error); | ||
| 359 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 360 | // TODO(ogniK): Use correct error code | ||
| 361 | rb.Push(ResultUnknown); | ||
| 362 | return; | ||
| 363 | } | ||
| 364 | 432 | ||
| 365 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 433 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 366 | rb.Push(ResultSuccess); | 434 | rb.Push(result); |
| 367 | rb.PushIpcInterface<IHardwareOpusDecoderManager>( | 435 | rb.PushIpcInterface(decoder); |
| 368 | system, OpusDecoderState{std::move(decoder), sample_rate, channel_count}); | ||
| 369 | } | 436 | } |
| 370 | 437 | ||
| 371 | void HwOpus::OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx) { | 438 | void HwOpus::GetWorkBufferSizeForMultiStreamEx(HLERequestContext& ctx) { |
| 439 | IPC::RequestParser rp{ctx}; | ||
| 440 | |||
| 441 | auto input{ctx.ReadBuffer()}; | ||
| 372 | OpusMultiStreamParametersEx params; | 442 | OpusMultiStreamParametersEx params; |
| 373 | std::memcpy(¶ms, ctx.ReadBuffer().data(), ctx.GetReadBufferSize()); | 443 | std::memcpy(¶ms, input.data(), sizeof(OpusMultiStreamParametersEx)); |
| 374 | |||
| 375 | const auto& sample_rate = params.sample_rate; | ||
| 376 | const auto& channel_count = params.channel_count; | ||
| 377 | |||
| 378 | LOG_INFO( | ||
| 379 | Audio, | ||
| 380 | "called with sample_rate={}, channel_count={}, number_streams={}, number_stereo_streams={}", | ||
| 381 | sample_rate, channel_count, params.number_streams, params.number_stereo_streams); | ||
| 382 | |||
| 383 | ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 || | ||
| 384 | sample_rate == 12000 || sample_rate == 8000, | ||
| 385 | "Invalid sample rate"); | ||
| 386 | |||
| 387 | int error = 0; | ||
| 388 | OpusDecoderPtr decoder{opus_multistream_decoder_create( | ||
| 389 | sample_rate, static_cast<int>(channel_count), params.number_streams, | ||
| 390 | params.number_stereo_streams, params.channel_mappings.data(), &error)}; | ||
| 391 | if (error != OPUS_OK || decoder == nullptr) { | ||
| 392 | LOG_ERROR(Audio, "Failed to create Opus decoder (error={}).", error); | ||
| 393 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 394 | // TODO(ogniK): Use correct error code | ||
| 395 | rb.Push(ResultUnknown); | ||
| 396 | return; | ||
| 397 | } | ||
| 398 | 444 | ||
| 399 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 445 | u64 size{}; |
| 400 | rb.Push(ResultSuccess); | 446 | auto result = impl.GetWorkBufferSizeForMultiStreamEx(params, size); |
| 401 | rb.PushIpcInterface<IHardwareOpusDecoderManager>( | 447 | |
| 402 | system, OpusDecoderState{std::move(decoder), sample_rate, channel_count}); | 448 | LOG_DEBUG(Service_Audio, |
| 449 | "sample_rate {} channel_count {} total_stream_count {} stereo_stream_count {} " | ||
| 450 | "use_large_frame_size {} -- returned size 0x{:X}", | ||
| 451 | params.sample_rate, params.channel_count, params.total_stream_count, | ||
| 452 | params.stereo_stream_count, params.use_large_frame_size, size); | ||
| 453 | |||
| 454 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 455 | rb.Push(result); | ||
| 456 | rb.Push(size); | ||
| 457 | } | ||
| 458 | |||
| 459 | void HwOpus::GetWorkBufferSizeExEx(HLERequestContext& ctx) { | ||
| 460 | IPC::RequestParser rp{ctx}; | ||
| 461 | auto params = rp.PopRaw<OpusParametersEx>(); | ||
| 462 | |||
| 463 | u64 size{}; | ||
| 464 | auto result = impl.GetWorkBufferSizeExEx(params, size); | ||
| 465 | |||
| 466 | LOG_DEBUG(Service_Audio, "size 0x{:X}", size); | ||
| 467 | |||
| 468 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 469 | rb.Push(result); | ||
| 470 | rb.Push(size); | ||
| 471 | } | ||
| 472 | |||
| 473 | void HwOpus::GetWorkBufferSizeForMultiStreamExEx(HLERequestContext& ctx) { | ||
| 474 | IPC::RequestParser rp{ctx}; | ||
| 475 | |||
| 476 | auto input{ctx.ReadBuffer()}; | ||
| 477 | OpusMultiStreamParametersEx params; | ||
| 478 | std::memcpy(¶ms, input.data(), sizeof(OpusMultiStreamParametersEx)); | ||
| 479 | |||
| 480 | u64 size{}; | ||
| 481 | auto result = impl.GetWorkBufferSizeForMultiStreamExEx(params, size); | ||
| 482 | |||
| 483 | LOG_DEBUG(Service_Audio, "size 0x{:X}", size); | ||
| 484 | |||
| 485 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 486 | rb.Push(result); | ||
| 487 | rb.Push(size); | ||
| 403 | } | 488 | } |
| 404 | 489 | ||
| 405 | HwOpus::HwOpus(Core::System& system_) : ServiceFramework{system_, "hwopus"} { | 490 | HwOpus::HwOpus(Core::System& system_) |
| 491 | : ServiceFramework{system_, "hwopus"}, system{system_}, impl{system} { | ||
| 406 | static const FunctionInfo functions[] = { | 492 | static const FunctionInfo functions[] = { |
| 407 | {0, &HwOpus::OpenHardwareOpusDecoder, "OpenHardwareOpusDecoder"}, | 493 | {0, &HwOpus::OpenHardwareOpusDecoder, "OpenHardwareOpusDecoder"}, |
| 408 | {1, &HwOpus::GetWorkBufferSize, "GetWorkBufferSize"}, | 494 | {1, &HwOpus::GetWorkBufferSize, "GetWorkBufferSize"}, |
| 409 | {2, nullptr, "OpenOpusDecoderForMultiStream"}, | 495 | {2, &HwOpus::OpenHardwareOpusDecoderForMultiStream, "OpenOpusDecoderForMultiStream"}, |
| 410 | {3, nullptr, "GetWorkBufferSizeForMultiStream"}, | 496 | {3, &HwOpus::GetWorkBufferSizeForMultiStream, "GetWorkBufferSizeForMultiStream"}, |
| 411 | {4, &HwOpus::OpenHardwareOpusDecoderEx, "OpenHardwareOpusDecoderEx"}, | 497 | {4, &HwOpus::OpenHardwareOpusDecoderEx, "OpenHardwareOpusDecoderEx"}, |
| 412 | {5, &HwOpus::GetWorkBufferSizeEx, "GetWorkBufferSizeEx"}, | 498 | {5, &HwOpus::GetWorkBufferSizeEx, "GetWorkBufferSizeEx"}, |
| 413 | {6, &HwOpus::OpenHardwareOpusDecoderForMultiStreamEx, | 499 | {6, &HwOpus::OpenHardwareOpusDecoderForMultiStreamEx, |
| 414 | "OpenHardwareOpusDecoderForMultiStreamEx"}, | 500 | "OpenHardwareOpusDecoderForMultiStreamEx"}, |
| 415 | {7, &HwOpus::GetWorkBufferSizeForMultiStreamEx, "GetWorkBufferSizeForMultiStreamEx"}, | 501 | {7, &HwOpus::GetWorkBufferSizeForMultiStreamEx, "GetWorkBufferSizeForMultiStreamEx"}, |
| 416 | {8, &HwOpus::GetWorkBufferSizeExEx, "GetWorkBufferSizeExEx"}, | 502 | {8, &HwOpus::GetWorkBufferSizeExEx, "GetWorkBufferSizeExEx"}, |
| 417 | {9, nullptr, "GetWorkBufferSizeForMultiStreamExEx"}, | 503 | {9, &HwOpus::GetWorkBufferSizeForMultiStreamExEx, "GetWorkBufferSizeForMultiStreamExEx"}, |
| 418 | }; | 504 | }; |
| 419 | RegisterHandlers(functions); | 505 | RegisterHandlers(functions); |
| 420 | } | 506 | } |
diff --git a/src/core/hle/service/audio/hwopus.h b/src/core/hle/service/audio/hwopus.h index 90867bf74..d3960065e 100644 --- a/src/core/hle/service/audio/hwopus.h +++ b/src/core/hle/service/audio/hwopus.h | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include "audio_core/opus/decoder_manager.h" | ||
| 6 | #include "core/hle/service/service.h" | 7 | #include "core/hle/service/service.h" |
| 7 | 8 | ||
| 8 | namespace Core { | 9 | namespace Core { |
| @@ -11,18 +12,6 @@ class System; | |||
| 11 | 12 | ||
| 12 | namespace Service::Audio { | 13 | namespace Service::Audio { |
| 13 | 14 | ||
| 14 | struct OpusMultiStreamParametersEx { | ||
| 15 | u32 sample_rate; | ||
| 16 | u32 channel_count; | ||
| 17 | u32 number_streams; | ||
| 18 | u32 number_stereo_streams; | ||
| 19 | u32 use_large_frame_size; | ||
| 20 | u32 padding; | ||
| 21 | std::array<u8, 0x100> channel_mappings; | ||
| 22 | }; | ||
| 23 | static_assert(sizeof(OpusMultiStreamParametersEx) == 0x118, | ||
| 24 | "OpusMultiStreamParametersEx has incorrect size"); | ||
| 25 | |||
| 26 | class HwOpus final : public ServiceFramework<HwOpus> { | 15 | class HwOpus final : public ServiceFramework<HwOpus> { |
| 27 | public: | 16 | public: |
| 28 | explicit HwOpus(Core::System& system_); | 17 | explicit HwOpus(Core::System& system_); |
| @@ -30,12 +19,18 @@ public: | |||
| 30 | 19 | ||
| 31 | private: | 20 | private: |
| 32 | void OpenHardwareOpusDecoder(HLERequestContext& ctx); | 21 | void OpenHardwareOpusDecoder(HLERequestContext& ctx); |
| 33 | void OpenHardwareOpusDecoderEx(HLERequestContext& ctx); | ||
| 34 | void OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx); | ||
| 35 | void GetWorkBufferSize(HLERequestContext& ctx); | 22 | void GetWorkBufferSize(HLERequestContext& ctx); |
| 23 | void OpenHardwareOpusDecoderForMultiStream(HLERequestContext& ctx); | ||
| 24 | void GetWorkBufferSizeForMultiStream(HLERequestContext& ctx); | ||
| 25 | void OpenHardwareOpusDecoderEx(HLERequestContext& ctx); | ||
| 36 | void GetWorkBufferSizeEx(HLERequestContext& ctx); | 26 | void GetWorkBufferSizeEx(HLERequestContext& ctx); |
| 37 | void GetWorkBufferSizeExEx(HLERequestContext& ctx); | 27 | void OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx); |
| 38 | void GetWorkBufferSizeForMultiStreamEx(HLERequestContext& ctx); | 28 | void GetWorkBufferSizeForMultiStreamEx(HLERequestContext& ctx); |
| 29 | void GetWorkBufferSizeExEx(HLERequestContext& ctx); | ||
| 30 | void GetWorkBufferSizeForMultiStreamExEx(HLERequestContext& ctx); | ||
| 31 | |||
| 32 | Core::System& system; | ||
| 33 | AudioCore::OpusDecoder::OpusDecoderManager impl; | ||
| 39 | }; | 34 | }; |
| 40 | 35 | ||
| 41 | } // namespace Service::Audio | 36 | } // namespace Service::Audio |