From a897feb21e603a7844c44a8ef5e00ce42ab96b43 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Tue, 29 Jan 2019 18:21:17 -0500 Subject: hwopus: Implement DecodeInterleaved This functions almost identically to DecodeInterleavedWithPerfOld, however this function also has the ability to reset the decoder context. This is documented as a potentially desirable thing in the libopus manual in some circumstances as it says for the OPUS_RESET_STATE ctl: "This should be called when switching streams in order to prevent the back to back decoding from giving different result from one at a time decoding." --- src/core/hle/service/audio/hwopus.cpp | 39 +++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) (limited to 'src/core/hle/service') diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp index dffb890d5..11eba4a12 100644 --- a/src/core/hle/service/audio/hwopus.cpp +++ b/src/core/hle/service/audio/hwopus.cpp @@ -37,7 +37,7 @@ public: {3, nullptr, "SetContextForMultiStream"}, {4, &IHardwareOpusDecoderManager::DecodeInterleavedWithPerfOld, "DecodeInterleavedWithPerfOld"}, {5, nullptr, "DecodeInterleavedForMultiStreamWithPerfOld"}, - {6, nullptr, "DecodeInterleaved"}, + {6, &IHardwareOpusDecoderManager::DecodeInterleaved, "DecodeInterleaved"}, {7, nullptr, "DecodeInterleavedForMultiStream"}, }; // clang-format on @@ -46,24 +46,49 @@ public: } private: + /// Describes extra behavior that may be asked of the decoding context. + enum class ExtraBehavior { + /// No extra behavior. + None, + + /// Resets the decoder context back to a freshly initialized state. + ResetContext, + }; + void DecodeInterleavedOld(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Audio, "called"); - DecodeInterleavedHelper(ctx, nullptr); + DecodeInterleavedHelper(ctx, nullptr, ExtraBehavior::None); } void DecodeInterleavedWithPerfOld(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Audio, "called"); u64 performance = 0; - DecodeInterleavedHelper(ctx, &performance); + DecodeInterleavedHelper(ctx, &performance, ExtraBehavior::None); + } + + void DecodeInterleaved(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Audio, "called"); + + IPC::RequestParser rp{ctx}; + const auto extra_behavior = + rp.Pop() ? ExtraBehavior::ResetContext : ExtraBehavior::None; + + u64 performance = 0; + DecodeInterleavedHelper(ctx, &performance, extra_behavior); } - void DecodeInterleavedHelper(Kernel::HLERequestContext& ctx, u64* performance) { + void DecodeInterleavedHelper(Kernel::HLERequestContext& ctx, u64* performance, + ExtraBehavior extra_behavior) { u32 consumed = 0; u32 sample_count = 0; std::vector samples(ctx.GetWriteBufferSize() / sizeof(opus_int16)); + if (extra_behavior == ExtraBehavior::ResetContext) { + ResetDecoderContext(); + } + if (!Decoder_DecodeInterleaved(consumed, sample_count, ctx.ReadBuffer(), samples, performance)) { LOG_ERROR(Audio, "Failed to decode opus data"); @@ -136,6 +161,12 @@ private: return true; } + void ResetDecoderContext() { + ASSERT(decoder != nullptr); + + opus_decoder_ctl(decoder.get(), OPUS_RESET_STATE); + } + struct OpusHeader { u32_be sz; // Needs to be BE for some odd reason INSERT_PADDING_WORDS(1); -- cgit v1.2.3