From 4cfb9301d1b3fb487e68ede6e8910ae52db6a256 Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Wed, 15 May 2024 17:58:14 -0400 Subject: [PATCH] Apply channel gains on the producer side of the RingBuffer --- libraries/lib-audio-io/AudioIO.cpp | 60 +++++++++++++++++++----------- libraries/lib-audio-io/AudioIO.h | 7 +--- 2 files changed, 40 insertions(+), 27 deletions(-) diff --git a/libraries/lib-audio-io/AudioIO.cpp b/libraries/lib-audio-io/AudioIO.cpp index c53013e64ab3..3e91c3f642b7 100644 --- a/libraries/lib-audio-io/AudioIO.cpp +++ b/libraries/lib-audio-io/AudioIO.cpp @@ -2086,12 +2086,19 @@ void AudioIO::TransformPlayBuffers( const auto numPlaybackSequences = mPlaybackSequences.size(); // mPlaybackBuffers correspond many-to-one with mPlaybackSequences size_t iBuffer = 0; + const auto factor = decayFactor(mRate); + const bool silent = + mForceFadeOut.load(std::memory_order_relaxed) || IsPaused(); + size_t iTrack = 0; for (const auto vt : mPlaybackSequences) { + Finally Do{ [&]{ ++iTrack; } }; if (!vt) continue; const auto pGroup = vt->FindChannelGroup(); if (!pGroup) continue; + // Check for asynchronous user changes in mute, solo, pause status + const bool drop = (silent || SequenceShouldBeSilent(*vt)); // Loop over the blocks of unflushed data, at most two size_t discardable = 0; @@ -2123,6 +2130,33 @@ void AudioIO::TransformPlayBuffers( auto discarded = ringBuffer.Unput(discardable); // assert(discarded == discardable); } + // Iterate the two blocks again, only after latency has been shifted out, + // which maybe changes the extent of those blocks in memory, and apply + // the per-track pan, mute, gain, and solo, with microfading + for (unsigned iBlock : {0, 1}) { + size_t len = 0; + for (size_t iChannel = 0; iChannel < mNumPlaybackChannels; ++iChannel) { + const float goal = (drop ? 0.0f : vt->GetChannelGain(iChannel)); + auto &laggingChannelGain = mOldChannelGains[iTrack][iChannel]; + // if no microfades, jump in volume. + float diff = (mbMicroFades ? goal - laggingChannelGain : 0.0f); + auto &ringBuffer = *mPlaybackBuffers[iBuffer + iChannel]; + const auto pair = ringBuffer.GetUnflushed(iBlock); + // Playback RingBuffers have float format: see AllocateBuffers + auto pFloats = reinterpret_cast(pair.first); + // The lengths of corresponding unflushed blocks should be + // the same for all channels + if (len == 0) + len = pair.second; + else + assert(len == pair.second); + for (size_t ii = 0; ii < len; ++ii) { + pFloats[ii] *= (goal - diff); + diff *= factor; + } + laggingChannelGain = goal - diff; + } + } iBuffer += mNumPlaybackChannels; } } @@ -2582,31 +2616,17 @@ void AudioIoCallback::AddToOutputChannel(unsigned int chan, float * outputMeterFloats, float * outputFloats, const float * tempBuf, - bool drop, - const unsigned long len, - const PlayableSequence &ps, - float &laggingChannelGain) + const unsigned long len) { const auto numPlaybackChannels = mNumPlaybackChannels; - const auto factor = decayFactor(mRate); - - float gain = ps.GetChannelGain(chan); - if (drop || mForceFadeOut.load(std::memory_order_relaxed) || IsPaused()) - gain = 0.0; - - const float goal = gain; - // if no microfades, jump in volume. - float diff = (mbMicroFades ? goal - laggingChannelGain : 0.0f); for (size_t i = 0; i < len; ++i) { - const auto term = (goal - diff) * tempBuf[i]; + const auto term = tempBuf[i]; outputFloats[numPlaybackChannels * i + chan] += term; if (outputMeterFloats != outputFloats) // The level shown in output meters also sums the track level // microfaded for gain, pan, and mute, but before applying master gain outputMeterFloats[numPlaybackChannels * i + chan] += term; - diff *= factor; } - laggingChannelGain = goal - diff; }; // Limit values to -1.0..+1.0 @@ -2684,9 +2704,6 @@ bool AudioIoCallback::FillOutputBuffers( for (unsigned tt = 0; tt < numPlaybackSequences; ++tt) { auto vt = mPlaybackSequences[tt].get(); - // Check for asynchronous user changes in mute, solo, pause status - const bool drop = SequenceShouldBeSilent(*vt); - decltype(framesPerBuffer) len = 0; for (size_t c = 0; c < mNumPlaybackChannels; ++c) { @@ -2725,12 +2742,11 @@ bool AudioIoCallback::FillOutputBuffers( // the device. For example mono channels output to both left and right // output channels. if (len > 0) { - auto &gains = mOldChannelGains[tt]; AddToOutputChannel(0, outputMeterFloats, outputFloats, - tempBufs[0], drop, len, *vt, gains[0]); + tempBufs[0], len); AddToOutputChannel(1, outputMeterFloats, outputFloats, - tempBufs[1], drop, len, *vt, gains[1]); + tempBufs[1], len); // Output volume emulation const auto factor = decayFactor(mRate); diff --git a/libraries/lib-audio-io/AudioIO.h b/libraries/lib-audio-io/AudioIO.h index 4f1af8198332..ef3727ada7ce 100644 --- a/libraries/lib-audio-io/AudioIO.h +++ b/libraries/lib-audio-io/AudioIO.h @@ -194,14 +194,11 @@ class AUDIO_IO_API AudioIoCallback /* not final */ /*! @param[in,out] channelGain */ - void AddToOutputChannel( unsigned int chan, // index into gains + void AddToOutputChannel(unsigned int chan, float * outputMeterFloats, float * outputFloats, const float * tempBuf, - bool drop, - unsigned long len, - const PlayableSequence &ps, - float &laggingChannelGain + unsigned long len ); bool FillOutputBuffers( float *outputBuffer,