From bf30af18960ec1443bd166bf145e632570bd9072 Mon Sep 17 00:00:00 2001
From: Chris Robinson <chris.kcat@gmail.com>
Date: Thu, 21 Sep 2023 09:06:48 -0700
Subject: Add a SampleConverter method to convert planar buffer lines

---
 core/converter.cpp | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 core/converter.h   |  1 +
 2 files changed, 93 insertions(+)

diff --git a/core/converter.cpp b/core/converter.cpp
index b3994d3f..dea31bd5 100644
--- a/core/converter.cpp
+++ b/core/converter.cpp
@@ -309,6 +309,98 @@ uint SampleConverter::convert(const void **src, uint *srcframes, void *dst, uint
     return pos;
 }
 
+uint SampleConverter::convertPlanar(const void **src, uint *srcframes, void **dst, uint dstframes)
+{
+    const uint increment{mIncrement};
+    uint NumSrcSamples{*srcframes};
+
+    FPUCtl mixer_mode{};
+    uint pos{0};
+    while(pos < dstframes && NumSrcSamples > 0)
+    {
+        const uint prepcount{mSrcPrepCount};
+        const uint readable{minu(NumSrcSamples, BufferLineSize - prepcount)};
+
+        if(prepcount < MaxResamplerPadding && MaxResamplerPadding-prepcount >= readable)
+        {
+            /* Not enough input samples to generate an output sample. Store
+             * what we're given for later.
+             */
+            for(size_t chan{0u};chan < mChan.size();chan++)
+            {
+                LoadSamples(&mChan[chan].PrevSamples[prepcount],
+                    static_cast<const std::byte*>(src[chan]), 1, mSrcType, readable);
+                src[chan] = static_cast<const std::byte*>(src[chan]) + mSrcTypeSize*readable;
+            }
+
+            mSrcPrepCount = prepcount + readable;
+            NumSrcSamples = 0;
+            break;
+        }
+
+        float *RESTRICT SrcData{mSrcSamples};
+        float *RESTRICT DstData{mDstSamples};
+        uint DataPosFrac{mFracOffset};
+        uint64_t DataSize64{prepcount};
+        DataSize64 += readable;
+        DataSize64 -= MaxResamplerPadding;
+        DataSize64 <<= MixerFracBits;
+        DataSize64 -= DataPosFrac;
+
+        /* If we have a full prep, we can generate at least one sample. */
+        auto DstSize = static_cast<uint>(
+            clampu64((DataSize64 + increment-1)/increment, 1, BufferLineSize));
+        DstSize = minu(DstSize, dstframes-pos);
+
+        const uint DataPosEnd{DstSize*increment + DataPosFrac};
+        const uint SrcDataEnd{DataPosEnd>>MixerFracBits};
+
+        assert(prepcount+readable >= SrcDataEnd);
+        const uint nextprep{minu(prepcount + readable - SrcDataEnd, MaxResamplerPadding)};
+
+        for(size_t chan{0u};chan < mChan.size();chan++)
+        {
+            /* Load the previous samples into the source data first, then the
+             * new samples from the input buffer.
+             */
+            std::copy_n(mChan[chan].PrevSamples, prepcount, SrcData);
+            LoadSamples(SrcData + prepcount, src[chan], 1, mSrcType, readable);
+
+            /* Store as many prep samples for next time as possible, given the
+             * number of output samples being generated.
+             */
+            std::copy_n(SrcData+SrcDataEnd, nextprep, mChan[chan].PrevSamples);
+            std::fill(std::begin(mChan[chan].PrevSamples)+nextprep,
+                std::end(mChan[chan].PrevSamples), 0.0f);
+
+            /* Now resample, and store the result in the output buffer. */
+            mResample(&mState, SrcData+MaxResamplerEdge, DataPosFrac, increment,
+                {DstData, DstSize});
+
+            std::byte *DstSamples = static_cast<std::byte*>(dst[chan]) + pos*mDstTypeSize;
+            StoreSamples(DstSamples, DstData, 1, mDstType, DstSize);
+        }
+
+        /* Update the number of prep samples still available, as well as the
+         * fractional offset.
+         */
+        mSrcPrepCount = nextprep;
+        mFracOffset = DataPosEnd & MixerFracMask;
+
+        /* Update the src and dst pointers in case there's still more to do. */
+        const uint srcread{minu(NumSrcSamples, SrcDataEnd + mSrcPrepCount - prepcount)};
+        for(size_t chan{0u};chan < mChan.size();chan++)
+            src[chan] = static_cast<const std::byte*>(src[chan]) + mSrcTypeSize*srcread;
+        NumSrcSamples -= srcread;
+
+        pos += DstSize;
+    }
+
+    *srcframes = NumSrcSamples;
+
+    return pos;
+}
+
 
 void ChannelConverter::convert(const void *src, float *dst, uint frames) const
 {
diff --git a/core/converter.h b/core/converter.h
index 01becea2..d811b46b 100644
--- a/core/converter.h
+++ b/core/converter.h
@@ -36,6 +36,7 @@ struct SampleConverter {
     SampleConverter(size_t numchans) : mChan{numchans} { }
 
     uint convert(const void **src, uint *srcframes, void *dst, uint dstframes);
+    uint convertPlanar(const void **src, uint *srcframes, void **dst, uint dstframes);
     uint availableOut(uint srcframes) const;
 
     using SampleOffset = std::chrono::duration<int64_t, std::ratio<1,MixerFracOne>>;
-- 
cgit v1.2.3