summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Lioncash2019-03-11 04:04:38 -0400
committerGravatar Lioncash2019-03-11 07:06:18 -0400
commit7ad3d4e49cbf8490dff39606d9683aa4efd09c02 (patch)
tree17a4109284983d5496fa4f0891f4fafd42f0c798 /src
parentMerge pull request #2207 from lioncash/hwopus (diff)
downloadyuzu-7ad3d4e49cbf8490dff39606d9683aa4efd09c02.tar.gz
yuzu-7ad3d4e49cbf8490dff39606d9683aa4efd09c02.tar.xz
yuzu-7ad3d4e49cbf8490dff39606d9683aa4efd09c02.zip
hwopus: Leverage multistream API for decoding regular Opus packets
After doing a little more reading up on the Opus codec, it turns out that the multistream API that is part of libopus can handle regular packets. Regular packets are just a degenerate case of multistream Opus packets, and all that's necessary is to pass the number of streams as 1 and provide a basic channel mapping, then everything works fine for that case. This allows us to get rid of the need to use both APIs in the future when implementing multistream variants in a follow-up PR, greatly simplifying the code that needs to be written.
Diffstat (limited to '')
-rw-r--r--src/core/hle/service/audio/hwopus.cpp82
1 files changed, 48 insertions, 34 deletions
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index 377e12cfa..cb4a1160d 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -8,6 +8,7 @@
8#include <vector> 8#include <vector>
9 9
10#include <opus.h> 10#include <opus.h>
11#include <opus_multistream.h>
11 12
12#include "common/assert.h" 13#include "common/assert.h"
13#include "common/logging/log.h" 14#include "common/logging/log.h"
@@ -18,12 +19,12 @@
18namespace Service::Audio { 19namespace Service::Audio {
19namespace { 20namespace {
20struct OpusDeleter { 21struct OpusDeleter {
21 void operator()(void* ptr) const { 22 void operator()(OpusMSDecoder* ptr) const {
22 operator delete(ptr); 23 opus_multistream_decoder_destroy(ptr);
23 } 24 }
24}; 25};
25 26
26using OpusDecoderPtr = std::unique_ptr<OpusDecoder, OpusDeleter>; 27using OpusDecoderPtr = std::unique_ptr<OpusMSDecoder, OpusDeleter>;
27 28
28struct OpusPacketHeader { 29struct OpusPacketHeader {
29 // Packet size in bytes. 30 // Packet size in bytes.
@@ -33,7 +34,7 @@ struct OpusPacketHeader {
33}; 34};
34static_assert(sizeof(OpusPacketHeader) == 0x8, "OpusHeader is an invalid size"); 35static_assert(sizeof(OpusPacketHeader) == 0x8, "OpusHeader is an invalid size");
35 36
36class OpusDecoderStateBase { 37class OpusDecoderState {
37public: 38public:
38 /// Describes extra behavior that may be asked of the decoding context. 39 /// Describes extra behavior that may be asked of the decoding context.
39 enum class ExtraBehavior { 40 enum class ExtraBehavior {
@@ -49,22 +50,13 @@ public:
49 Enabled, 50 Enabled,
50 }; 51 };
51 52
52 virtual ~OpusDecoderStateBase() = default;
53
54 // Decodes interleaved Opus packets. Optionally allows reporting time taken to
55 // perform the decoding, as well as any relevant extra behavior.
56 virtual void DecodeInterleaved(Kernel::HLERequestContext& ctx, PerfTime perf_time,
57 ExtraBehavior extra_behavior) = 0;
58};
59
60// Represents the decoder state for a non-multistream decoder.
61class OpusDecoderState final : public OpusDecoderStateBase {
62public:
63 explicit OpusDecoderState(OpusDecoderPtr decoder, u32 sample_rate, u32 channel_count) 53 explicit OpusDecoderState(OpusDecoderPtr decoder, u32 sample_rate, u32 channel_count)
64 : decoder{std::move(decoder)}, sample_rate{sample_rate}, channel_count{channel_count} {} 54 : decoder{std::move(decoder)}, sample_rate{sample_rate}, channel_count{channel_count} {}
65 55
56 // Decodes interleaved Opus packets. Optionally allows reporting time taken to
57 // perform the decoding, as well as any relevant extra behavior.
66 void DecodeInterleaved(Kernel::HLERequestContext& ctx, PerfTime perf_time, 58 void DecodeInterleaved(Kernel::HLERequestContext& ctx, PerfTime perf_time,
67 ExtraBehavior extra_behavior) override { 59 ExtraBehavior extra_behavior) {
68 if (perf_time == PerfTime::Disabled) { 60 if (perf_time == PerfTime::Disabled) {
69 DecodeInterleavedHelper(ctx, nullptr, extra_behavior); 61 DecodeInterleavedHelper(ctx, nullptr, extra_behavior);
70 } else { 62 } else {
@@ -135,7 +127,7 @@ private:
135 127
136 const int frame_size = (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count)); 128 const int frame_size = (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count));
137 const auto out_sample_count = 129 const auto out_sample_count =
138 opus_decode(decoder.get(), frame, hdr.size, output.data(), frame_size, 0); 130 opus_multistream_decode(decoder.get(), frame, hdr.size, output.data(), frame_size, 0);
139 if (out_sample_count < 0) { 131 if (out_sample_count < 0) {
140 LOG_ERROR(Audio, 132 LOG_ERROR(Audio,
141 "Incorrect sample count received from opus_decode, " 133 "Incorrect sample count received from opus_decode, "
@@ -158,7 +150,7 @@ private:
158 void ResetDecoderContext() { 150 void ResetDecoderContext() {
159 ASSERT(decoder != nullptr); 151 ASSERT(decoder != nullptr);
160 152
161 opus_decoder_ctl(decoder.get(), OPUS_RESET_STATE); 153 opus_multistream_decoder_ctl(decoder.get(), OPUS_RESET_STATE);
162 } 154 }
163 155
164 OpusDecoderPtr decoder; 156 OpusDecoderPtr decoder;
@@ -168,7 +160,7 @@ private:
168 160
169class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> { 161class IHardwareOpusDecoderManager final : public ServiceFramework<IHardwareOpusDecoderManager> {
170public: 162public:
171 explicit IHardwareOpusDecoderManager(std::unique_ptr<OpusDecoderStateBase> decoder_state) 163 explicit IHardwareOpusDecoderManager(OpusDecoderState decoder_state)
172 : ServiceFramework("IHardwareOpusDecoderManager"), decoder_state{std::move(decoder_state)} { 164 : ServiceFramework("IHardwareOpusDecoderManager"), decoder_state{std::move(decoder_state)} {
173 // clang-format off 165 // clang-format off
174 static const FunctionInfo functions[] = { 166 static const FunctionInfo functions[] = {
@@ -190,35 +182,51 @@ private:
190 void DecodeInterleavedOld(Kernel::HLERequestContext& ctx) { 182 void DecodeInterleavedOld(Kernel::HLERequestContext& ctx) {
191 LOG_DEBUG(Audio, "called"); 183 LOG_DEBUG(Audio, "called");
192 184
193 decoder_state->DecodeInterleaved(ctx, OpusDecoderStateBase::PerfTime::Disabled, 185 decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Disabled,
194 OpusDecoderStateBase::ExtraBehavior::None); 186 OpusDecoderState::ExtraBehavior::None);
195 } 187 }
196 188
197 void DecodeInterleavedWithPerfOld(Kernel::HLERequestContext& ctx) { 189 void DecodeInterleavedWithPerfOld(Kernel::HLERequestContext& ctx) {
198 LOG_DEBUG(Audio, "called"); 190 LOG_DEBUG(Audio, "called");
199 191
200 decoder_state->DecodeInterleaved(ctx, OpusDecoderStateBase::PerfTime::Enabled, 192 decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled,
201 OpusDecoderStateBase::ExtraBehavior::None); 193 OpusDecoderState::ExtraBehavior::None);
202 } 194 }
203 195
204 void DecodeInterleaved(Kernel::HLERequestContext& ctx) { 196 void DecodeInterleaved(Kernel::HLERequestContext& ctx) {
205 LOG_DEBUG(Audio, "called"); 197 LOG_DEBUG(Audio, "called");
206 198
207 IPC::RequestParser rp{ctx}; 199 IPC::RequestParser rp{ctx};
208 const auto extra_behavior = rp.Pop<bool>() 200 const auto extra_behavior = rp.Pop<bool>() ? OpusDecoderState::ExtraBehavior::ResetContext
209 ? OpusDecoderStateBase::ExtraBehavior::ResetContext 201 : OpusDecoderState::ExtraBehavior::None;
210 : OpusDecoderStateBase::ExtraBehavior::None;
211 202
212 decoder_state->DecodeInterleaved(ctx, OpusDecoderStateBase::PerfTime::Enabled, 203 decoder_state.DecodeInterleaved(ctx, OpusDecoderState::PerfTime::Enabled, extra_behavior);
213 extra_behavior);
214 } 204 }
215 205
216 std::unique_ptr<OpusDecoderStateBase> decoder_state; 206 OpusDecoderState decoder_state;
217}; 207};
218 208
219std::size_t WorkerBufferSize(u32 channel_count) { 209std::size_t WorkerBufferSize(u32 channel_count) {
220 ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); 210 ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count");
221 return opus_decoder_get_size(static_cast<int>(channel_count)); 211 constexpr int num_streams = 1;
212 const int num_stereo_streams = channel_count == 2 ? 1 : 0;
213 return opus_multistream_decoder_get_size(num_streams, num_stereo_streams);
214}
215
216// Creates the mapping table that maps the input channels to the particular
217// output channels. In the stereo case, we map the left and right input channels
218// to the left and right output channels respectively.
219//
220// However, in the monophonic case, we only map the one available channel
221// to the sole output channel. We specify 255 for the would-be right channel
222// as this is a special value defined by Opus to indicate to the decoder to
223// ignore that channel.
224std::array<u8, 2> CreateMappingTable(u32 channel_count) {
225 if (channel_count == 2) {
226 return {{0, 1}};
227 }
228
229 return {{0, 255}};
222} 230}
223} // Anonymous namespace 231} // Anonymous namespace
224 232
@@ -259,9 +267,15 @@ void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) {
259 const std::size_t worker_sz = WorkerBufferSize(channel_count); 267 const std::size_t worker_sz = WorkerBufferSize(channel_count);
260 ASSERT_MSG(buffer_sz >= worker_sz, "Worker buffer too large"); 268 ASSERT_MSG(buffer_sz >= worker_sz, "Worker buffer too large");
261 269
262 OpusDecoderPtr decoder{static_cast<OpusDecoder*>(operator new(worker_sz))}; 270 const int num_stereo_streams = channel_count == 2 ? 1 : 0;
263 if (const int err = opus_decoder_init(decoder.get(), sample_rate, channel_count)) { 271 const auto mapping_table = CreateMappingTable(channel_count);
264 LOG_ERROR(Audio, "Failed to init opus decoder with error={}", err); 272
273 int error = 0;
274 OpusDecoderPtr decoder{
275 opus_multistream_decoder_create(sample_rate, static_cast<int>(channel_count), 1,
276 num_stereo_streams, mapping_table.data(), &error)};
277 if (error != OPUS_OK || decoder == nullptr) {
278 LOG_ERROR(Audio, "Failed to create Opus decoder (error={}).", error);
265 IPC::ResponseBuilder rb{ctx, 2}; 279 IPC::ResponseBuilder rb{ctx, 2};
266 // TODO(ogniK): Use correct error code 280 // TODO(ogniK): Use correct error code
267 rb.Push(ResultCode(-1)); 281 rb.Push(ResultCode(-1));
@@ -271,7 +285,7 @@ void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) {
271 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 285 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
272 rb.Push(RESULT_SUCCESS); 286 rb.Push(RESULT_SUCCESS);
273 rb.PushIpcInterface<IHardwareOpusDecoderManager>( 287 rb.PushIpcInterface<IHardwareOpusDecoderManager>(
274 std::make_unique<OpusDecoderState>(std::move(decoder), sample_rate, channel_count)); 288 OpusDecoderState{std::move(decoder), sample_rate, channel_count});
275} 289}
276 290
277HwOpus::HwOpus() : ServiceFramework("hwopus") { 291HwOpus::HwOpus() : ServiceFramework("hwopus") {