summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
authorGravatar Kelebek12023-08-31 15:09:15 +0100
committerGravatar Liam2023-09-16 11:56:25 -0400
commit67e2d5c28b8423c4f3f1d5b00f87325684158a6f (patch)
treee419a2bb6c064ddc69a49046705b6187772fee48 /src/core
parentMerge pull request #11519 from german77/system-policy (diff)
downloadyuzu-67e2d5c28b8423c4f3f1d5b00f87325684158a6f.tar.gz
yuzu-67e2d5c28b8423c4f3f1d5b00f87325684158a6f.tar.xz
yuzu-67e2d5c28b8423c4f3f1d5b00f87325684158a6f.zip
Reimplement HardwareOpus
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt2
-rw-r--r--src/core/hle/result.h2
-rw-r--r--src/core/hle/service/audio/errors.h12
-rw-r--r--src/core/hle/service/audio/hwopus.cpp722
-rw-r--r--src/core/hle/service/audio/hwopus.h25
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()
890create_target_directory_groups(core) 890create_target_directory_groups(core)
891 891
892target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core nx_tzdb) 892target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core nx_tzdb)
893target_link_libraries(core PUBLIC Boost::headers PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls Opus::opus renderdoc) 893target_link_libraries(core PUBLIC Boost::headers PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls renderdoc)
894if (MINGW) 894if (MINGW)
895 target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY}) 895 target_link_libraries(core PRIVATE ${MSWSOCK_LIBRARY})
896endif() 896endif()
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};
20constexpr Result ResultInvalidHandle{ErrorModule::Audio, 1536}; 20constexpr Result ResultInvalidHandle{ErrorModule::Audio, 1536};
21constexpr Result ResultInvalidRevision{ErrorModule::Audio, 1537}; 21constexpr Result ResultInvalidRevision{ErrorModule::Audio, 1537};
22 22
23constexpr Result ResultLibOpusAllocFail{ErrorModule::HwOpus, 7};
24constexpr Result ResultInputDataTooSmall{ErrorModule::HwOpus, 8};
25constexpr Result ResultLibOpusInvalidState{ErrorModule::HwOpus, 6};
26constexpr Result ResultLibOpusUnimplemented{ErrorModule::HwOpus, 5};
27constexpr Result ResultLibOpusInvalidPacket{ErrorModule::HwOpus, 17};
28constexpr Result ResultLibOpusInternalError{ErrorModule::HwOpus, 4};
29constexpr Result ResultBufferTooSmall{ErrorModule::HwOpus, 3};
30constexpr Result ResultLibOpusBadArg{ErrorModule::HwOpus, 2};
31constexpr Result ResultInvalidOpusDSPReturnCode{ErrorModule::HwOpus, 259};
32constexpr Result ResultInvalidOpusSampleRate{ErrorModule::HwOpus, 1001};
33constexpr 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
18namespace Service::Audio { 16namespace Service::Audio {
19namespace { 17using namespace AudioCore::OpusDecoder;
20struct OpusDeleter { 18
21 void operator()(OpusMSDecoder* ptr) const { 19class IHardwareOpusDecoder final : public ServiceFramework<IHardwareOpusDecoder> {
22 opus_multistream_decoder_destroy(ptr); 20public:
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
26using 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
28struct 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};
34static_assert(sizeof(OpusPacketHeader) == 0x8, "OpusHeader is an invalid size");
35 51
36class OpusDecoderState { 52private:
37public: 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
67private: 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
161class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> { 146 auto input_data{ctx.ReadBuffer(0)};
162public: 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
184private: 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
222std::size_t WorkerBufferSize(u32 channel_count) { 275void 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}",
237std::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
246void HwOpus::GetWorkBufferSize(HLERequestContext& ctx) { 302void 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
266void HwOpus::GetWorkBufferSizeEx(HLERequestContext& ctx) { 317void HwOpus::OpenHardwareOpusDecoderForMultiStream(HLERequestContext& ctx) {
267 GetWorkBufferSize(ctx); 318 IPC::RequestParser rp{ctx};
319
320 auto input{ctx.ReadBuffer()};
321 OpusMultiStreamParameters params;
322 std::memcpy(&params, 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
270void HwOpus::GetWorkBufferSizeExEx(HLERequestContext& ctx) { 354void HwOpus::GetWorkBufferSizeForMultiStream(HLERequestContext& ctx) {
271 GetWorkBufferSizeEx(ctx); 355 IPC::RequestParser rp{ctx};
356
357 auto input{ctx.ReadBuffer()};
358 OpusMultiStreamParameters params;
359 std::memcpy(&params, 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
274void HwOpus::GetWorkBufferSizeForMultiStreamEx(HLERequestContext& ctx) { 371void HwOpus::OpenHardwareOpusDecoderEx(HLERequestContext& ctx) {
275 OpusMultiStreamParametersEx param; 372 IPC::RequestParser rp{ctx};
276 std::memcpy(&param, 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
300void HwOpus::OpenHardwareOpusDecoder(HLERequestContext& ctx) { 394void 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
338void HwOpus::OpenHardwareOpusDecoderEx(HLERequestContext& ctx) { 408void 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(&params, 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
371void HwOpus::OpenHardwareOpusDecoderForMultiStreamEx(HLERequestContext& ctx) { 438void HwOpus::GetWorkBufferSizeForMultiStreamEx(HLERequestContext& ctx) {
439 IPC::RequestParser rp{ctx};
440
441 auto input{ctx.ReadBuffer()};
372 OpusMultiStreamParametersEx params; 442 OpusMultiStreamParametersEx params;
373 std::memcpy(&params, ctx.ReadBuffer().data(), ctx.GetReadBufferSize()); 443 std::memcpy(&params, 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
459void 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
473void HwOpus::GetWorkBufferSizeForMultiStreamExEx(HLERequestContext& ctx) {
474 IPC::RequestParser rp{ctx};
475
476 auto input{ctx.ReadBuffer()};
477 OpusMultiStreamParametersEx params;
478 std::memcpy(&params, 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
405HwOpus::HwOpus(Core::System& system_) : ServiceFramework{system_, "hwopus"} { 490HwOpus::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
8namespace Core { 9namespace Core {
@@ -11,18 +12,6 @@ class System;
11 12
12namespace Service::Audio { 13namespace Service::Audio {
13 14
14struct 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};
23static_assert(sizeof(OpusMultiStreamParametersEx) == 0x118,
24 "OpusMultiStreamParametersEx has incorrect size");
25
26class HwOpus final : public ServiceFramework<HwOpus> { 15class HwOpus final : public ServiceFramework<HwOpus> {
27public: 16public:
28 explicit HwOpus(Core::System& system_); 17 explicit HwOpus(Core::System& system_);
@@ -30,12 +19,18 @@ public:
30 19
31private: 20private:
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