From debb932573010fb7b260f56d618644dbadd2e6b1 Mon Sep 17 00:00:00 2001
From: Chris Robinson <chris.kcat@gmail.com>
Date: Tue, 8 Jun 2021 10:52:37 -0700
Subject: Add an option to mix directly in the JACK callback

---
 alc/alu.cpp | 102 ++++++++++++++++++++++++++++++++++++++----------------------
 1 file changed, 64 insertions(+), 38 deletions(-)

(limited to 'alc/alu.cpp')

diff --git a/alc/alu.cpp b/alc/alu.cpp
index 00b91bd4..507a5de1 100644
--- a/alc/alu.cpp
+++ b/alc/alu.cpp
@@ -1925,52 +1925,78 @@ void Write(const al::span<const FloatBufferLine> InBuffer, void *OutBuffer, cons
 
 } // namespace
 
-void DeviceBase::renderSamples(void *outBuffer, const uint numSamples, const size_t frameStep)
+uint DeviceBase::renderSamples(const uint numSamples)
 {
-    FPUCtl mixer_mode{};
-    for(uint written{0u};written < numSamples;)
-    {
-        const uint samplesToDo{minu(numSamples-written, BufferLineSize)};
+    const uint samplesToDo{minu(numSamples, BufferLineSize)};
 
-        /* Clear main mixing buffers. */
-        for(FloatBufferLine &buffer : MixBuffer)
-            buffer.fill(0.0f);
+    /* Clear main mixing buffers. */
+    for(FloatBufferLine &buffer : MixBuffer)
+        buffer.fill(0.0f);
 
-        /* Increment the mix count at the start (lsb should now be 1). */
-        IncrementRef(MixCount);
+    /* Increment the mix count at the start (lsb should now be 1). */
+    IncrementRef(MixCount);
 
-        /* Process and mix each context's sources and effects. */
-        ProcessContexts(this, samplesToDo);
+    /* Process and mix each context's sources and effects. */
+    ProcessContexts(this, samplesToDo);
 
-        /* Increment the clock time. Every second's worth of samples is
-         * converted and added to clock base so that large sample counts don't
-         * overflow during conversion. This also guarantees a stable
-         * conversion.
-         */
-        SamplesDone += samplesToDo;
-        ClockBase += std::chrono::seconds{SamplesDone / Frequency};
-        SamplesDone %= Frequency;
+    /* Increment the clock time. Every second's worth of samples is converted
+     * and added to clock base so that large sample counts don't overflow
+     * during conversion. This also guarantees a stable conversion.
+     */
+    SamplesDone += samplesToDo;
+    ClockBase += std::chrono::seconds{SamplesDone / Frequency};
+    SamplesDone %= Frequency;
+
+    /* Increment the mix count at the end (lsb should now be 0). */
+    IncrementRef(MixCount);
 
-        /* Increment the mix count at the end (lsb should now be 0). */
-        IncrementRef(MixCount);
+    /* Apply any needed post-process for finalizing the Dry mix to the RealOut
+     * (Ambisonic decode, UHJ encode, etc).
+     */
+    postProcess(samplesToDo);
 
-        /* Apply any needed post-process for finalizing the Dry mix to the
-         * RealOut (Ambisonic decode, UHJ encode, etc).
-         */
-        postProcess(samplesToDo);
+    /* Apply compression, limiting sample amplitude if needed or desired. */
+    if(Limiter) Limiter->process(samplesToDo, RealOut.Buffer.data());
 
-        /* Apply compression, limiting sample amplitude if needed or desired. */
-        if(Limiter) Limiter->process(samplesToDo, RealOut.Buffer.data());
+    /* Apply delays and attenuation for mismatched speaker distances. */
+    if(ChannelDelays)
+        ApplyDistanceComp(RealOut.Buffer, samplesToDo, ChannelDelays->mChannels.data());
 
-        /* Apply delays and attenuation for mismatched speaker distances. */
-        if(ChannelDelays)
-            ApplyDistanceComp(RealOut.Buffer, samplesToDo, ChannelDelays->mChannels.data());
+    /* Apply dithering. The compressor should have left enough headroom for the
+     * dither noise to not saturate.
+     */
+    if(DitherDepth > 0.0f)
+        ApplyDither(RealOut.Buffer, &DitherSeed, DitherDepth, samplesToDo);
 
-        /* Apply dithering. The compressor should have left enough headroom for
-         * the dither noise to not saturate.
-         */
-        if(DitherDepth > 0.0f)
-            ApplyDither(RealOut.Buffer, &DitherSeed, DitherDepth, samplesToDo);
+    return samplesToDo;
+}
+
+void DeviceBase::renderSamples(const al::span<float*> outBuffers, const uint numSamples)
+{
+    FPUCtl mixer_mode{};
+    uint total{0};
+    while(const uint todo{numSamples - total})
+    {
+        const uint samplesToDo{renderSamples(todo)};
+
+        auto *srcbuf = RealOut.Buffer.data();
+        for(auto *dstbuf : outBuffers)
+        {
+            std::copy_n(srcbuf->data(), samplesToDo, dstbuf + total);
+            ++srcbuf;
+        }
+
+        total += samplesToDo;
+    }
+}
+
+void DeviceBase::renderSamples(void *outBuffer, const uint numSamples, const size_t frameStep)
+{
+    FPUCtl mixer_mode{};
+    uint total{0};
+    while(const uint todo{numSamples - total})
+    {
+        const uint samplesToDo{renderSamples(todo)};
 
         if LIKELY(outBuffer)
         {
@@ -1980,7 +2006,7 @@ void DeviceBase::renderSamples(void *outBuffer, const uint numSamples, const siz
             switch(FmtType)
             {
 #define HANDLE_WRITE(T) case T:                                               \
-    Write<T>(RealOut.Buffer, outBuffer, written, samplesToDo, frameStep); break;
+    Write<T>(RealOut.Buffer, outBuffer, total, samplesToDo, frameStep); break;
             HANDLE_WRITE(DevFmtByte)
             HANDLE_WRITE(DevFmtUByte)
             HANDLE_WRITE(DevFmtShort)
@@ -1992,7 +2018,7 @@ void DeviceBase::renderSamples(void *outBuffer, const uint numSamples, const siz
             }
         }
 
-        written += samplesToDo;
+        total += samplesToDo;
     }
 }
 
-- 
cgit v1.2.3