Skip to content
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

Fix quantization in the effect engine (metronome effect) #13636

Merged
merged 19 commits into from
Sep 15, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
27baedd
Skip copy if source is equal the destination the copy is unnecessary …
daschuer Sep 1, 2024
77cdbac
Rename beat_fraction to beat_fraction_buffer_end to clarify that the …
daschuer Sep 1, 2024
92ffa72
Avoid unnecessary seconds round-trip in GroupFeatureState
daschuer Sep 2, 2024
22b7c29
Remove unused include
daschuer Sep 3, 2024
808c09c
GroupFeatureState: std::optional
daschuer Sep 3, 2024
c0cbd6a
Add scratch_rate to GroupFeatureState to allow to quantize effects du…
daschuer Sep 3, 2024
8454cce
Fix quantization with the metronome effect. It was one buffer off.
daschuer Sep 3, 2024
6c67cd8
Introduce m_actual_speed with is what the scalers actually did.
daschuer Sep 6, 2024
885e9ef
Don't snap "beat_next" and "beat_prev" to the nearest beat. This was …
daschuer Sep 6, 2024
8ea3457
Fix the metronome effect during scratching using scratch_rate.
daschuer Sep 6, 2024
c3f084d
Don't use std::optional::value introduced in macOS 10.13
daschuer Sep 9, 2024
e5ff7f3
default GroupFeatureState ctor
daschuer Sep 9, 2024
1fecf39
Improve readability in meronomeeffect.cpp
daschuer Sep 9, 2024
11af751
Fix use of GroupFeatureState in LoudnessContourEffect
daschuer Sep 9, 2024
9293b51
refactor: move metronome sampledata into separate translation unit
Swiftb0y Sep 11, 2024
5e74ec6
Fix metronome without a sync enabled
daschuer Sep 12, 2024
26d3029
Avoid to fade in the metronome effect, because this would mangle the …
daschuer Sep 13, 2024
2081952
Combine frames and scratch_rate into one optional because they are al…
daschuer Sep 13, 2024
f019874
refactor: use higher-level `std::span` based logic
Swiftb0y Sep 11, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/effects/backends/builtin/autopaneffect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,10 @@ void AutoPanEffect::processChannel(
double period = m_pPeriodParameter->value();
const auto smoothing = static_cast<float>(0.5 - m_pSmoothingParameter->value());

if (groupFeatures.has_beat_length_frames) {
if (groupFeatures.beat_length_frames.has_value()) {
// period is a number of beats
double beats = std::max(roundToFraction(period, 2), 0.25);
period = beats * groupFeatures.beat_length_frames;
period = beats * groupFeatures.beat_length_frames.value();

// TODO(xxx) sync phase
//if (groupFeatures.has_beat_fraction) {
Expand Down
4 changes: 2 additions & 2 deletions src/effects/backends/builtin/echoeffect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ void EchoEffect::processChannel(
const auto pingpong_frac = static_cast<CSAMPLE_GAIN>(m_pPingPongParameter->value());

int delay_frames;
if (groupFeatures.has_beat_length_frames) {
if (groupFeatures.beat_length_frames.has_value()) {
// period is a number of beats
if (m_pQuantizeParameter->toBool()) {
period = std::max(roundToFraction(period, 4), 1 / 8.0);
Expand All @@ -142,7 +142,7 @@ void EchoEffect::processChannel(
} else if (period < 1 / 8.0) {
period = 1 / 8.0;
}
delay_frames = static_cast<int>(period * groupFeatures.beat_length_frames);
delay_frames = static_cast<int>(period * groupFeatures.beat_length_frames.value());
} else {
// period is a number of seconds
period = std::max(period, 1 / 8.0);
Expand Down
4 changes: 2 additions & 2 deletions src/effects/backends/builtin/flangereffect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,13 +117,13 @@ void FlangerEffect::processChannel(
const GroupFeatureState& groupFeatures) {
double lfoPeriodParameter = m_pSpeedParameter->value();
double lfoPeriodFrames;
if (groupFeatures.has_beat_length_frames) {
if (groupFeatures.beat_length_frames.has_value()) {
// lfoPeriodParameter is a number of beats
lfoPeriodParameter = std::max(roundToFraction(lfoPeriodParameter, 2.0), kMinLfoBeats);
if (m_pTripletParameter->toBool()) {
lfoPeriodParameter /= 3.0;
}
lfoPeriodFrames = lfoPeriodParameter * groupFeatures.beat_length_frames;
lfoPeriodFrames = lfoPeriodParameter * groupFeatures.beat_length_frames.value();
} else {
// lfoPeriodParameter is a number of seconds
lfoPeriodFrames = std::max(lfoPeriodParameter, kMinLfoBeats) *
Expand Down
4 changes: 2 additions & 2 deletions src/effects/backends/builtin/loudnesscontoureffect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,9 @@ void LoudnessContourEffect::processChannel(
auto gain = static_cast<CSAMPLE_GAIN>(pState->m_oldGain);

if (enableState != EffectEnableState::Disabling) {
bool useGain = m_pUseGain->toBool() && groupFeatures.has_gain;
bool useGain = m_pUseGain->toBool() && groupFeatures.gain.has_value();
double loudness = m_pLoudness->value();
double gainKnob = groupFeatures.gain;
double gainKnob = groupFeatures.gain.value();
Swiftb0y marked this conversation as resolved.
Show resolved Hide resolved

filterGainDb = loudness;

Expand Down
8 changes: 4 additions & 4 deletions src/effects/backends/builtin/metronomeeffect.cpp
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the changes here are really hard to follow because they mix offset and timing calculations with buffer indexing (mixing levels of abstraction) and thus I'm neither confident about the reliability nor safety of the code...

Do you think its feasible to reorganize it a little so its easier to understand?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have tried to fix that by calculate everything in frames. Maybe I was not too successful or comments are missing.
Can you give some hints what can be improved?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will rework it a little and open a PR to your branch ASAP.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please take a look daschuer#97

Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,11 @@ void MetronomeEffect::processChannel(
}

SINT maxFrames;
if (m_pSyncParameter->toBool() && groupFeatures.has_beat_length_frames) {
maxFrames = static_cast<SINT>(groupFeatures.beat_length_frames);
if (groupFeatures.has_beat_fraction) {
if (m_pSyncParameter->toBool() && groupFeatures.beat_length_frames.has_value()) {
maxFrames = static_cast<SINT>(groupFeatures.beat_length_frames.value());
if (groupFeatures.beat_fraction_buffer_end.has_value()) {
const auto currentFrame = static_cast<SINT>(
maxFrames * groupFeatures.beat_fraction_buffer_end);
maxFrames * groupFeatures.beat_fraction_buffer_end.value());
if (maxFrames > clickSize &&
currentFrame > clickSize &&
currentFrame < maxFrames - clickSize &&
Expand Down
4 changes: 2 additions & 2 deletions src/effects/backends/builtin/phasereffect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,13 +132,13 @@ void PhaserEffect::processChannel(

double periodParameter = m_pLFOPeriodParameter->value();
double periodSamples;
if (groupFeatures.has_beat_length_frames) {
if (groupFeatures.beat_length_frames.has_value()) {
// periodParameter is a number of beats
periodParameter = std::max(roundToFraction(periodParameter, 2.0), 1 / 4.0);
if (m_pTripletParameter->toBool()) {
periodParameter /= 3.0;
}
periodSamples = periodParameter * groupFeatures.beat_length_frames;
periodSamples = periodParameter * groupFeatures.beat_length_frames.value();
} else {
// periodParameter is a number of seconds
periodSamples = std::max(periodParameter, 1 / 4.0) * engineParameters.sampleRate();
Expand Down
10 changes: 5 additions & 5 deletions src/effects/backends/builtin/tremoloeffect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,9 @@ void TremoloEffect::processChannel(
bool tripletDisabling = pState->tripletEnabled && !m_pTripletParameter->toBool();

if (enableState == EffectEnableState::Enabling || quantizeEnabling || tripletDisabling) {
if (gf.has_beat_length_frames && gf.has_beat_fraction) {
currentFrame = static_cast<unsigned int>(gf.beat_fraction_buffer_end *
gf.beat_length_frames);
if (gf.beat_length_frames.has_value() && gf.beat_fraction_buffer_end.has_value()) {
currentFrame = static_cast<unsigned int>(gf.beat_fraction_buffer_end.value() *
gf.beat_length_frames.value());
} else {
currentFrame = 0;
}
Expand All @@ -149,7 +149,7 @@ void TremoloEffect::processChannel(

int framePerPeriod;
double rate = m_pRateParameter->value();
if (gf.has_beat_length_frames && gf.has_beat_fraction) {
if (gf.beat_length_frames.has_value() && gf.beat_fraction_buffer_end.has_value()) {
if (m_pQuantizeParameter->toBool()) {
const auto divider = static_cast<int>(log2(rate));
rate = pow(2, divider);
Expand All @@ -158,7 +158,7 @@ void TremoloEffect::processChannel(
rate *= 3.0;
}
}
const auto framePerBeat = static_cast<int>(gf.beat_length_frames);
const auto framePerBeat = static_cast<int>(gf.beat_length_frames.value());
framePerPeriod = static_cast<int>(framePerBeat / rate);
} else {
framePerPeriod = static_cast<int>(engineParameters.sampleRate() / rate);
Expand Down
5 changes: 0 additions & 5 deletions src/engine/controls/bpmcontrol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1109,12 +1109,7 @@ void BpmControl::collectFeatures(GroupFeatureState* pGroupFeatures) const {
const double rateRatio = m_pRateRatio->get();
if (rateRatio != 0.0) {
pGroupFeatures->beat_length_frames = beatLengthFrames / rateRatio;
pGroupFeatures->has_beat_length_frames = true;
} else {
pGroupFeatures->has_beat_length_frames = false;
}

pGroupFeatures->has_beat_fraction = true;
pGroupFeatures->beat_fraction_buffer_end = beatFraction;
}
}
Expand Down
19 changes: 6 additions & 13 deletions src/engine/effects/groupfeaturestate.h
Original file line number Diff line number Diff line change
@@ -1,25 +1,18 @@
#pragma once

#include <optional>

/// GroupFeatureState communicates metadata about EngineChannels to EffectProcessors.
struct GroupFeatureState {
GroupFeatureState()
: has_beat_length_frames(false),
beat_length_frames(0.0),
has_beat_fraction(false),
beat_fraction_buffer_end(0.0),
has_gain(false),
gain(1.0) {
GroupFeatureState() {
}
daschuer marked this conversation as resolved.
Show resolved Hide resolved

bool has_beat_length_frames;
double beat_length_frames;
std::optional<double> beat_length_frames;

// Beat fraction (0.0 to 1.0) of the position at the buffer end.
// Previous beat is in the current or earlier buffer. The next beat
// in the next or later buffer.
bool has_beat_fraction;
double beat_fraction_buffer_end;
std::optional<double> beat_fraction_buffer_end;

bool has_gain;
double gain;
std::optional<double> gain;
};
2 changes: 0 additions & 2 deletions src/engine/enginemixer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -768,7 +768,6 @@ void EngineMixer::process(const int iBufferSize) {
// record/broadcast signal
if (m_pEngineEffectsManager) {
GroupFeatureState mainFeatures;
mainFeatures.has_gain = true;
mainFeatures.gain = m_pMainGain->get();
m_pEngineEffectsManager->processPostFaderInPlace(
m_mainOutputHandle.handle(),
Expand Down Expand Up @@ -832,7 +831,6 @@ void EngineMixer::applyMainEffects(int bufferSize) {
// Apply main effects
if (m_pEngineEffectsManager) {
GroupFeatureState mainFeatures;
mainFeatures.has_gain = true;
mainFeatures.gain = m_pMainGain->get();
m_pEngineEffectsManager->processPostFaderInPlace(m_mainHandle.handle(),
m_mainHandle.handle(),
Expand Down
1 change: 0 additions & 1 deletion src/engine/enginepregain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,5 +164,4 @@ void EnginePregain::process(CSAMPLE* pInOut, const int iBufferSize) {

void EnginePregain::collectFeatures(GroupFeatureState* pGroupFeatures) const {
pGroupFeatures->gain = m_pPotmeterPregain->get();
pGroupFeatures->has_gain = true;
}