-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow effect to continue processing if its buffer isn't empty (builtin reverb/delay) #14099
base: main
Are you sure you want to change the base?
Changes from all commits
ac70a9a
8fe86ec
b3938ea
c8e5222
38f1858
e3cd6bb
29f5317
c1ba962
2153a91
9f2fd6f
a9a679c
1920ffc
29b6fa2
aaf6d09
3654121
ebba883
3f1f385
fe00dbf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
@@ -1,5 +1,7 @@ | ||||
#include "effects/backends/builtin/echoeffect.h" | ||||
|
||||
#include <span> | ||||
|
||||
#include "effects/backends/effectmanifest.h" | ||||
#include "engine/effects/engineeffectparameter.h" | ||||
#include "util/math.h" | ||||
|
@@ -118,6 +120,14 @@ | |||
m_pTripletParameter = parameters.value("triplet"); | ||||
} | ||||
|
||||
float averageSampleEnergy(std::span<const CSAMPLE> delay_buffer) { | ||||
float differenceSum = 0.0f; | ||||
for (const CSAMPLE sample : delay_buffer.span()) { | ||||
Check failure on line 125 in src/effects/backends/builtin/echoeffect.cpp
|
||||
differenceSum += std::abs(sample); | ||||
} | ||||
return differenceSum / delay_buffer.size(); | ||||
} | ||||
|
||||
void EchoEffect::processChannel( | ||||
EchoGroupState* pGroupState, | ||||
const CSAMPLE* pInput, | ||||
|
@@ -127,10 +137,16 @@ | |||
const GroupFeatureState& groupFeatures) { | ||||
// The minimum of the parameter is zero so the exact center of the knob is 1 beat. | ||||
double period = m_pDelayParameter->value(); | ||||
const auto send_current = static_cast<CSAMPLE_GAIN>(m_pSendParameter->value()); | ||||
const auto send_current = enableState == EffectEnableState::Disabling | ||||
? 0 | ||||
: static_cast<CSAMPLE_GAIN>(m_pSendParameter->value()); | ||||
const auto feedback_current = static_cast<CSAMPLE_GAIN>(m_pFeedbackParameter->value()); | ||||
const auto pingpong_frac = static_cast<CSAMPLE_GAIN>(m_pPingPongParameter->value()); | ||||
|
||||
if (enableState == EffectEnableState::Enabling) { | ||||
m_isReadyForDisable = false; | ||||
} | ||||
|
||||
int delay_frames; | ||||
if (groupFeatures.beat_length.has_value()) { | ||||
// period is a number of beats | ||||
|
@@ -236,16 +252,17 @@ | |||
pGroupState->ping_pong = 0; | ||||
} | ||||
} | ||||
|
||||
// The ramping of the send parameter handles ramping when enabling, so | ||||
// this effect must handle ramping to dry when disabling itself (instead | ||||
// of being handled by EngineEffect::process). | ||||
pGroupState->prev_send = send_current; | ||||
if (enableState == EffectEnableState::Disabling) { | ||||
SampleUtil::applyRampingGain(pOutput, 1.0, 0.0, engineParameters.samplesPerBuffer()); | ||||
pGroupState->delay_buf.clear(); | ||||
pGroupState->prev_send = 0; | ||||
} else { | ||||
pGroupState->prev_send = send_current; | ||||
const SINT delayBufferSize = pGroupState->delay_buf.size(); | ||||
// Calculate if the delayline-buffer is approx. zero/empty. | ||||
const float avgSampleEnergy = averageSampleEnergy(pGroupState->delay_buf.span().first(engineParameters.sampleRate())); | ||||
// If echo tail fully faded | ||||
if (avgSampleEnergy < (0.00001f / delayBufferSize)) { | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why does the energy depend on the buffer size? The average shouldn't be influenced by the sample size of the dataset. If the concern is that the fading tail influences the current average, simply don't sample it. Only sample as much as you need: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The average sample energy depends on the buffer-size since it is computed from the buffer (average over buffer length, the average over the next buffer as well, or only half the buffer, would be numerically different). Thus, Before your previous review, the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Well, now that we're only looking at the samples at the beginning of the delay buffer (exactly the samples that feedback into the effect to be exact) the size shouldn't anymore, right? Am I making sense here? Besides I think There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mhm I am not sure I follow sorry, I think the average is calculated over the whole delay buffer. But we can in any case remove the division by the delay buffer length in the treshhold check. This is just numerics:--) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like the solution is not indepenedent of single clicks vs pink noise. If we assume that the last added sample was above the threshold, we can alternatively just consume the number samples according to the current picked delay value. Not sure if the earlay disabling has benefits. Does it? Did you consicder to loop through the whole buffer and continue once you have found a single semale above a certain thresshold? Maybe copy/reuse the approch that we took here: mixxx/src/analyzer/analyzersilence.cpp Line 27 in ae2dc10
|
||||
m_isReadyForDisable = true; | ||||
pGroupState->delay_buf.clear(); | ||||
} | ||||
} | ||||
|
||||
pGroupState->prev_feedback = feedback_current; | ||||
|
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -77,6 +77,24 @@ void ReverbEffect::loadEngineEffectParameters( | |||
m_pSendParameter = parameters.value("send_amount"); | ||||
} | ||||
|
||||
float averageSampleDifferenceEnergy(const SINT samplesPerBuffer, | ||||
const CSAMPLE* buffer_in, | ||||
const CSAMPLE* buffer_out, | ||||
const SINT tailCheckLength) { | ||||
if (tailCheckLength == 0) { | ||||
return 0; | ||||
} | ||||
float differenceSum = 0.0f; | ||||
for (SINT i = samplesPerBuffer - tailCheckLength; | ||||
i < samplesPerBuffer; | ||||
++i) { | ||||
differenceSum += std::abs(buffer_out[i] - buffer_in[i]); | ||||
} | ||||
// Calculate average of the differences | ||||
const float averageDifference = differenceSum / tailCheckLength; | ||||
return averageDifference; | ||||
} | ||||
|
||||
void ReverbEffect::processChannel( | ||||
ReverbGroupState* pState, | ||||
const CSAMPLE* pInput, | ||||
|
@@ -89,7 +107,9 @@ void ReverbEffect::processChannel( | |||
const auto decay = static_cast<sample_t>(m_pDecayParameter->value()); | ||||
const auto bandwidth = static_cast<sample_t>(m_pBandWidthParameter->value()); | ||||
const auto damping = static_cast<sample_t>(m_pDampingParameter->value()); | ||||
const auto sendCurrent = static_cast<sample_t>(m_pSendParameter->value()); | ||||
const auto sendCurrent = enableState == EffectEnableState::Disabling | ||||
? 0 | ||||
: static_cast<sample_t>(m_pSendParameter->value()); | ||||
|
||||
// Reinitialize the effect when turning it on to prevent replaying the old buffer | ||||
// from the last time the effect was enabled. | ||||
|
@@ -98,6 +118,7 @@ void ReverbEffect::processChannel( | |||
pState->sampleRate != engineParameters.sampleRate()) { | ||||
pState->reverb.init(engineParameters.sampleRate()); | ||||
pState->sampleRate = engineParameters.sampleRate(); | ||||
m_isReadyForDisable = false; | ||||
} | ||||
|
||||
pState->reverb.processBuffer(pInput, | ||||
|
@@ -109,13 +130,16 @@ void ReverbEffect::processChannel( | |||
sendCurrent, | ||||
pState->sendPrevious); | ||||
|
||||
// The ramping of the send parameter handles ramping when enabling, so | ||||
// this effect must handle ramping to dry when disabling itself (instead | ||||
// of being handled by EngineEffect::process). | ||||
if (enableState == EffectEnableState::Disabling) { | ||||
SampleUtil::applyRampingGain(pOutput, 1.0, 0.0, engineParameters.samplesPerBuffer()); | ||||
pState->sendPrevious = 0; | ||||
} else { | ||||
pState->sendPrevious = sendCurrent; | ||||
// Calculate absolute difference between wet and dry buffers for the tail | ||||
const SINT tailCheckLength = engineParameters.samplesPerBuffer() / 4; | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why you /4 ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have here two delay filters per channel: Line 216 in ae2dc10
Initalized with 2^6 and 2^8 buffer size. This means the maximum delay is 256 frames. The issue here in addition is that you are checking the current buffer and not was has added to the reverb. EffectEnableState::Disabling means that no new samples are added, right? |
||||
const float averageDifference = averageSampleDifferenceEnergy( | ||||
engineParameters.samplesPerBuffer(), | ||||
pInput, | ||||
pOutput, | ||||
tailCheckLength); | ||||
if (averageDifference < 0.002f) { | ||||
m_isReadyForDisable = true; | ||||
} | ||||
} | ||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Energy is voltage * current. Here you use only voltage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We collect here such sample functions that are reusable together with a hint that indicates if the function is auto vectorized.
This is probably already the case here, but worth to double check.
mixxx/src/util/sample.cpp
Line 463 in ae2dc10