You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JUCE乒乓延迟插件设为0延迟仍有延迟的解决求助

乒乓延迟插件延迟时间设为0时仍有延迟的问题排查

问题描述

我正在使用JUCE开发一款乒乓延迟插件,当延迟时间设为0时仍存在延迟,但设置为非0值时功能完全正常。尝试过调整延迟循环顺序、关闭参数平滑、用调试器确认popSample的输入变量,但均未找到问题所在。

现有代码

void AAPDelayCWAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages)
{
    juce::ScopedNoDenormals noDenormals;
    auto totalNumInputChannels  = getTotalNumInputChannels();
    auto totalNumOutputChannels = getTotalNumOutputChannels();

    for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
        buffer.clear (i, 0, buffer.getNumSamples());

        // Create dry audio block
        juce::dsp::AudioBlock<float> dryBlock(buffer);

        // Push dry samples to dryWetMixer
        dryWetMixer.pushDrySamples(dryBlock);

        // Set mix value
        dryWetMixer.setWetMixProportion(mDryWet);

        // Access left and right channels seperately
        float* channelDataLEFT = buffer.getWritePointer(0);
        float* channelDataRIGHT = buffer.getWritePointer(1);

        
        for (int i = 0; i < buffer.getNumSamples(); i++)
        {
            // Smoothed Delay value
            float currentDelayTimeSamples = smoothedDelayTime.getNextValue();
            
            float tempLeft = mDelayLine.popSample(0, currentDelayTimeSamples);     
            float inLeft = channelDataLEFT[i];                                      

            float tempRight = mDelayLine.popSample(1, currentDelayTimeSamples);     
            float inRight = channelDataRIGHT[i];                                    

            mDelayLine.pushSample(0, inLeft + (tempRight * mFeedback));             
            mDelayLine.pushSample(1, inRight + (tempLeft * mFeedback));             

            channelDataLEFT[i] = tempRight;                                            
            channelDataRIGHT[i] = tempLeft;                                          

        }

        // Pass wet samples from buffer 
        dryWetMixer.mixWetSamples(buffer);
}

问题分析

当延迟时间设为0时,JUCE的DelayLine::popSample(0)并不会返回当前刚推入的采样,而是返回上一个处理周期推入的旧采样——这是因为延迟线的内部缓存机制,导致输出比输入晚了一个缓冲区的时间,也就是你感受到的延迟。

另外,原代码中直接从正在修改的缓冲区读取输入信号,后续写入操作可能会干扰输入读取,乒乓延迟的交叉反馈逻辑会放大这个问题。

修复代码

void AAPDelayCWAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages)
{
    juce::ScopedNoDenormals noDenormals;
    auto totalNumInputChannels  = getTotalNumInputChannels();
    auto totalNumOutputChannels = getTotalNumOutputChannels();

    // 清空多余输出通道
    for (auto i = totalNumInputChannels; i < totalNumOutputChannels; ++i)
        buffer.clear (i, 0, buffer.getNumSamples());

    // 备份原始干信号,避免后续修改缓冲区影响干湿混合
    juce::AudioBuffer<float> dryBuffer(buffer);
    juce::dsp::AudioBlock<float> dryBlock(dryBuffer);
    dryWetMixer.pushDrySamples(dryBlock);
    dryWetMixer.setWetMixProportion(mDryWet);

    float* leftOut = buffer.getWritePointer(0);
    float* rightOut = buffer.getWritePointer(1);
    const float* leftIn = dryBuffer.getReadPointer(0);
    const float* rightIn = dryBuffer.getReadPointer(1);

    for (int i = 0; i < buffer.getNumSamples(); ++i)
    {
        float currentDelay = smoothedDelayTime.getNextValue();
        float tempLeft, tempRight;

        if (currentDelay <= 0.0f)
        {
            // 延迟为0时直接交叉输出,不读取延迟线旧采样
            tempLeft = rightIn[i];
            tempRight = leftIn[i];
            // 仍需推入信号维持反馈链
            mDelayLine.pushSample(0, leftIn[i] + tempRight * mFeedback);
            mDelayLine.pushSample(1, rightIn[i] + tempLeft * mFeedback);
        }
        else
        {
            // 正常延迟逻辑:先取延迟信号,再推新信号
            tempLeft = mDelayLine.popSample(0, currentDelay);
            tempRight = mDelayLine.popSample(1, currentDelay);
            
            mDelayLine.pushSample(0, leftIn[i] + tempRight * mFeedback);
            mDelayLine.pushSample(1, rightIn[i] + tempLeft * mFeedback);
        }

        leftOut[i] = tempLeft;
        rightOut[i] = tempRight;
    }

    dryWetMixer.mixWetSamples(buffer);
}

关键修改说明

  • 备份干信号:用dryBuffer保存原始输入,确保干湿混合的干信号不受后续湿信号修改的影响。
  • 延迟0特殊处理:直接交叉输出左右声道,跳过延迟线的旧采样读取,解决缓冲区级别的延迟问题。
  • 分离输入输出指针:用const指针读取原始输入,避免读写冲突。
  • 维持反馈链:即使延迟为0,仍向延迟线推入信号,保证切换到非0延迟时反馈逻辑能正常衔接。

额外建议:检查smoothedDelayTime的平滑速率,确保延迟值能快速收敛到0,避免平滑过程中残留的小延迟值导致的过渡问题。

内容的提问来源于stack exchange,提问作者louism9

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 10:03:25