Looking for feedback on Sampler Voice reverse implementation


#1

Hi there,

I hate to ask this question this way, but I couldn’t think of any other way but to just blurt out all the code involved for someone kind to look over.

Basically I’m trying to implement sample reverse playback using JUCE’s SamplerVoice code as a starting point. The only thing I’ve changed is the class’s startNote() and renderNextBlock() methods shown below. Reverse playback is enabled/disabled via the member: bool is_reverse_playing

Honestly, I don’t completely understand how the render method works and was wondering if someone could find anything wrong with my modifications.

Many thanks in advance for your help.


In the voice’s startNote() method:

        sourceSamplePosition = owner.is_reverse_playing ? sound->length : 0.;

void Sampler::Voice::renderNextBlock (AudioBuffer<float>& outputBuffer, int startSample, int numSamples)
{
    if (auto* playingSound = static_cast<Sound*> (getCurrentlyPlayingSound().get())) {
        
        auto& data = *playingSound->data;
        const float* const inL = data.getReadPointer (0);
        const float* const inR = data.getNumChannels() > 1 ? data.getReadPointer (1) : nullptr;
        
        float* outL = outputBuffer.getWritePointer (0, startSample);
        float* outR = outputBuffer.getNumChannels() > 1 ? outputBuffer.getWritePointer (1, startSample) : nullptr;
        
        while (--numSamples >= 0) {
            
            auto pos = owner.is_reverse_playing ? (int)std::ceil(sourceSamplePosition) : (int) sourceSamplePosition;
            auto alpha = owner.is_reverse_playing ? (float) (pos - sourceSamplePosition) : (float) (sourceSamplePosition - pos);
            auto invAlpha = 1.0f - alpha;
            
            // just using a very simple linear interpolation here..
            float l = (inL[pos] * invAlpha + inL[pos + (owner.is_reverse_playing ? -1 : 1)] * alpha);
            float r = (inR != nullptr) ? (inR[pos] * invAlpha + inR[pos + (owner.is_reverse_playing ? -1 : 1)] * alpha)
                                       : l;
            
            l *= lgain;
            r *= rgain;
            
            if (isInAttack) {
                l *= attackReleaseLevel;
                r *= attackReleaseLevel;
                
                attackReleaseLevel += attackDelta;
                
                if (attackReleaseLevel >= 1.0f) {
                    attackReleaseLevel = 1.0f;
                    isInAttack = false;
                }
            }
            else if (isInRelease) {
                l *= attackReleaseLevel;
                r *= attackReleaseLevel;
                
                attackReleaseLevel += releaseDelta;
                
                if (attackReleaseLevel <= 0.0f) {
                    stopNote (0.0f, false);
                    break;
                }
            }
            if (outR != nullptr) {
                *outL++ += l;
                *outR++ += r;
            }
            else {
                *outL++ += (l + r) * 0.5f;
            }
            
            sourceSamplePosition += (owner.is_reverse_playing ? -pitchRatio : pitchRatio);
            if (owner.is_reverse_playing ? (sourceSamplePosition < 0.)
                                         : (sourceSamplePosition > playingSound->length)) {
                stopNote (0.0f, false);
                break;
            }
        }
    }
}

#2

The main section in question is the code that initializes the variables alpha, l, and r.