Explorar o código

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."
Lioncash %!s(int64=7) %!d(string=hai) anos
pai
achega
a897feb21e
Modificáronse 1 ficheiros con 35 adicións e 4 borrados
  1. 35 4
      src/core/hle/service/audio/hwopus.cpp

+ 35 - 4
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<bool>() ? 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<opus_int16> 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);