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
相关产品推荐
相关产品推荐

