From a4707920672967543c8d762310280ac531402385 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sat, 14 Oct 2017 14:49:24 +0200 Subject: [PATCH 01/31] Rename Depth to Mix and added the standard flanger parameters without function. --- src/effects/native/flangereffect.cpp | 101 ++++++++++++++++++--------- src/effects/native/flangereffect.h | 7 +- 2 files changed, 74 insertions(+), 34 deletions(-) diff --git a/src/effects/native/flangereffect.cpp b/src/effects/native/flangereffect.cpp index 11d19eaace1f..3c542ec747c2 100644 --- a/src/effects/native/flangereffect.cpp +++ b/src/effects/native/flangereffect.cpp @@ -24,30 +24,64 @@ EffectManifest FlangerEffect::getManifest() { "A simple modulation effect, created by taking the input signal " "and mixing it with a delayed, pitch modulated copy of itself.")); - EffectManifestParameter* period = manifest.addParameter(); - period->setId("period"); - period->setName(QObject::tr("Period")); - period->setDescription(QObject::tr("Controls the period of the LFO (low frequency oscillator)\n" + EffectManifestParameter* speed = manifest.addParameter(); + speed->setId("speed"); + speed->setName(QObject::tr("Speed")); + speed->setDescription(QObject::tr("Controls the speed of the LFO (low frequency oscillator)\n" "1/4 - 4 beats rounded to 1/2 beat if tempo is detected (decks and samplers) \n" "0.05 - 4 seconds if no tempo is detected (mic & aux inputs, master mix)")); - period->setControlHint(EffectManifestParameter::ControlHint::KNOB_LINEAR); - period->setSemanticHint(EffectManifestParameter::SemanticHint::UNKNOWN); - period->setUnitsHint(EffectManifestParameter::UnitsHint::BEATS); - period->setMinimum(0.00); - period->setMaximum(4.00); - period->setDefault(1.00); - - EffectManifestParameter* depth = manifest.addParameter(); - depth->setId("depth"); - depth->setName(QObject::tr("Depth")); - depth->setDescription(QObject::tr("Controls the intensity of the effect.")); - depth->setControlHint(EffectManifestParameter::ControlHint::KNOB_LINEAR); - depth->setSemanticHint(EffectManifestParameter::SemanticHint::UNKNOWN); - depth->setUnitsHint(EffectManifestParameter::UnitsHint::UNKNOWN); - depth->setDefaultLinkType(EffectManifestParameter::LinkType::LINKED); - depth->setDefault(1.0); - depth->setMinimum(0.0); - depth->setMaximum(1.0); + speed->setControlHint(EffectManifestParameter::ControlHint::KNOB_LINEAR); + speed->setSemanticHint(EffectManifestParameter::SemanticHint::UNKNOWN); + speed->setUnitsHint(EffectManifestParameter::UnitsHint::BEATS); + speed->setMinimum(0.00); + speed->setMaximum(4.00); + speed->setDefault(1.00); + + EffectManifestParameter* width = manifest.addParameter(); + width->setId("width"); + width->setName(QObject::tr("Width")); + width->setDescription(QObject::tr("Controls the delay amplitude of the LFO (low frequency oscillator).")); + width->setControlHint(EffectManifestParameter::ControlHint::KNOB_LINEAR); + width->setSemanticHint(EffectManifestParameter::SemanticHint::UNKNOWN); + width->setUnitsHint(EffectManifestParameter::UnitsHint::UNKNOWN); + width->setDefault(1.0); + width->setMinimum(0.0); + width->setMaximum(1.0); + + EffectManifestParameter* manual = manifest.addParameter(); + manual->setId("manual"); + manual->setName(QObject::tr("Manual")); + manual->setDescription(QObject::tr("Controls the delay offset of the LFO (low frequency oscillator).\n" + "With width at zero, it allows to manual sweep over the entire delay range.")); + manual->setControlHint(EffectManifestParameter::ControlHint::KNOB_LINEAR); + manual->setSemanticHint(EffectManifestParameter::SemanticHint::UNKNOWN); + manual->setUnitsHint(EffectManifestParameter::UnitsHint::UNKNOWN); + manual->setDefaultLinkType(EffectManifestParameter::LinkType::LINKED); + manual->setDefault(1.0); + manual->setMinimum(0.0); + manual->setMaximum(1.0); + + EffectManifestParameter* regen = manifest.addParameter(); + regen->setId("regen"); + regen->setName(QObject::tr("Regen.")); + regen->setDescription(QObject::tr("Controls how much of the delay output is feed back into the input.")); + regen->setControlHint(EffectManifestParameter::ControlHint::KNOB_LINEAR); + regen->setSemanticHint(EffectManifestParameter::SemanticHint::UNKNOWN); + regen->setUnitsHint(EffectManifestParameter::UnitsHint::UNKNOWN); + regen->setDefault(1.0); + regen->setMinimum(0.0); + regen->setMaximum(1.0); + + EffectManifestParameter* mix = manifest.addParameter(); + mix->setId("mix"); + mix->setName(QObject::tr("Mix")); + mix->setDescription(QObject::tr("Controls the intensity of the effect.")); + mix->setControlHint(EffectManifestParameter::ControlHint::KNOB_LINEAR); + mix->setSemanticHint(EffectManifestParameter::SemanticHint::UNKNOWN); + mix->setUnitsHint(EffectManifestParameter::UnitsHint::UNKNOWN); + mix->setDefault(1.0); + mix->setMinimum(0.0); + mix->setMaximum(1.0); EffectManifestParameter* triplet = manifest.addParameter(); triplet->setId("triplet"); @@ -65,8 +99,11 @@ EffectManifest FlangerEffect::getManifest() { FlangerEffect::FlangerEffect(EngineEffect* pEffect, const EffectManifest& manifest) - : m_pPeriodParameter(pEffect->getParameterById("period")), - m_pDepthParameter(pEffect->getParameterById("depth")), + : m_pSpeedParameter(pEffect->getParameterById("speed")), + m_pWidthParameter(pEffect->getParameterById("width")), + m_pManualParameter(pEffect->getParameterById("manual")), + m_pRegenParameter(pEffect->getParameterById("regen")), + m_pMixParameter(pEffect->getParameterById("mix")), m_pTripletParameter(pEffect->getParameterById("triplet")) { Q_UNUSED(manifest); } @@ -88,24 +125,24 @@ void FlangerEffect::processChannel(const ChannelHandle& handle, const int kChannels = 2; // The parameter minimum is zero so the exact center of the knob is 2 beats. - double lfoPeriodParameter = m_pPeriodParameter->value(); + double lfoSpeedParameter = m_pSpeedParameter->value(); double lfoPeriodSamples; if (groupFeatures.has_beat_length_sec) { // lfoPeriodParameter is a number of beats - lfoPeriodParameter = std::max(roundToFraction(lfoPeriodParameter, 2.0), 1/4.0); + lfoSpeedParameter = std::max(roundToFraction(lfoSpeedParameter, 2.0), 1/4.0); if (m_pTripletParameter->toBool()) { - lfoPeriodParameter /= 3.0; + lfoSpeedParameter /= 3.0; } - lfoPeriodSamples = lfoPeriodParameter * groupFeatures.beat_length_sec * sampleRate; + lfoPeriodSamples = lfoSpeedParameter * groupFeatures.beat_length_sec * sampleRate; } else { // lfoPeriodParameter is a number of seconds - lfoPeriodSamples = std::max(lfoPeriodParameter, 1/4.0) * sampleRate; + lfoPeriodSamples = std::max(lfoSpeedParameter, 1/4.0) * sampleRate; } // lfoPeriodSamples is used to calculate the delay for each channel // independently in the loop below, so do not multiply lfoPeriodSamples by // the number of channels. - CSAMPLE lfoDepth = m_pDepthParameter->value(); + CSAMPLE mix = m_pMixParameter->value(); CSAMPLE* delayLeft = pState->delayLeft; CSAMPLE* delayRight = pState->delayRight; @@ -136,8 +173,8 @@ void FlangerEffect::processChannel(const ChannelHandle& handle, CSAMPLE delayedSampleLeft = prevLeft + frac * (nextLeft - prevLeft); CSAMPLE delayedSampleRight = prevRight + frac * (nextRight - prevRight); - pOutput[i] = pInput[i] + lfoDepth * delayedSampleLeft; - pOutput[i+1] = pInput[i+1] + lfoDepth * delayedSampleRight; + pOutput[i] = pInput[i] + mix * delayedSampleLeft; + pOutput[i+1] = pInput[i+1] + mix * delayedSampleRight; } if (enableState == EffectProcessor::DISABLING) { diff --git a/src/effects/native/flangereffect.h b/src/effects/native/flangereffect.h index f9c626f8cb1a..c06c41c1f5c6 100644 --- a/src/effects/native/flangereffect.h +++ b/src/effects/native/flangereffect.h @@ -46,8 +46,11 @@ class FlangerEffect : public PerChannelEffectProcessor { return getId(); } - EngineEffectParameter* m_pPeriodParameter; - EngineEffectParameter* m_pDepthParameter; + EngineEffectParameter* m_pSpeedParameter; + EngineEffectParameter* m_pWidthParameter; + EngineEffectParameter* m_pManualParameter; + EngineEffectParameter* m_pRegenParameter; + EngineEffectParameter* m_pMixParameter; EngineEffectParameter* m_pTripletParameter; DISALLOW_COPY_AND_ASSIGN(FlangerEffect); From 7303e59044ab924d8863e6cef9fe8a448dec6543 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sat, 14 Oct 2017 22:58:36 +0200 Subject: [PATCH 02/31] Make flanger sample rate independent --- src/effects/native/flangereffect.cpp | 42 ++++++++++++++-------------- src/effects/native/flangereffect.h | 19 +++++++++---- 2 files changed, 34 insertions(+), 27 deletions(-) diff --git a/src/effects/native/flangereffect.cpp b/src/effects/native/flangereffect.cpp index 3c542ec747c2..cf753f9403ed 100644 --- a/src/effects/native/flangereffect.cpp +++ b/src/effects/native/flangereffect.cpp @@ -4,10 +4,6 @@ #include "util/math.h" -const unsigned int kMaxDelay = 5000; -const unsigned int kLfoAmplitude = 240; -const unsigned int kAverageDelayLength = 250; - // static QString FlangerEffect::getId() { return "org.mixxx.effects.flanger"; @@ -44,9 +40,9 @@ EffectManifest FlangerEffect::getManifest() { width->setControlHint(EffectManifestParameter::ControlHint::KNOB_LINEAR); width->setSemanticHint(EffectManifestParameter::SemanticHint::UNKNOWN); width->setUnitsHint(EffectManifestParameter::UnitsHint::UNKNOWN); - width->setDefault(1.0); + width->setDefault(kMaxLfoWidthMs); width->setMinimum(0.0); - width->setMaximum(1.0); + width->setMaximum(kMaxLfoWidthMs); EffectManifestParameter* manual = manifest.addParameter(); manual->setId("manual"); @@ -57,9 +53,9 @@ EffectManifest FlangerEffect::getManifest() { manual->setSemanticHint(EffectManifestParameter::SemanticHint::UNKNOWN); manual->setUnitsHint(EffectManifestParameter::UnitsHint::UNKNOWN); manual->setDefaultLinkType(EffectManifestParameter::LinkType::LINKED); - manual->setDefault(1.0); - manual->setMinimum(0.0); - manual->setMaximum(1.0); + manual->setDefault((kMaxDelayMs - kMinDelayMs) / 2 + kMinDelayMs); + manual->setMinimum(kMinDelayMs); + manual->setMaximum(kMaxDelayMs); EffectManifestParameter* regen = manifest.addParameter(); regen->setId("regen"); @@ -126,17 +122,17 @@ void FlangerEffect::processChannel(const ChannelHandle& handle, // The parameter minimum is zero so the exact center of the knob is 2 beats. double lfoSpeedParameter = m_pSpeedParameter->value(); - double lfoPeriodSamples; + double lfoPeriodFrames; if (groupFeatures.has_beat_length_sec) { // lfoPeriodParameter is a number of beats lfoSpeedParameter = std::max(roundToFraction(lfoSpeedParameter, 2.0), 1/4.0); if (m_pTripletParameter->toBool()) { lfoSpeedParameter /= 3.0; } - lfoPeriodSamples = lfoSpeedParameter * groupFeatures.beat_length_sec * sampleRate; + lfoPeriodFrames = lfoSpeedParameter * groupFeatures.beat_length_sec * sampleRate; } else { // lfoPeriodParameter is a number of seconds - lfoPeriodSamples = std::max(lfoSpeedParameter, 1/4.0) * sampleRate; + lfoPeriodFrames = std::max(lfoSpeedParameter, 1/4.0) * sampleRate; } // lfoPeriodSamples is used to calculate the delay for each channel // independently in the loop below, so do not multiply lfoPeriodSamples by @@ -151,25 +147,29 @@ void FlangerEffect::processChannel(const ChannelHandle& handle, delayLeft[pState->delayPos] = pInput[i]; delayRight[pState->delayPos] = pInput[i+1]; - pState->delayPos = (pState->delayPos + 1) % kMaxDelay; + pState->delayPos = (pState->delayPos + 1) % kBufferLenth; - pState->time++; - if (pState->time > lfoPeriodSamples) { - pState->time = 0; + pState->lfoFrames++; + if (pState->lfoFrames >= lfoPeriodFrames) { + pState->lfoFrames = 0; } - CSAMPLE periodFraction = CSAMPLE(pState->time) / lfoPeriodSamples; - CSAMPLE delay = kAverageDelayLength + kLfoAmplitude * sin(M_PI * 2.0f * periodFraction); + float periodFraction = static_cast(pState->lfoFrames) / lfoPeriodFrames; + double delayMs = ((kMaxDelayMs - kMinDelayMs) / 2 + kMinDelayMs) + + ((kMaxDelayMs - kMinDelayMs) / 2) * sin(M_PI * 2.0f * periodFraction); + double delayFrames = delayMs * sampleRate / 1000; - int framePrev = (pState->delayPos - int(delay) + kMaxDelay - 1) % kMaxDelay; - int frameNext = (pState->delayPos - int(delay) + kMaxDelay ) % kMaxDelay; + SINT framePrev = (pState->delayPos - static_cast(floor(delayFrames)) + + kBufferLenth) % kBufferLenth; + SINT frameNext = (pState->delayPos - static_cast(ceil(delayFrames)) + + kBufferLenth) % kBufferLenth; CSAMPLE prevLeft = delayLeft[framePrev]; CSAMPLE nextLeft = delayLeft[frameNext]; CSAMPLE prevRight = delayRight[framePrev]; CSAMPLE nextRight = delayRight[frameNext]; - CSAMPLE frac = delay - floorf(delay); + CSAMPLE frac = delayFrames - floorf(delayFrames); CSAMPLE delayedSampleLeft = prevLeft + frac * (nextLeft - prevLeft); CSAMPLE delayedSampleRight = prevRight + frac * (nextRight - prevRight); diff --git a/src/effects/native/flangereffect.h b/src/effects/native/flangereffect.h index c06c41c1f5c6..1a977cd8b957 100644 --- a/src/effects/native/flangereffect.h +++ b/src/effects/native/flangereffect.h @@ -11,17 +11,24 @@ #include "util/sample.h" #include "util/types.h" +namespace { +constexpr double kMaxDelayMs = 14.0; +constexpr double kMinDelayMs = 3.0; +constexpr double kMaxLfoWidthMs = kMaxDelayMs - kMinDelayMs; +constexpr SINT kBufferLenth = static_cast(ceil(kMaxDelayMs)) * 96; // for 96 kHz +} // anonymous namespace + struct FlangerGroupState { FlangerGroupState() : delayPos(0), - time(0) { - SampleUtil::applyGain(delayLeft, 0, MAX_BUFFER_LEN); - SampleUtil::applyGain(delayRight, 0, MAX_BUFFER_LEN); + lfoFrames(0) { + SampleUtil::applyGain(delayLeft, 0, kBufferLenth); + SampleUtil::applyGain(delayRight, 0, kBufferLenth); } - CSAMPLE delayRight[MAX_BUFFER_LEN]; - CSAMPLE delayLeft[MAX_BUFFER_LEN]; + CSAMPLE delayRight[kBufferLenth]; + CSAMPLE delayLeft[kBufferLenth]; unsigned int delayPos; - unsigned int time; + unsigned int lfoFrames; }; class FlangerEffect : public PerChannelEffectProcessor { From bc061539af87f8218f2fc3481a109f250e52da79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sat, 14 Oct 2017 23:56:25 +0200 Subject: [PATCH 03/31] Enable width and manual parameter --- src/effects/native/flangereffect.cpp | 16 ++++++++++++---- src/effects/native/flangereffect.h | 1 + 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/effects/native/flangereffect.cpp b/src/effects/native/flangereffect.cpp index cf753f9403ed..6c735e8da8da 100644 --- a/src/effects/native/flangereffect.cpp +++ b/src/effects/native/flangereffect.cpp @@ -53,7 +53,7 @@ EffectManifest FlangerEffect::getManifest() { manual->setSemanticHint(EffectManifestParameter::SemanticHint::UNKNOWN); manual->setUnitsHint(EffectManifestParameter::UnitsHint::UNKNOWN); manual->setDefaultLinkType(EffectManifestParameter::LinkType::LINKED); - manual->setDefault((kMaxDelayMs - kMinDelayMs) / 2 + kMinDelayMs); + manual->setDefault(kCenterDelayMs); manual->setMinimum(kMinDelayMs); manual->setMaximum(kMaxDelayMs); @@ -138,7 +138,16 @@ void FlangerEffect::processChannel(const ChannelHandle& handle, // independently in the loop below, so do not multiply lfoPeriodSamples by // the number of channels. - CSAMPLE mix = m_pMixParameter->value(); + CSAMPLE_GAIN mix = m_pMixParameter->value(); + CSAMPLE_GAIN regen = m_pRegenParameter->value(); + + // With and Manual is limited by amount of amplitude that remains from width + // to kMaxDelaMS + double width = m_pWidthParameter->value(); + double manual = m_pManualParameter->value(); + double maxManual = kCenterDelayMs + (kMaxLfoWidthMs - width) / 2; + double minManual = kCenterDelayMs - (kMaxLfoWidthMs - width) / 2; + manual = math_clamp(manual, minManual, maxManual); CSAMPLE* delayLeft = pState->delayLeft; CSAMPLE* delayRight = pState->delayRight; @@ -155,8 +164,7 @@ void FlangerEffect::processChannel(const ChannelHandle& handle, } float periodFraction = static_cast(pState->lfoFrames) / lfoPeriodFrames; - double delayMs = ((kMaxDelayMs - kMinDelayMs) / 2 + kMinDelayMs) - + ((kMaxDelayMs - kMinDelayMs) / 2) * sin(M_PI * 2.0f * periodFraction); + double delayMs = manual + width / 2 * sin(M_PI * 2.0f * periodFraction); double delayFrames = delayMs * sampleRate / 1000; SINT framePrev = (pState->delayPos - static_cast(floor(delayFrames)) diff --git a/src/effects/native/flangereffect.h b/src/effects/native/flangereffect.h index 1a977cd8b957..0539fc6fdf3f 100644 --- a/src/effects/native/flangereffect.h +++ b/src/effects/native/flangereffect.h @@ -14,6 +14,7 @@ namespace { constexpr double kMaxDelayMs = 14.0; constexpr double kMinDelayMs = 3.0; +constexpr double kCenterDelayMs = (kMaxDelayMs - kMinDelayMs) / 2 + kMinDelayMs; constexpr double kMaxLfoWidthMs = kMaxDelayMs - kMinDelayMs; constexpr SINT kBufferLenth = static_cast(ceil(kMaxDelayMs)) * 96; // for 96 kHz } // anonymous namespace From daeecad4520e20d22e99eb8dd17149ea5a97082f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sun, 15 Oct 2017 00:26:50 +0200 Subject: [PATCH 04/31] enable regen parameter and added a tanh clipper --- src/effects/native/flangereffect.cpp | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/effects/native/flangereffect.cpp b/src/effects/native/flangereffect.cpp index 6c735e8da8da..d707956b29bd 100644 --- a/src/effects/native/flangereffect.cpp +++ b/src/effects/native/flangereffect.cpp @@ -4,6 +4,13 @@ #include "util/math.h" +namespace{ +inline CSAMPLE tanh_approx(CSAMPLE input) { + // return tanhf(input); // 142ns for process; + return input / (1 + input * input / (3 + input * input / 5)); // 119ns for process +} +} + // static QString FlangerEffect::getId() { return "org.mixxx.effects.flanger"; @@ -40,6 +47,7 @@ EffectManifest FlangerEffect::getManifest() { width->setControlHint(EffectManifestParameter::ControlHint::KNOB_LINEAR); width->setSemanticHint(EffectManifestParameter::SemanticHint::UNKNOWN); width->setUnitsHint(EffectManifestParameter::UnitsHint::UNKNOWN); + width->setDefaultLinkType(EffectManifestParameter::LinkType::LINKED); width->setDefault(kMaxLfoWidthMs); width->setMinimum(0.0); width->setMaximum(kMaxLfoWidthMs); @@ -52,7 +60,6 @@ EffectManifest FlangerEffect::getManifest() { manual->setControlHint(EffectManifestParameter::ControlHint::KNOB_LINEAR); manual->setSemanticHint(EffectManifestParameter::SemanticHint::UNKNOWN); manual->setUnitsHint(EffectManifestParameter::UnitsHint::UNKNOWN); - manual->setDefaultLinkType(EffectManifestParameter::LinkType::LINKED); manual->setDefault(kCenterDelayMs); manual->setMinimum(kMinDelayMs); manual->setMaximum(kMaxDelayMs); @@ -64,7 +71,7 @@ EffectManifest FlangerEffect::getManifest() { regen->setControlHint(EffectManifestParameter::ControlHint::KNOB_LINEAR); regen->setSemanticHint(EffectManifestParameter::SemanticHint::UNKNOWN); regen->setUnitsHint(EffectManifestParameter::UnitsHint::UNKNOWN); - regen->setDefault(1.0); + regen->setDefault(0.0); regen->setMinimum(0.0); regen->setMaximum(1.0); @@ -153,10 +160,6 @@ void FlangerEffect::processChannel(const ChannelHandle& handle, CSAMPLE* delayRight = pState->delayRight; for (unsigned int i = 0; i < numSamples; i += kChannels) { - delayLeft[pState->delayPos] = pInput[i]; - delayRight[pState->delayPos] = pInput[i+1]; - - pState->delayPos = (pState->delayPos + 1) % kBufferLenth; pState->lfoFrames++; if (pState->lfoFrames >= lfoPeriodFrames) { @@ -181,8 +184,13 @@ void FlangerEffect::processChannel(const ChannelHandle& handle, CSAMPLE delayedSampleLeft = prevLeft + frac * (nextLeft - prevLeft); CSAMPLE delayedSampleRight = prevRight + frac * (nextRight - prevRight); + delayLeft[pState->delayPos] = tanh_approx(pInput[i] + regen * delayedSampleLeft); + delayRight[pState->delayPos] = tanh_approx(pInput[i + 1] + regen * delayedSampleRight); + + pState->delayPos = (pState->delayPos + 1) % kBufferLenth; + pOutput[i] = pInput[i] + mix * delayedSampleLeft; - pOutput[i+1] = pInput[i+1] + mix * delayedSampleRight; + pOutput[i+1] = pInput[i + 1] + mix * delayedSampleRight; } if (enableState == EffectProcessor::DISABLING) { From 0e5110da9c5b2c3c13c200d0e7e6b8e56862ea87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sun, 15 Oct 2017 00:46:33 +0200 Subject: [PATCH 05/31] Rvert parameter speed back to period --- src/effects/native/flangereffect.cpp | 36 ++++++++++++++-------------- src/effects/native/flangereffect.h | 2 +- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/effects/native/flangereffect.cpp b/src/effects/native/flangereffect.cpp index d707956b29bd..5ebb42a37b97 100644 --- a/src/effects/native/flangereffect.cpp +++ b/src/effects/native/flangereffect.cpp @@ -27,18 +27,18 @@ EffectManifest FlangerEffect::getManifest() { "A simple modulation effect, created by taking the input signal " "and mixing it with a delayed, pitch modulated copy of itself.")); - EffectManifestParameter* speed = manifest.addParameter(); - speed->setId("speed"); - speed->setName(QObject::tr("Speed")); - speed->setDescription(QObject::tr("Controls the speed of the LFO (low frequency oscillator)\n" - "1/4 - 4 beats rounded to 1/2 beat if tempo is detected (decks and samplers) \n" - "0.05 - 4 seconds if no tempo is detected (mic & aux inputs, master mix)")); - speed->setControlHint(EffectManifestParameter::ControlHint::KNOB_LINEAR); - speed->setSemanticHint(EffectManifestParameter::SemanticHint::UNKNOWN); - speed->setUnitsHint(EffectManifestParameter::UnitsHint::BEATS); - speed->setMinimum(0.00); - speed->setMaximum(4.00); - speed->setDefault(1.00); + EffectManifestParameter* period = manifest.addParameter(); + period->setId("period"); + period->setName(QObject::tr("Period")); + period->setDescription(QObject::tr("Controls the period of the LFO (low frequency oscillator)\n" + "1/4 - 20 beats rounded to 1/2 beat if tempo is detected (decks and samplers) \n" + "0.05 - 20 seconds if no tempo is detected (mic & aux inputs, master mix)")); + period->setControlHint(EffectManifestParameter::ControlHint::KNOB_LINEAR); + period->setSemanticHint(EffectManifestParameter::SemanticHint::UNKNOWN); + period->setUnitsHint(EffectManifestParameter::UnitsHint::BEATS); + period->setMinimum(0.00); + period->setMaximum(20.00); + period->setDefault(1.00); EffectManifestParameter* width = manifest.addParameter(); width->setId("width"); @@ -102,7 +102,7 @@ EffectManifest FlangerEffect::getManifest() { FlangerEffect::FlangerEffect(EngineEffect* pEffect, const EffectManifest& manifest) - : m_pSpeedParameter(pEffect->getParameterById("speed")), + : m_pPeriodParameter(pEffect->getParameterById("period")), m_pWidthParameter(pEffect->getParameterById("width")), m_pManualParameter(pEffect->getParameterById("manual")), m_pRegenParameter(pEffect->getParameterById("regen")), @@ -128,18 +128,18 @@ void FlangerEffect::processChannel(const ChannelHandle& handle, const int kChannels = 2; // The parameter minimum is zero so the exact center of the knob is 2 beats. - double lfoSpeedParameter = m_pSpeedParameter->value(); + double lfoPeriodParameter = m_pPeriodParameter->value(); double lfoPeriodFrames; if (groupFeatures.has_beat_length_sec) { // lfoPeriodParameter is a number of beats - lfoSpeedParameter = std::max(roundToFraction(lfoSpeedParameter, 2.0), 1/4.0); + lfoPeriodParameter = std::max(roundToFraction(lfoPeriodParameter, 2.0), 1/4.0); if (m_pTripletParameter->toBool()) { - lfoSpeedParameter /= 3.0; + lfoPeriodParameter /= 3.0; } - lfoPeriodFrames = lfoSpeedParameter * groupFeatures.beat_length_sec * sampleRate; + lfoPeriodFrames = lfoPeriodParameter * groupFeatures.beat_length_sec * sampleRate; } else { // lfoPeriodParameter is a number of seconds - lfoPeriodFrames = std::max(lfoSpeedParameter, 1/4.0) * sampleRate; + lfoPeriodFrames = std::max(lfoPeriodParameter, 1/4.0) * sampleRate; } // lfoPeriodSamples is used to calculate the delay for each channel // independently in the loop below, so do not multiply lfoPeriodSamples by diff --git a/src/effects/native/flangereffect.h b/src/effects/native/flangereffect.h index 0539fc6fdf3f..3b1fc30dd394 100644 --- a/src/effects/native/flangereffect.h +++ b/src/effects/native/flangereffect.h @@ -54,7 +54,7 @@ class FlangerEffect : public PerChannelEffectProcessor { return getId(); } - EngineEffectParameter* m_pSpeedParameter; + EngineEffectParameter* m_pPeriodParameter; EngineEffectParameter* m_pWidthParameter; EngineEffectParameter* m_pManualParameter; EngineEffectParameter* m_pRegenParameter; From 138ecfcab729c1154867b4dfe25e754519c98719 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sun, 15 Oct 2017 01:11:33 +0200 Subject: [PATCH 06/31] De-crackel period knob --- src/effects/native/flangereffect.cpp | 10 ++++++++++ src/effects/native/flangereffect.h | 4 +++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/effects/native/flangereffect.cpp b/src/effects/native/flangereffect.cpp index 5ebb42a37b97..7d03841b07e4 100644 --- a/src/effects/native/flangereffect.cpp +++ b/src/effects/native/flangereffect.cpp @@ -141,6 +141,15 @@ void FlangerEffect::processChannel(const ChannelHandle& handle, // lfoPeriodParameter is a number of seconds lfoPeriodFrames = std::max(lfoPeriodParameter, 1/4.0) * sampleRate; } + + // When the period is changed, the position of the sound shouldn't + // so time need to be recalculated + if (pState->previousPeriodFrames != -1.0) { + pState->lfoFrames *= lfoPeriodFrames / pState->previousPeriodFrames; + } + pState->previousPeriodFrames = lfoPeriodFrames; + + // lfoPeriodSamples is used to calculate the delay for each channel // independently in the loop below, so do not multiply lfoPeriodSamples by // the number of channels. @@ -196,5 +205,6 @@ void FlangerEffect::processChannel(const ChannelHandle& handle, if (enableState == EffectProcessor::DISABLING) { SampleUtil::clear(delayLeft, numSamples); SampleUtil::clear(delayRight, numSamples); + pState->previousPeriodFrames = -1; } } diff --git a/src/effects/native/flangereffect.h b/src/effects/native/flangereffect.h index 3b1fc30dd394..2083b834ae56 100644 --- a/src/effects/native/flangereffect.h +++ b/src/effects/native/flangereffect.h @@ -22,7 +22,8 @@ constexpr SINT kBufferLenth = static_cast(ceil(kMaxDelayMs)) * 96; // for struct FlangerGroupState { FlangerGroupState() : delayPos(0), - lfoFrames(0) { + lfoFrames(0), + previousPeriodFrames(-1) { SampleUtil::applyGain(delayLeft, 0, kBufferLenth); SampleUtil::applyGain(delayRight, 0, kBufferLenth); } @@ -30,6 +31,7 @@ struct FlangerGroupState { CSAMPLE delayLeft[kBufferLenth]; unsigned int delayPos; unsigned int lfoFrames; + double previousPeriodFrames; }; class FlangerEffect : public PerChannelEffectProcessor { From d313cebd8c59c25fe31a68a430cb7a3ba20f52cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sun, 15 Oct 2017 01:40:01 +0200 Subject: [PATCH 07/31] De-crackle mix and regen knob --- src/effects/native/flangereffect.cpp | 26 ++++++++++++++++++++++---- src/effects/native/flangereffect.h | 6 +++++- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/effects/native/flangereffect.cpp b/src/effects/native/flangereffect.cpp index 7d03841b07e4..7ec12cb1a899 100644 --- a/src/effects/native/flangereffect.cpp +++ b/src/effects/native/flangereffect.cpp @@ -154,8 +154,19 @@ void FlangerEffect::processChannel(const ChannelHandle& handle, // independently in the loop below, so do not multiply lfoPeriodSamples by // the number of channels. + CSAMPLE_GAIN mix = m_pMixParameter->value(); + const CSAMPLE_GAIN mix_delta = (mix - pState->prev_mix) / + (numSamples / kChannels); + const CSAMPLE_GAIN mix_start = pState->prev_mix + mix_delta; + pState->prev_mix = mix; + CSAMPLE_GAIN regen = m_pRegenParameter->value(); + const CSAMPLE_GAIN regen_delta = (regen - pState->prev_regen) / + (numSamples / kChannels); + const CSAMPLE_GAIN regen_start = pState->prev_regen + regen_delta; + pState->prev_regen = regen; + // With and Manual is limited by amount of amplitude that remains from width // to kMaxDelaMS @@ -170,6 +181,11 @@ void FlangerEffect::processChannel(const ChannelHandle& handle, for (unsigned int i = 0; i < numSamples; i += kChannels) { + CSAMPLE_GAIN mix_ramped = mix_start + + mix_delta * i / kChannels; + CSAMPLE_GAIN regen_ramped = regen_start + + regen_delta * i / kChannels; + pState->lfoFrames++; if (pState->lfoFrames >= lfoPeriodFrames) { pState->lfoFrames = 0; @@ -193,18 +209,20 @@ void FlangerEffect::processChannel(const ChannelHandle& handle, CSAMPLE delayedSampleLeft = prevLeft + frac * (nextLeft - prevLeft); CSAMPLE delayedSampleRight = prevRight + frac * (nextRight - prevRight); - delayLeft[pState->delayPos] = tanh_approx(pInput[i] + regen * delayedSampleLeft); - delayRight[pState->delayPos] = tanh_approx(pInput[i + 1] + regen * delayedSampleRight); + delayLeft[pState->delayPos] = tanh_approx(pInput[i] + regen_ramped * delayedSampleLeft); + delayRight[pState->delayPos] = tanh_approx(pInput[i + 1] + regen_ramped * delayedSampleRight); pState->delayPos = (pState->delayPos + 1) % kBufferLenth; - pOutput[i] = pInput[i] + mix * delayedSampleLeft; - pOutput[i+1] = pInput[i + 1] + mix * delayedSampleRight; + pOutput[i] = pInput[i] + mix_ramped * delayedSampleLeft; + pOutput[i+1] = pInput[i + 1] + mix_ramped * delayedSampleRight; } if (enableState == EffectProcessor::DISABLING) { SampleUtil::clear(delayLeft, numSamples); SampleUtil::clear(delayRight, numSamples); pState->previousPeriodFrames = -1; + pState->prev_regen = -1; + pState->prev_mix = -1; } } diff --git a/src/effects/native/flangereffect.h b/src/effects/native/flangereffect.h index 2083b834ae56..43d4e3cba1c4 100644 --- a/src/effects/native/flangereffect.h +++ b/src/effects/native/flangereffect.h @@ -23,7 +23,9 @@ struct FlangerGroupState { FlangerGroupState() : delayPos(0), lfoFrames(0), - previousPeriodFrames(-1) { + previousPeriodFrames(-1), + prev_regen(-1), + prev_mix(-1) { SampleUtil::applyGain(delayLeft, 0, kBufferLenth); SampleUtil::applyGain(delayRight, 0, kBufferLenth); } @@ -32,6 +34,8 @@ struct FlangerGroupState { unsigned int delayPos; unsigned int lfoFrames; double previousPeriodFrames; + CSAMPLE_GAIN prev_regen; + CSAMPLE_GAIN prev_mix; }; class FlangerEffect : public PerChannelEffectProcessor { From 54aa94c74e14d160e9d6d394c92084baf402eb34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sun, 15 Oct 2017 01:52:46 +0200 Subject: [PATCH 08/31] De-crackle width and manual knob --- src/effects/native/flangereffect.cpp | 16 +++++++++++++++- src/effects/native/flangereffect.h | 8 ++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/effects/native/flangereffect.cpp b/src/effects/native/flangereffect.cpp index 7ec12cb1a899..64f339084575 100644 --- a/src/effects/native/flangereffect.cpp +++ b/src/effects/native/flangereffect.cpp @@ -176,6 +176,16 @@ void FlangerEffect::processChannel(const ChannelHandle& handle, double minManual = kCenterDelayMs - (kMaxLfoWidthMs - width) / 2; manual = math_clamp(manual, minManual, maxManual); + const double width_delta = (width - pState->prev_width) / + (numSamples / kChannels); + const double width_start = pState->prev_width + width_delta; + pState->prev_width = width; + + const double manual_delta = (manual - pState->prev_manual) / + (numSamples / kChannels); + const double manual_start = pState->prev_manual + manual_delta; + pState->prev_manual = manual; + CSAMPLE* delayLeft = pState->delayLeft; CSAMPLE* delayRight = pState->delayRight; @@ -185,6 +195,10 @@ void FlangerEffect::processChannel(const ChannelHandle& handle, + mix_delta * i / kChannels; CSAMPLE_GAIN regen_ramped = regen_start + regen_delta * i / kChannels; + CSAMPLE_GAIN width_ramped = width_start + + width_delta * i / kChannels; + CSAMPLE_GAIN manual_ramped = manual_start + + manual_delta * i / kChannels; pState->lfoFrames++; if (pState->lfoFrames >= lfoPeriodFrames) { @@ -192,7 +206,7 @@ void FlangerEffect::processChannel(const ChannelHandle& handle, } float periodFraction = static_cast(pState->lfoFrames) / lfoPeriodFrames; - double delayMs = manual + width / 2 * sin(M_PI * 2.0f * periodFraction); + double delayMs = manual_ramped + width_ramped / 2 * sin(M_PI * 2.0f * periodFraction); double delayFrames = delayMs * sampleRate / 1000; SINT framePrev = (pState->delayPos - static_cast(floor(delayFrames)) diff --git a/src/effects/native/flangereffect.h b/src/effects/native/flangereffect.h index 43d4e3cba1c4..2e4a7cbfd70c 100644 --- a/src/effects/native/flangereffect.h +++ b/src/effects/native/flangereffect.h @@ -24,8 +24,10 @@ struct FlangerGroupState { : delayPos(0), lfoFrames(0), previousPeriodFrames(-1), - prev_regen(-1), - prev_mix(-1) { + prev_regen(0), + prev_mix(0), + prev_width(0), + prev_manual(kCenterDelayMs) { SampleUtil::applyGain(delayLeft, 0, kBufferLenth); SampleUtil::applyGain(delayRight, 0, kBufferLenth); } @@ -36,6 +38,8 @@ struct FlangerGroupState { double previousPeriodFrames; CSAMPLE_GAIN prev_regen; CSAMPLE_GAIN prev_mix; + CSAMPLE_GAIN prev_width; + CSAMPLE_GAIN prev_manual; }; class FlangerEffect : public PerChannelEffectProcessor { From 408a182ba60c9b7f3a193afacb7eaa3ec1234360 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Mon, 16 Oct 2017 02:27:18 +0200 Subject: [PATCH 09/31] tweak min an max delay --- src/effects/native/flangereffect.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/effects/native/flangereffect.h b/src/effects/native/flangereffect.h index 2e4a7cbfd70c..fad13a959491 100644 --- a/src/effects/native/flangereffect.h +++ b/src/effects/native/flangereffect.h @@ -12,8 +12,8 @@ #include "util/types.h" namespace { -constexpr double kMaxDelayMs = 14.0; -constexpr double kMinDelayMs = 3.0; +constexpr double kMaxDelayMs = 13.0; +constexpr double kMinDelayMs = 0.22; constexpr double kCenterDelayMs = (kMaxDelayMs - kMinDelayMs) / 2 + kMinDelayMs; constexpr double kMaxLfoWidthMs = kMaxDelayMs - kMinDelayMs; constexpr SINT kBufferLenth = static_cast(ceil(kMaxDelayMs)) * 96; // for 96 kHz From 3b78e41838a85e6e75593e39bc6b38d4bbf5d510 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Wed, 18 Oct 2017 14:56:19 +0200 Subject: [PATCH 10/31] use override keyword in controlbehaviour.h --- src/control/controlbehavior.h | 53 ++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/src/control/controlbehavior.h b/src/control/controlbehavior.h index 7185d62a5085..ab6b9d77f8ea 100644 --- a/src/control/controlbehavior.h +++ b/src/control/controlbehavior.h @@ -12,14 +12,19 @@ class ControlNumericBehavior { public: virtual ~ControlNumericBehavior() { }; - // Returns true if the set should occur. Mutates dValue if the value should - // be changed. + // this may change the dValue in place before it is adopted + // Returns false to reject the new value entirely virtual bool setFilter(double* dValue); + // returns the normalized parameter range 0..1 virtual double valueToParameter(double dValue); + // returns the normalized parameter range 0..1 virtual double midiValueToParameter(double midiValue); + // returns the scaled user visible value virtual double parameterToValue(double dParam); + // returns the midi range parameter 0..127 virtual double valueToMidiParameter(double dValue); + virtual void setValueFromMidiParameter(MidiOpCode o, double dParam, ControlDoublePrivate* pControl); }; @@ -28,13 +33,13 @@ class ControlPotmeterBehavior : public ControlNumericBehavior { public: ControlPotmeterBehavior(double dMinValue, double dMaxValue, bool allowOutOfBounds); - virtual ~ControlPotmeterBehavior(); + ~ControlPotmeterBehavior() override; - virtual bool setFilter(double* dValue); - virtual double valueToParameter(double dValue); - virtual double midiValueToParameter(double midiValue); - virtual double parameterToValue(double dParam); - virtual double valueToMidiParameter(double dValue); + bool setFilter(double* dValue) override; + double valueToParameter(double dValue) override; + double midiValueToParameter(double midiValue) override; + double parameterToValue(double dParam) override; + double valueToMidiParameter(double dValue) override; protected: double m_dMinValue; @@ -46,10 +51,10 @@ class ControlPotmeterBehavior : public ControlNumericBehavior { class ControlLogPotmeterBehavior : public ControlPotmeterBehavior { public: ControlLogPotmeterBehavior(double dMinValue, double dMaxValue, double minDB); - virtual ~ControlLogPotmeterBehavior(); + ~ControlLogPotmeterBehavior() override; - virtual double valueToParameter(double dValue); - virtual double parameterToValue(double dParam); + double valueToParameter(double dValue) override; + double parameterToValue(double dParam) override; protected: double m_minDB; @@ -60,21 +65,22 @@ class ControlLinPotmeterBehavior : public ControlPotmeterBehavior { public: ControlLinPotmeterBehavior(double dMinValue, double dMaxValue, bool allowOutOfBounds); - virtual ~ControlLinPotmeterBehavior(); + ~ControlLinPotmeterBehavior() override; }; class ControlAudioTaperPotBehavior : public ControlPotmeterBehavior { public: ControlAudioTaperPotBehavior(double minDB, double maxDB, double neutralParameter); - virtual ~ControlAudioTaperPotBehavior(); + ~ControlAudioTaperPotBehavior() override; - virtual double valueToParameter(double dValue); - virtual double parameterToValue(double dParam); - virtual double midiValueToParameter(double midiValue); - virtual double valueToMidiParameter(double dValue); - virtual void setValueFromMidiParameter(MidiOpCode o, double dParam, - ControlDoublePrivate* pControl); + double valueToParameter(double dValue); + double parameterToValue(double dParam); + double midiValueToParameter(double midiValue); + double valueToMidiParameter(double dValue); + void setValueFromMidiParameter( + MidiOpCode o, double dParam, ControlDoublePrivate* pControl) + override; protected: // a knob position between 0 and 1 where the gain is 1 (0dB) @@ -94,8 +100,8 @@ class ControlAudioTaperPotBehavior : public ControlPotmeterBehavior { class ControlTTRotaryBehavior : public ControlNumericBehavior { public: - virtual double valueToParameter(double dValue); - virtual double parameterToValue(double dParam); + double valueToParameter(double dValue) override; + double parameterToValue(double dParam) override; }; class ControlPushButtonBehavior : public ControlNumericBehavior { @@ -114,8 +120,9 @@ class ControlPushButtonBehavior : public ControlNumericBehavior { }; ControlPushButtonBehavior(ButtonMode buttonMode, int iNumStates); - virtual void setValueFromMidiParameter(MidiOpCode o, double dParam, - ControlDoublePrivate* pControl); + void setValueFromMidiParameter( + MidiOpCode o, double dParam, ControlDoublePrivate* pControl) + override; private: // We create many hundreds of push buttons at Mixxx startup and most of them From 49078e730d5a0459931861acf3f743b0e0a09707 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Wed, 18 Oct 2017 15:26:58 +0200 Subject: [PATCH 11/31] Disable broken midi access to COs where no behaviour is installed --- src/control/control.cpp | 27 ++++++++++++++------------- src/control/control.h | 4 ++-- src/control/controlobject.cpp | 4 ++-- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/control/control.cpp b/src/control/control.cpp index 178fa922c812..474d71d72308 100644 --- a/src/control/control.cpp +++ b/src/control/control.cpp @@ -233,30 +233,31 @@ double ControlDoublePrivate::getParameterForValue(double value) const { return value; } -double ControlDoublePrivate::getParameterForMidiValue(double midiValue) const { +double ControlDoublePrivate::getParameterForMidi(double midiParam) const { QSharedPointer pBehavior = m_pBehavior; - if (!pBehavior.isNull()) { - return pBehavior->midiValueToParameter(midiValue); + VERIFY_OR_DEBUG_ASSERT(m_pBehavior) { + qWarning() << "Cannot set" << m_key << "by Midi"; + return 0; } - return midiValue; + return pBehavior->midiValueToParameter(midiParam); } -void ControlDoublePrivate::setMidiParameter(MidiOpCode opcode, double dParam) { +void ControlDoublePrivate::setValueFromMidi(MidiOpCode opcode, double midiParam) { QSharedPointer pBehavior = m_pBehavior; - if (!pBehavior.isNull()) { - pBehavior->setValueFromMidiParameter(opcode, dParam, this); - } else { - set(dParam, NULL); + VERIFY_OR_DEBUG_ASSERT(m_pBehavior) { + qWarning() << "Cannot set" << m_key << "by Midi"; + return; } + pBehavior->setValueFromMidiParameter(opcode, midiParam, this); } double ControlDoublePrivate::getMidiParameter() const { QSharedPointer pBehavior = m_pBehavior; - double value = get(); - if (!pBehavior.isNull()) { - value = pBehavior->valueToMidiParameter(value); + VERIFY_OR_DEBUG_ASSERT(m_pBehavior) { + qWarning() << "Cannot get" << m_key << "by Midi"; + return 0; } - return value; + return pBehavior->valueToMidiParameter(get()); } bool ControlDoublePrivate::connectValueChangeRequest(const QObject* receiver, diff --git a/src/control/control.h b/src/control/control.h index edc876f5eac2..ea3fded2f73e 100644 --- a/src/control/control.h +++ b/src/control/control.h @@ -80,9 +80,9 @@ class ControlDoublePrivate : public QObject { void setParameter(double dParam, QObject* pSender); double getParameter() const; double getParameterForValue(double value) const; - double getParameterForMidiValue(double midiValue) const; + double getParameterForMidi(double midiValue) const; - void setMidiParameter(MidiOpCode opcode, double dParam); + void setValueFromMidi(MidiOpCode opcode, double dParam); double getMidiParameter() const; inline bool ignoreNops() const { diff --git a/src/control/controlobject.cpp b/src/control/controlobject.cpp index a4a29f5cddca..1d70089a721f 100644 --- a/src/control/controlobject.cpp +++ b/src/control/controlobject.cpp @@ -80,7 +80,7 @@ ControlObject* ControlObject::getControl(const ConfigKey& key, bool warn) { void ControlObject::setValueFromMidi(MidiOpCode o, double v) { if (m_pControl) { - m_pControl->setMidiParameter(o, v); + m_pControl->setValueFromMidi(o, v); } } @@ -103,7 +103,7 @@ double ControlObject::getParameterForValue(double value) const { } double ControlObject::getParameterForMidiValue(double midiValue) const { - return m_pControl ? m_pControl->getParameterForMidiValue(midiValue) : 0.0; + return m_pControl ? m_pControl->getParameterForMidi(midiValue) : 0.0; } void ControlObject::setParameter(double v) { From 12c286d8be696144071c051df086b55d70496d81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Thu, 19 Oct 2017 16:27:05 +0200 Subject: [PATCH 12/31] clean up function names a bit --- src/control/control.cpp | 4 ++-- src/control/controlbehavior.cpp | 28 ++++++++++++------------- src/control/controlbehavior.h | 12 +++++------ src/control/controlobject.cpp | 4 ++-- src/control/controlobject.h | 2 +- src/controllers/midi/midicontroller.cpp | 2 +- src/test/audiotaperpot_test.cpp | 18 ++++++++-------- 7 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/control/control.cpp b/src/control/control.cpp index 474d71d72308..8e2d8bc2773f 100644 --- a/src/control/control.cpp +++ b/src/control/control.cpp @@ -239,7 +239,7 @@ double ControlDoublePrivate::getParameterForMidi(double midiParam) const { qWarning() << "Cannot set" << m_key << "by Midi"; return 0; } - return pBehavior->midiValueToParameter(midiParam); + return pBehavior->midiToParameter(midiParam); } void ControlDoublePrivate::setValueFromMidi(MidiOpCode opcode, double midiParam) { @@ -248,7 +248,7 @@ void ControlDoublePrivate::setValueFromMidi(MidiOpCode opcode, double midiParam) qWarning() << "Cannot set" << m_key << "by Midi"; return; } - pBehavior->setValueFromMidiParameter(opcode, midiParam, this); + pBehavior->setValueFromMidi(opcode, midiParam, this); } double ControlDoublePrivate::getMidiParameter() const { diff --git a/src/control/controlbehavior.cpp b/src/control/controlbehavior.cpp index a435bf7d973f..a4205ecd84ef 100644 --- a/src/control/controlbehavior.cpp +++ b/src/control/controlbehavior.cpp @@ -11,7 +11,7 @@ double ControlNumericBehavior::valueToParameter(double dValue) { return dValue; } -double ControlNumericBehavior::midiValueToParameter(double midiValue) { +double ControlNumericBehavior::midiToParameter(double midiValue) { return midiValue; } @@ -23,10 +23,10 @@ double ControlNumericBehavior::valueToMidiParameter(double dValue) { return dValue; } -void ControlNumericBehavior::setValueFromMidiParameter(MidiOpCode o, double dParam, - ControlDoublePrivate* pControl) { +void ControlNumericBehavior::setValueFromMidi( + MidiOpCode o, double dParam, ControlDoublePrivate* pControl) { Q_UNUSED(o); - double dNorm = midiValueToParameter(dParam); + double dNorm = midiToParameter(dParam); pControl->set(parameterToValue(dNorm), NULL); } @@ -64,7 +64,7 @@ double ControlPotmeterBehavior::valueToParameter(double dValue) { return (dValue - m_dMinValue) / m_dValueRange; } -double ControlPotmeterBehavior::midiValueToParameter(double midiValue) { +double ControlPotmeterBehavior::midiToParameter(double midiValue) { double parameter; if (midiValue > 64) { parameter = (midiValue - 1) / 126.0; @@ -98,7 +98,8 @@ double ControlPotmeterBehavior::valueToMidiParameter(double dValue) { #define middlePosition ((maxPosition - minPosition) / 2.0) #define positionrange (maxPosition - minPosition) -ControlLogPotmeterBehavior::ControlLogPotmeterBehavior(double dMinValue, double dMaxValue, double minDB) +ControlLogPotmeterBehavior::ControlLogPotmeterBehavior(double dMinValue, + double dMaxValue, double minDB) : ControlPotmeterBehavior(dMinValue, dMaxValue, false) { if (minDB >= 0) { qWarning() << "ControlLogPotmeterBehavior::ControlLogPotmeterBehavior() minDB must be negative"; @@ -132,8 +133,8 @@ double ControlLogPotmeterBehavior::parameterToValue(double dParam) { return m_dMinValue + (linPrameter * m_dValueRange); } -ControlLinPotmeterBehavior::ControlLinPotmeterBehavior(double dMinValue, double dMaxValue, - bool allowOutOfBounds) +ControlLinPotmeterBehavior::ControlLinPotmeterBehavior( + double dMinValue, double dMaxValue, bool allowOutOfBounds) : ControlPotmeterBehavior(dMinValue, dMaxValue, allowOutOfBounds) { } @@ -206,7 +207,7 @@ double ControlAudioTaperPotBehavior::parameterToValue(double dParam) { return dValue; } -double ControlAudioTaperPotBehavior::midiValueToParameter(double midiValue) { +double ControlAudioTaperPotBehavior::midiToParameter(double midiValue) { double dParam; if (m_neutralParameter && m_neutralParameter != 1.0) { double neutralTest = (midiValue - m_midiCorrection) / 127.0; @@ -241,14 +242,13 @@ double ControlAudioTaperPotBehavior::valueToMidiParameter(double dValue) { return dMidiParam; } -void ControlAudioTaperPotBehavior::setValueFromMidiParameter(MidiOpCode o, double dMidiParam, - ControlDoublePrivate* pControl) { +void ControlAudioTaperPotBehavior::setValueFromMidi( + MidiOpCode o, double dMidiParam, ControlDoublePrivate* pControl) { Q_UNUSED(o); - double dParam = midiValueToParameter(dMidiParam); + double dParam = midiToParameter(dMidiParam); pControl->set(parameterToValue(dParam), NULL); } - double ControlTTRotaryBehavior::valueToParameter(double dValue) { return (dValue * 200.0 + 64) / 127.0; } @@ -273,7 +273,7 @@ ControlPushButtonBehavior::ControlPushButtonBehavior(ButtonMode buttonMode, m_iNumStates(iNumStates) { } -void ControlPushButtonBehavior::setValueFromMidiParameter( +void ControlPushButtonBehavior::setValueFromMidi( MidiOpCode o, double dParam, ControlDoublePrivate* pControl) { // Calculate pressed State of the midi Button // Some controller like the RMX2 are sending always MIDI_NOTE_ON diff --git a/src/control/controlbehavior.h b/src/control/controlbehavior.h index ab6b9d77f8ea..26ca5290d465 100644 --- a/src/control/controlbehavior.h +++ b/src/control/controlbehavior.h @@ -19,13 +19,13 @@ class ControlNumericBehavior { // returns the normalized parameter range 0..1 virtual double valueToParameter(double dValue); // returns the normalized parameter range 0..1 - virtual double midiValueToParameter(double midiValue); + virtual double midiToParameter(double midiValue); // returns the scaled user visible value virtual double parameterToValue(double dParam); // returns the midi range parameter 0..127 virtual double valueToMidiParameter(double dValue); - virtual void setValueFromMidiParameter(MidiOpCode o, double dParam, + virtual void setValueFromMidi(MidiOpCode o, double dParam, ControlDoublePrivate* pControl); }; @@ -37,7 +37,7 @@ class ControlPotmeterBehavior : public ControlNumericBehavior { bool setFilter(double* dValue) override; double valueToParameter(double dValue) override; - double midiValueToParameter(double midiValue) override; + double midiToParameter(double midiValue) override; double parameterToValue(double dParam) override; double valueToMidiParameter(double dValue) override; @@ -76,9 +76,9 @@ class ControlAudioTaperPotBehavior : public ControlPotmeterBehavior { double valueToParameter(double dValue); double parameterToValue(double dParam); - double midiValueToParameter(double midiValue); + double midiToParameter(double midiValue); double valueToMidiParameter(double dValue); - void setValueFromMidiParameter( + void setValueFromMidi( MidiOpCode o, double dParam, ControlDoublePrivate* pControl) override; @@ -120,7 +120,7 @@ class ControlPushButtonBehavior : public ControlNumericBehavior { }; ControlPushButtonBehavior(ButtonMode buttonMode, int iNumStates); - void setValueFromMidiParameter( + void setValueFromMidi( MidiOpCode o, double dParam, ControlDoublePrivate* pControl) override; diff --git a/src/control/controlobject.cpp b/src/control/controlobject.cpp index 1d70089a721f..31c8c2b51022 100644 --- a/src/control/controlobject.cpp +++ b/src/control/controlobject.cpp @@ -102,8 +102,8 @@ double ControlObject::getParameterForValue(double value) const { return m_pControl ? m_pControl->getParameterForValue(value) : 0.0; } -double ControlObject::getParameterForMidiValue(double midiValue) const { - return m_pControl ? m_pControl->getParameterForMidi(midiValue) : 0.0; +double ControlObject::getParameterForMidi(double midiParameter) const { + return m_pControl ? m_pControl->getParameterForMidi(midiParameter) : 0.0; } void ControlObject::setParameter(double v) { diff --git a/src/control/controlobject.h b/src/control/controlobject.h index f5e6396fc173..96de336d8018 100644 --- a/src/control/controlobject.h +++ b/src/control/controlobject.h @@ -136,7 +136,7 @@ class ControlObject : public QObject { virtual double getParameterForValue(double value) const; // Returns the parameterized value of the object. Thread safe, non-blocking. - virtual double getParameterForMidiValue(double midiValue) const; + virtual double getParameterForMidi(double midiValue) const; // Sets the control parameterized value to v. Thread safe, non-blocking. virtual void setParameter(double v); diff --git a/src/controllers/midi/midicontroller.cpp b/src/controllers/midi/midicontroller.cpp index c2848d302023..23aa6994ef8e 100644 --- a/src/controllers/midi/midicontroller.cpp +++ b/src/controllers/midi/midicontroller.cpp @@ -348,7 +348,7 @@ void MidiController::processInputMapping(const MidiInputMapping& mapping, if (mapping.options.soft_takeover) { // This is the only place to enable it if it isn't already. m_st.enable(pCO); - if (m_st.ignore(pCO, pCO->getParameterForMidiValue(newValue))) { + if (m_st.ignore(pCO, pCO->getParameterForMidi(newValue))) { return; } } diff --git a/src/test/audiotaperpot_test.cpp b/src/test/audiotaperpot_test.cpp index cd312f320ded..3b3e611a687f 100644 --- a/src/test/audiotaperpot_test.cpp +++ b/src/test/audiotaperpot_test.cpp @@ -25,10 +25,10 @@ TEST_F(AudioTaperPotTest, ScaleTest) { double neutralMidi = catpb.valueToMidiParameter(1); ASSERT_EQ(0.0, fmod(neutralMidi, 1)); // Midi value 64 should result in 0,5 - ASSERT_EQ(neutralParameter, catpb.midiValueToParameter(neutralMidi)); + ASSERT_EQ(neutralParameter, catpb.midiToParameter(neutralMidi)); // roundtrip check - ASSERT_DOUBLE_EQ(0.25, catpb.parameterToValue(catpb.midiValueToParameter(catpb.valueToMidiParameter(0.25)))); - ASSERT_DOUBLE_EQ(0.75, catpb.parameterToValue(catpb.midiValueToParameter(catpb.valueToMidiParameter(0.75)))); + ASSERT_DOUBLE_EQ(0.25, catpb.parameterToValue(catpb.midiToParameter(catpb.valueToMidiParameter(0.25)))); + ASSERT_DOUBLE_EQ(0.75, catpb.parameterToValue(catpb.midiToParameter(catpb.valueToMidiParameter(0.75)))); } { @@ -46,10 +46,10 @@ TEST_F(AudioTaperPotTest, ScaleTest) { double neutralMidi = catpb.valueToMidiParameter(1); ASSERT_EQ(0.0, fmod(neutralMidi, 1)); // Midi value 64 should result in 0,5 - ASSERT_EQ(neutralParameter, catpb.midiValueToParameter(neutralMidi)); + ASSERT_EQ(neutralParameter, catpb.midiToParameter(neutralMidi)); // roundtrip check - ASSERT_DOUBLE_EQ(0.25, catpb.parameterToValue(catpb.midiValueToParameter(catpb.valueToMidiParameter(0.25)))); - ASSERT_DOUBLE_EQ(0.75, catpb.parameterToValue(catpb.midiValueToParameter(catpb.valueToMidiParameter(0.75)))); + ASSERT_DOUBLE_EQ(0.25, catpb.parameterToValue(catpb.midiToParameter(catpb.valueToMidiParameter(0.25)))); + ASSERT_DOUBLE_EQ(0.75, catpb.parameterToValue(catpb.midiToParameter(catpb.valueToMidiParameter(0.75)))); } { @@ -67,9 +67,9 @@ TEST_F(AudioTaperPotTest, ScaleTest) { double neutralMidi = catpb.valueToMidiParameter(1); ASSERT_EQ(0.0, fmod(neutralMidi, 1)); // Midi value 64 should result in 0,5 - ASSERT_EQ(neutralParameter, catpb.midiValueToParameter(neutralMidi)); + ASSERT_EQ(neutralParameter, catpb.midiToParameter(neutralMidi)); // roundtrip checkx - ASSERT_DOUBLE_EQ(0.25, catpb.parameterToValue(catpb.midiValueToParameter(catpb.valueToMidiParameter(0.25)))); - ASSERT_DOUBLE_EQ(0.75, catpb.parameterToValue(catpb.midiValueToParameter(catpb.valueToMidiParameter(0.75)))); + ASSERT_DOUBLE_EQ(0.25, catpb.parameterToValue(catpb.midiToParameter(catpb.valueToMidiParameter(0.25)))); + ASSERT_DOUBLE_EQ(0.75, catpb.parameterToValue(catpb.midiToParameter(catpb.valueToMidiParameter(0.75)))); } } From fbf7b450d14574d68cb6877a4adfef2a41f1b1ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sat, 21 Oct 2017 05:47:10 +0200 Subject: [PATCH 13/31] verify lokal pBehavior --- src/control/control.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/control/control.cpp b/src/control/control.cpp index 8e2d8bc2773f..0e757ed43ecf 100644 --- a/src/control/control.cpp +++ b/src/control/control.cpp @@ -235,7 +235,7 @@ double ControlDoublePrivate::getParameterForValue(double value) const { double ControlDoublePrivate::getParameterForMidi(double midiParam) const { QSharedPointer pBehavior = m_pBehavior; - VERIFY_OR_DEBUG_ASSERT(m_pBehavior) { + VERIFY_OR_DEBUG_ASSERT(pBehavior) { qWarning() << "Cannot set" << m_key << "by Midi"; return 0; } @@ -244,7 +244,7 @@ double ControlDoublePrivate::getParameterForMidi(double midiParam) const { void ControlDoublePrivate::setValueFromMidi(MidiOpCode opcode, double midiParam) { QSharedPointer pBehavior = m_pBehavior; - VERIFY_OR_DEBUG_ASSERT(m_pBehavior) { + VERIFY_OR_DEBUG_ASSERT(pBehavior) { qWarning() << "Cannot set" << m_key << "by Midi"; return; } @@ -253,7 +253,7 @@ void ControlDoublePrivate::setValueFromMidi(MidiOpCode opcode, double midiParam) double ControlDoublePrivate::getMidiParameter() const { QSharedPointer pBehavior = m_pBehavior; - VERIFY_OR_DEBUG_ASSERT(m_pBehavior) { + VERIFY_OR_DEBUG_ASSERT(pBehavior) { qWarning() << "Cannot get" << m_key << "by Midi"; return 0; } From af7ea00a14d790b939a9f23392f0675545c61c39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sat, 21 Oct 2017 14:16:40 +0200 Subject: [PATCH 14/31] Warn about missing Midi representation --- src/control/controlbehavior.cpp | 10 ++++++++-- src/control/controlbehavior.h | 5 +++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/control/controlbehavior.cpp b/src/control/controlbehavior.cpp index a4205ecd84ef..55eaf6165a67 100644 --- a/src/control/controlbehavior.cpp +++ b/src/control/controlbehavior.cpp @@ -12,7 +12,10 @@ double ControlNumericBehavior::valueToParameter(double dValue) { } double ControlNumericBehavior::midiToParameter(double midiValue) { - return midiValue; + Q_UNUSED(midiValue); + DEBUG_ASSERT(false); + qWarning() << "midiToParameter not implemented"; + return 0; } double ControlNumericBehavior::parameterToValue(double dParam) { @@ -20,7 +23,10 @@ double ControlNumericBehavior::parameterToValue(double dParam) { } double ControlNumericBehavior::valueToMidiParameter(double dValue) { - return dValue; + Q_UNUSED(dValue); + DEBUG_ASSERT(false); + qWarning() << "valueToMidiParameter not implemented"; + return 0; } void ControlNumericBehavior::setValueFromMidi( diff --git a/src/control/controlbehavior.h b/src/control/controlbehavior.h index 26ca5290d465..93c6afb21121 100644 --- a/src/control/controlbehavior.h +++ b/src/control/controlbehavior.h @@ -8,6 +8,7 @@ class ControlDoublePrivate; +// A linear 0 .. 1 control without Midi representation class ControlNumericBehavior { public: virtual ~ControlNumericBehavior() { }; @@ -25,8 +26,8 @@ class ControlNumericBehavior { // returns the midi range parameter 0..127 virtual double valueToMidiParameter(double dValue); - virtual void setValueFromMidi(MidiOpCode o, double dParam, - ControlDoublePrivate* pControl); + virtual void setValueFromMidi( + MidiOpCode o, double dParam, ControlDoublePrivate* pControl); }; class ControlPotmeterBehavior : public ControlNumericBehavior { From e797489259390aa69d5efb3b2ca29e178427f7c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sat, 21 Oct 2017 16:04:10 +0200 Subject: [PATCH 15/31] Added KNOB_LINEAR_INVERSE and KNOB_LOGARITHMIC_INVERSE --- src/control/controlbehavior.cpp | 34 +++++++++++++++++++-------- src/control/controlbehavior.h | 24 ++++++++++++++----- src/control/controleffectknob.cpp | 6 +++++ src/effects/effectmanifestparameter.h | 2 ++ 4 files changed, 50 insertions(+), 16 deletions(-) diff --git a/src/control/controlbehavior.cpp b/src/control/controlbehavior.cpp index 55eaf6165a67..50678e69c11a 100644 --- a/src/control/controlbehavior.cpp +++ b/src/control/controlbehavior.cpp @@ -44,9 +44,6 @@ ControlPotmeterBehavior::ControlPotmeterBehavior(double dMinValue, double dMaxVa m_bAllowOutOfBounds(allowOutOfBounds) { } -ControlPotmeterBehavior::~ControlPotmeterBehavior() { -} - bool ControlPotmeterBehavior::setFilter(double* dValue) { if (!m_bAllowOutOfBounds) { if (*dValue > m_dMaxValue) { @@ -116,9 +113,6 @@ ControlLogPotmeterBehavior::ControlLogPotmeterBehavior(double dMinValue, m_minOffset = db2ratio(m_minDB); } -ControlLogPotmeterBehavior::~ControlLogPotmeterBehavior() { -} - double ControlLogPotmeterBehavior::valueToParameter(double dValue) { if (m_dValueRange == 0.0) { return 0; @@ -139,12 +133,35 @@ double ControlLogPotmeterBehavior::parameterToValue(double dParam) { return m_dMinValue + (linPrameter * m_dValueRange); } +ControlLogInvPotmeterBehavior::ControlLogInvPotmeterBehavior( + double dMinValue, double dMaxValue, double minDB) + : ControlLogPotmeterBehavior(dMinValue, dMaxValue, minDB) { +} + +double ControlLogInvPotmeterBehavior::valueToParameter(double dValue) { + return 1 - ControlLogPotmeterBehavior::valueToParameter(dValue); +} + +double ControlLogInvPotmeterBehavior::parameterToValue(double dParam) { + return ControlLogPotmeterBehavior::parameterToValue(1 - dParam); +} + ControlLinPotmeterBehavior::ControlLinPotmeterBehavior( double dMinValue, double dMaxValue, bool allowOutOfBounds) : ControlPotmeterBehavior(dMinValue, dMaxValue, allowOutOfBounds) { } -ControlLinPotmeterBehavior::~ControlLinPotmeterBehavior() { +ControlLinInvPotmeterBehavior::ControlLinInvPotmeterBehavior( + double dMinValue, double dMaxValue, bool allowOutOfBounds) + : ControlPotmeterBehavior(dMinValue, dMaxValue, allowOutOfBounds) { +} + +double ControlLinInvPotmeterBehavior::valueToParameter(double dValue) { + return 1 - ControlPotmeterBehavior::valueToParameter(dValue); +} + +double ControlLinInvPotmeterBehavior::parameterToValue(double dParam) { + return ControlPotmeterBehavior::parameterToValue(1 - dParam); } ControlAudioTaperPotBehavior::ControlAudioTaperPotBehavior( @@ -158,9 +175,6 @@ ControlAudioTaperPotBehavior::ControlAudioTaperPotBehavior( m_midiCorrection = ceil(m_neutralParameter * 127) - (m_neutralParameter * 127); } -ControlAudioTaperPotBehavior::~ControlAudioTaperPotBehavior() { -} - double ControlAudioTaperPotBehavior::valueToParameter(double dValue) { double dParam = 1.0; if (dValue <= 0.0) { diff --git a/src/control/controlbehavior.h b/src/control/controlbehavior.h index 93c6afb21121..4cece00a8129 100644 --- a/src/control/controlbehavior.h +++ b/src/control/controlbehavior.h @@ -34,7 +34,6 @@ class ControlPotmeterBehavior : public ControlNumericBehavior { public: ControlPotmeterBehavior(double dMinValue, double dMaxValue, bool allowOutOfBounds); - ~ControlPotmeterBehavior() override; bool setFilter(double* dValue) override; double valueToParameter(double dValue) override; @@ -52,7 +51,6 @@ class ControlPotmeterBehavior : public ControlNumericBehavior { class ControlLogPotmeterBehavior : public ControlPotmeterBehavior { public: ControlLogPotmeterBehavior(double dMinValue, double dMaxValue, double minDB); - ~ControlLogPotmeterBehavior() override; double valueToParameter(double dValue) override; double parameterToValue(double dParam) override; @@ -62,18 +60,32 @@ class ControlLogPotmeterBehavior : public ControlPotmeterBehavior { double m_minOffset; }; +class ControlLogInvPotmeterBehavior : public ControlLogPotmeterBehavior { + public: + ControlLogInvPotmeterBehavior(double dMinValue, double dMaxValue, double minDB); + + double valueToParameter(double dValue) override; + double parameterToValue(double dParam) override; +}; + class ControlLinPotmeterBehavior : public ControlPotmeterBehavior { public: - ControlLinPotmeterBehavior(double dMinValue, double dMaxValue, - bool allowOutOfBounds); - ~ControlLinPotmeterBehavior() override; + ControlLinPotmeterBehavior( + double dMinValue, double dMaxValue, bool allowOutOfBounds); +}; + +class ControlLinInvPotmeterBehavior : public ControlPotmeterBehavior { + public: + ControlLinInvPotmeterBehavior( + double dMinValue, double dMaxValue, bool allowOutOfBounds); + double valueToParameter(double dValue) override; + double parameterToValue(double dParam) override; }; class ControlAudioTaperPotBehavior : public ControlPotmeterBehavior { public: ControlAudioTaperPotBehavior(double minDB, double maxDB, double neutralParameter); - ~ControlAudioTaperPotBehavior() override; double valueToParameter(double dValue); double parameterToValue(double dParam); diff --git a/src/control/controleffectknob.cpp b/src/control/controleffectknob.cpp index b42d1ed83634..b509609aa0b6 100644 --- a/src/control/controleffectknob.cpp +++ b/src/control/controleffectknob.cpp @@ -16,6 +16,9 @@ void ControlEffectKnob::setBehaviour(EffectManifestParameter::ControlHint type, if (type == EffectManifestParameter::ControlHint::KNOB_LINEAR) { m_pControl->setBehavior(new ControlLinPotmeterBehavior( dMinValue, dMaxValue, false)); + } else if (type == EffectManifestParameter::ControlHint::KNOB_LINEAR_INVERSE) { + m_pControl->setBehavior(new ControlLinInvPotmeterBehavior( + dMinValue, dMaxValue, false)); } else if (type == EffectManifestParameter::ControlHint::KNOB_LOGARITHMIC) { if (dMinValue == 0) { if (dMaxValue == 1.0) { @@ -34,5 +37,8 @@ void ControlEffectKnob::setBehaviour(EffectManifestParameter::ControlHint type, m_pControl->setBehavior( new ControlLogPotmeterBehavior(dMinValue, dMaxValue, -40)); } + } else if (type == EffectManifestParameter::ControlHint::KNOB_LOGARITHMIC_INVERSE) { + m_pControl->setBehavior( + new ControlLogPotmeterBehavior(dMinValue, dMaxValue, -40)); } } diff --git a/src/effects/effectmanifestparameter.h b/src/effects/effectmanifestparameter.h index 623b6b2ee12a..0e059a52bbf6 100644 --- a/src/effects/effectmanifestparameter.h +++ b/src/effects/effectmanifestparameter.h @@ -10,7 +10,9 @@ class EffectManifestParameter { enum class ControlHint { UNKNOWN = 0, KNOB_LINEAR, + KNOB_LINEAR_INVERSE, KNOB_LOGARITHMIC, + KNOB_LOGARITHMIC_INVERSE, KNOB_STEPPING, // A step rotary, steps given by m_steps // are arranged with equal distance on scale TOGGLE_STEPPING // For button and enum controls, not accessible From 99cd40193e8fbbf33f1cb3e234e510dddff8b957 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sat, 21 Oct 2017 16:33:48 +0200 Subject: [PATCH 16/31] Made a logarithmic flanger speed knob --- src/control/controleffectknob.cpp | 2 +- src/effects/native/flangereffect.cpp | 32 +++++++++++++--------------- src/effects/native/flangereffect.h | 4 +++- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/control/controleffectknob.cpp b/src/control/controleffectknob.cpp index b509609aa0b6..169cade841d2 100644 --- a/src/control/controleffectknob.cpp +++ b/src/control/controleffectknob.cpp @@ -39,6 +39,6 @@ void ControlEffectKnob::setBehaviour(EffectManifestParameter::ControlHint type, } } else if (type == EffectManifestParameter::ControlHint::KNOB_LOGARITHMIC_INVERSE) { m_pControl->setBehavior( - new ControlLogPotmeterBehavior(dMinValue, dMaxValue, -40)); + new ControlLogInvPotmeterBehavior(dMinValue, dMaxValue, -40)); } } diff --git a/src/effects/native/flangereffect.cpp b/src/effects/native/flangereffect.cpp index 64f339084575..e97ecd501160 100644 --- a/src/effects/native/flangereffect.cpp +++ b/src/effects/native/flangereffect.cpp @@ -27,18 +27,17 @@ EffectManifest FlangerEffect::getManifest() { "A simple modulation effect, created by taking the input signal " "and mixing it with a delayed, pitch modulated copy of itself.")); - EffectManifestParameter* period = manifest.addParameter(); - period->setId("period"); - period->setName(QObject::tr("Period")); - period->setDescription(QObject::tr("Controls the period of the LFO (low frequency oscillator)\n" - "1/4 - 20 beats rounded to 1/2 beat if tempo is detected (decks and samplers) \n" - "0.05 - 20 seconds if no tempo is detected (mic & aux inputs, master mix)")); - period->setControlHint(EffectManifestParameter::ControlHint::KNOB_LINEAR); - period->setSemanticHint(EffectManifestParameter::SemanticHint::UNKNOWN); - period->setUnitsHint(EffectManifestParameter::UnitsHint::BEATS); - period->setMinimum(0.00); - period->setMaximum(20.00); - period->setDefault(1.00); + EffectManifestParameter* speed = manifest.addParameter(); + speed->setId("speed"); + speed->setName(QObject::tr("Speed")); + speed->setDescription(QObject::tr("Controls the speed of the LFO (low frequency oscillator)\n" + "32 - 1/4 beats rounded to 1/2 beat per lfo cycle if tempo is detected (decks and samplers) \n" + "1/32 - 4 Hz if no tempo is detected (mic & aux inputs, master mix)")); + speed->setControlHint(EffectManifestParameter::ControlHint::KNOB_LOGARITHMIC_INVERSE); + speed->setSemanticHint(EffectManifestParameter::SemanticHint::UNKNOWN); + speed->setMinimum(kMinLfoBeats); + speed->setMaximum(kMaxLfoBeats); + speed->setDefault(0.5); EffectManifestParameter* width = manifest.addParameter(); width->setId("width"); @@ -102,7 +101,7 @@ EffectManifest FlangerEffect::getManifest() { FlangerEffect::FlangerEffect(EngineEffect* pEffect, const EffectManifest& manifest) - : m_pPeriodParameter(pEffect->getParameterById("period")), + : m_pSpeedParameter(pEffect->getParameterById("speed")), m_pWidthParameter(pEffect->getParameterById("width")), m_pManualParameter(pEffect->getParameterById("manual")), m_pRegenParameter(pEffect->getParameterById("regen")), @@ -127,19 +126,18 @@ void FlangerEffect::processChannel(const ChannelHandle& handle, // TODO: remove assumption of stereo signal const int kChannels = 2; - // The parameter minimum is zero so the exact center of the knob is 2 beats. - double lfoPeriodParameter = m_pPeriodParameter->value(); + double lfoPeriodParameter = m_pSpeedParameter->value(); double lfoPeriodFrames; if (groupFeatures.has_beat_length_sec) { // lfoPeriodParameter is a number of beats - lfoPeriodParameter = std::max(roundToFraction(lfoPeriodParameter, 2.0), 1/4.0); + lfoPeriodParameter = std::max(roundToFraction(lfoPeriodParameter, 2.0), kMinLfoBeats); if (m_pTripletParameter->toBool()) { lfoPeriodParameter /= 3.0; } lfoPeriodFrames = lfoPeriodParameter * groupFeatures.beat_length_sec * sampleRate; } else { // lfoPeriodParameter is a number of seconds - lfoPeriodFrames = std::max(lfoPeriodParameter, 1/4.0) * sampleRate; + lfoPeriodFrames = std::max(lfoPeriodParameter, kMinLfoBeats) * sampleRate; } // When the period is changed, the position of the sound shouldn't diff --git a/src/effects/native/flangereffect.h b/src/effects/native/flangereffect.h index fad13a959491..d991c75b137c 100644 --- a/src/effects/native/flangereffect.h +++ b/src/effects/native/flangereffect.h @@ -17,6 +17,8 @@ constexpr double kMinDelayMs = 0.22; constexpr double kCenterDelayMs = (kMaxDelayMs - kMinDelayMs) / 2 + kMinDelayMs; constexpr double kMaxLfoWidthMs = kMaxDelayMs - kMinDelayMs; constexpr SINT kBufferLenth = static_cast(ceil(kMaxDelayMs)) * 96; // for 96 kHz +constexpr double kMinLfoBeats = 1/4.0; +constexpr double kMaxLfoBeats = 32.0; } // anonymous namespace struct FlangerGroupState { @@ -64,7 +66,7 @@ class FlangerEffect : public PerChannelEffectProcessor { return getId(); } - EngineEffectParameter* m_pPeriodParameter; + EngineEffectParameter* m_pSpeedParameter; EngineEffectParameter* m_pWidthParameter; EngineEffectParameter* m_pManualParameter; EngineEffectParameter* m_pRegenParameter; From 6ec565c2181b8ceecc57901c9be78beb500261c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Tue, 24 Oct 2017 17:28:29 +0200 Subject: [PATCH 17/31] Added long name for Regeneration parameter --- src/effects/native/flangereffect.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/effects/native/flangereffect.cpp b/src/effects/native/flangereffect.cpp index e97ecd501160..7ca62cd323d3 100644 --- a/src/effects/native/flangereffect.cpp +++ b/src/effects/native/flangereffect.cpp @@ -65,7 +65,8 @@ EffectManifest FlangerEffect::getManifest() { EffectManifestParameter* regen = manifest.addParameter(); regen->setId("regen"); - regen->setName(QObject::tr("Regen.")); + regen->setName(QObject::tr("Regeneration")); + regen->setShortName(QObject::tr("Regen")); regen->setDescription(QObject::tr("Controls how much of the delay output is feed back into the input.")); regen->setControlHint(EffectManifestParameter::ControlHint::KNOB_LINEAR); regen->setSemanticHint(EffectManifestParameter::SemanticHint::UNKNOWN); From 87d58631381f9285dcc99a13686671c8410c56f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Tue, 24 Oct 2017 17:30:39 +0200 Subject: [PATCH 18/31] default Regen to 0.25 --- src/effects/native/flangereffect.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/effects/native/flangereffect.cpp b/src/effects/native/flangereffect.cpp index 7ca62cd323d3..1dab6938cac6 100644 --- a/src/effects/native/flangereffect.cpp +++ b/src/effects/native/flangereffect.cpp @@ -71,7 +71,7 @@ EffectManifest FlangerEffect::getManifest() { regen->setControlHint(EffectManifestParameter::ControlHint::KNOB_LINEAR); regen->setSemanticHint(EffectManifestParameter::SemanticHint::UNKNOWN); regen->setUnitsHint(EffectManifestParameter::UnitsHint::UNKNOWN); - regen->setDefault(0.0); + regen->setDefault(0.25); regen->setMinimum(0.0); regen->setMaximum(1.0); From 2ba1e8138d3f07c7b1168b886484923f1b35856b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Tue, 24 Oct 2017 17:32:37 +0200 Subject: [PATCH 19/31] fix typo and code style --- src/effects/native/flangereffect.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/effects/native/flangereffect.cpp b/src/effects/native/flangereffect.cpp index 1dab6938cac6..065f0ad8fbbd 100644 --- a/src/effects/native/flangereffect.cpp +++ b/src/effects/native/flangereffect.cpp @@ -168,7 +168,7 @@ void FlangerEffect::processChannel(const ChannelHandle& handle, // With and Manual is limited by amount of amplitude that remains from width - // to kMaxDelaMS + // to kMaxDelayMs double width = m_pWidthParameter->value(); double manual = m_pManualParameter->value(); double maxManual = kCenterDelayMs + (kMaxLfoWidthMs - width) / 2; @@ -235,7 +235,7 @@ void FlangerEffect::processChannel(const ChannelHandle& handle, SampleUtil::clear(delayLeft, numSamples); SampleUtil::clear(delayRight, numSamples); pState->previousPeriodFrames = -1; - pState->prev_regen = -1; - pState->prev_mix = -1; + pState->prev_regen = -1; + pState->prev_mix = -1; } } From c52cfd797226be4e85c87de1dd74f38aa5b40eaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Tue, 24 Oct 2017 18:34:00 +0200 Subject: [PATCH 20/31] Added new rampingvalue helper class template --- src/effects/native/flangereffect.cpp | 36 +++++++++++----------------- src/effects/native/flangereffect.h | 1 + src/util/rampingvalue.h | 22 +++++++++++++++++ 3 files changed, 37 insertions(+), 22 deletions(-) create mode 100644 src/util/rampingvalue.h diff --git a/src/effects/native/flangereffect.cpp b/src/effects/native/flangereffect.cpp index 065f0ad8fbbd..fb0d4bf8bc8d 100644 --- a/src/effects/native/flangereffect.cpp +++ b/src/effects/native/flangereffect.cpp @@ -153,20 +153,16 @@ void FlangerEffect::processChannel(const ChannelHandle& handle, // independently in the loop below, so do not multiply lfoPeriodSamples by // the number of channels. - CSAMPLE_GAIN mix = m_pMixParameter->value(); - const CSAMPLE_GAIN mix_delta = (mix - pState->prev_mix) / - (numSamples / kChannels); - const CSAMPLE_GAIN mix_start = pState->prev_mix + mix_delta; + RampingValue mixRamped( + pState->prev_mix, mix, numSamples / kChannels); pState->prev_mix = mix; CSAMPLE_GAIN regen = m_pRegenParameter->value(); - const CSAMPLE_GAIN regen_delta = (regen - pState->prev_regen) / - (numSamples / kChannels); - const CSAMPLE_GAIN regen_start = pState->prev_regen + regen_delta; + RampingValue regenRamped( + pState->prev_regen, regen, numSamples / kChannels); pState->prev_regen = regen; - // With and Manual is limited by amount of amplitude that remains from width // to kMaxDelayMs double width = m_pWidthParameter->value(); @@ -175,29 +171,25 @@ void FlangerEffect::processChannel(const ChannelHandle& handle, double minManual = kCenterDelayMs - (kMaxLfoWidthMs - width) / 2; manual = math_clamp(manual, minManual, maxManual); - const double width_delta = (width - pState->prev_width) / - (numSamples / kChannels); - const double width_start = pState->prev_width + width_delta; + RampingValue widthRamped( + pState->prev_width, width, numSamples / kChannels); pState->prev_width = width; - const double manual_delta = (manual - pState->prev_manual) / - (numSamples / kChannels); - const double manual_start = pState->prev_manual + manual_delta; + RampingValue manualRamped( + pState->prev_manual, manual, numSamples / kChannels); pState->prev_manual = manual; CSAMPLE* delayLeft = pState->delayLeft; CSAMPLE* delayRight = pState->delayRight; + QList(); + for (unsigned int i = 0; i < numSamples; i += kChannels) { - CSAMPLE_GAIN mix_ramped = mix_start - + mix_delta * i / kChannels; - CSAMPLE_GAIN regen_ramped = regen_start - + regen_delta * i / kChannels; - CSAMPLE_GAIN width_ramped = width_start - + width_delta * i / kChannels; - CSAMPLE_GAIN manual_ramped = manual_start - + manual_delta * i / kChannels; + CSAMPLE_GAIN mix_ramped = mixRamped.getNext(); + CSAMPLE_GAIN regen_ramped = regenRamped.getNext(); + double width_ramped = widthRamped.getNext(); + double manual_ramped = manualRamped.getNext(); pState->lfoFrames++; if (pState->lfoFrames >= lfoPeriodFrames) { diff --git a/src/effects/native/flangereffect.h b/src/effects/native/flangereffect.h index d991c75b137c..76b711085abe 100644 --- a/src/effects/native/flangereffect.h +++ b/src/effects/native/flangereffect.h @@ -10,6 +10,7 @@ #include "util/defs.h" #include "util/sample.h" #include "util/types.h" +#include "util/rampingvalue.h" namespace { constexpr double kMaxDelayMs = 13.0; diff --git a/src/util/rampingvalue.h b/src/util/rampingvalue.h new file mode 100644 index 000000000000..51fa17f31437 --- /dev/null +++ b/src/util/rampingvalue.h @@ -0,0 +1,22 @@ +#ifndef MIXXX_UTIL_RAMPINGVALUE_H +#define MIXXX_UTIL_RAMPINGVALUE_H + + +template +class RampingValue { + public: + RampingValue(const T& initial, const T& final, int steps) { + m_value = initial; + m_increment = (final - initial) / steps; + } + + T getNext() { + return m_value += m_increment; + } + + private: + T m_value; + T m_increment; +}; + +#endif // MIXXX_UTIL_RAMPINGVALUE_H From 8defca726b709c1abc617159018535a1a4756ab0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Tue, 24 Oct 2017 18:40:17 +0200 Subject: [PATCH 21/31] default speed to 8 beats --- src/effects/native/flangereffect.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/effects/native/flangereffect.cpp b/src/effects/native/flangereffect.cpp index fb0d4bf8bc8d..c39f00a076b6 100644 --- a/src/effects/native/flangereffect.cpp +++ b/src/effects/native/flangereffect.cpp @@ -37,7 +37,7 @@ EffectManifest FlangerEffect::getManifest() { speed->setSemanticHint(EffectManifestParameter::SemanticHint::UNKNOWN); speed->setMinimum(kMinLfoBeats); speed->setMaximum(kMaxLfoBeats); - speed->setDefault(0.5); + speed->setDefault(8); EffectManifestParameter* width = manifest.addParameter(); width->setId("width"); From e076f3a21bc8f47c1260f509f59f8705027ea819 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Tue, 24 Oct 2017 20:15:36 +0200 Subject: [PATCH 22/31] tweak defauts and move super connection to mix parameter --- src/effects/native/flangereffect.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/effects/native/flangereffect.cpp b/src/effects/native/flangereffect.cpp index c39f00a076b6..29612b934bb3 100644 --- a/src/effects/native/flangereffect.cpp +++ b/src/effects/native/flangereffect.cpp @@ -43,11 +43,10 @@ EffectManifest FlangerEffect::getManifest() { width->setId("width"); width->setName(QObject::tr("Width")); width->setDescription(QObject::tr("Controls the delay amplitude of the LFO (low frequency oscillator).")); - width->setControlHint(EffectManifestParameter::ControlHint::KNOB_LINEAR); + width->setControlHint(EffectManifestParameter::ControlHint::KNOB_LOGARITHMIC); width->setSemanticHint(EffectManifestParameter::SemanticHint::UNKNOWN); width->setUnitsHint(EffectManifestParameter::UnitsHint::UNKNOWN); - width->setDefaultLinkType(EffectManifestParameter::LinkType::LINKED); - width->setDefault(kMaxLfoWidthMs); + width->setDefault(kMaxLfoWidthMs / 2); width->setMinimum(0.0); width->setMaximum(kMaxLfoWidthMs); @@ -82,6 +81,7 @@ EffectManifest FlangerEffect::getManifest() { mix->setControlHint(EffectManifestParameter::ControlHint::KNOB_LINEAR); mix->setSemanticHint(EffectManifestParameter::SemanticHint::UNKNOWN); mix->setUnitsHint(EffectManifestParameter::UnitsHint::UNKNOWN); + mix->setDefaultLinkType(EffectManifestParameter::LinkType::LINKED); mix->setDefault(1.0); mix->setMinimum(0.0); mix->setMaximum(1.0); From e4a8b3f26bf470056d95c564e26753d3a5d47739 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sun, 29 Oct 2017 21:42:45 +0100 Subject: [PATCH 23/31] clear the buffer with the right size --- src/effects/native/flangereffect.cpp | 4 ++-- src/effects/native/flangereffect.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/effects/native/flangereffect.cpp b/src/effects/native/flangereffect.cpp index 29612b934bb3..f7159121e72e 100644 --- a/src/effects/native/flangereffect.cpp +++ b/src/effects/native/flangereffect.cpp @@ -224,8 +224,8 @@ void FlangerEffect::processChannel(const ChannelHandle& handle, } if (enableState == EffectProcessor::DISABLING) { - SampleUtil::clear(delayLeft, numSamples); - SampleUtil::clear(delayRight, numSamples); + SampleUtil::clear(delayLeft, kBufferLenth); + SampleUtil::clear(delayRight, kBufferLenth); pState->previousPeriodFrames = -1; pState->prev_regen = -1; pState->prev_mix = -1; diff --git a/src/effects/native/flangereffect.h b/src/effects/native/flangereffect.h index 76b711085abe..23028b1a19fc 100644 --- a/src/effects/native/flangereffect.h +++ b/src/effects/native/flangereffect.h @@ -31,8 +31,8 @@ struct FlangerGroupState { prev_mix(0), prev_width(0), prev_manual(kCenterDelayMs) { - SampleUtil::applyGain(delayLeft, 0, kBufferLenth); - SampleUtil::applyGain(delayRight, 0, kBufferLenth); + SampleUtil::clear(delayLeft, kBufferLenth); + SampleUtil::clear(delayLeft, kBufferLenth); } CSAMPLE delayRight[kBufferLenth]; CSAMPLE delayLeft[kBufferLenth]; From f287686b7daca1db4c18e5e0626e3c0817a77939 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sun, 5 Nov 2017 01:32:43 +0100 Subject: [PATCH 24/31] Fix constexpr issue --- src/effects/native/flangereffect.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/effects/native/flangereffect.h b/src/effects/native/flangereffect.h index 23028b1a19fc..9ec444a4d373 100644 --- a/src/effects/native/flangereffect.h +++ b/src/effects/native/flangereffect.h @@ -17,7 +17,7 @@ constexpr double kMaxDelayMs = 13.0; constexpr double kMinDelayMs = 0.22; constexpr double kCenterDelayMs = (kMaxDelayMs - kMinDelayMs) / 2 + kMinDelayMs; constexpr double kMaxLfoWidthMs = kMaxDelayMs - kMinDelayMs; -constexpr SINT kBufferLenth = static_cast(ceil(kMaxDelayMs)) * 96; // for 96 kHz +const SINT kBufferLenth = static_cast(ceil(kMaxDelayMs)) * 96; // for 96 kHz constexpr double kMinLfoBeats = 1/4.0; constexpr double kMaxLfoBeats = 32.0; } // anonymous namespace From 141fb99548c2d56fabdf18429bee157403bfdc41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sun, 5 Nov 2017 01:34:11 +0100 Subject: [PATCH 25/31] fix include guard --- src/effects/native/balanceeffect.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/effects/native/balanceeffect.h b/src/effects/native/balanceeffect.h index 33453d7b841e..925ffbcad92c 100644 --- a/src/effects/native/balanceeffect.h +++ b/src/effects/native/balanceeffect.h @@ -1,4 +1,4 @@ -#ifndef PANEFFECT_H +#ifndef BALANCEEFFECT_H #define BALANCEEFFECT_H #include "effects/effectprocessor.h" From 4ed3a2d8740267ffa05a9dc533f4ae39ba500cfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sun, 5 Nov 2017 22:06:43 +0100 Subject: [PATCH 26/31] Fix the constexpr issue again --- src/effects/native/flangereffect.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/effects/native/flangereffect.h b/src/effects/native/flangereffect.h index 9ec444a4d373..b044a3f1a7a9 100644 --- a/src/effects/native/flangereffect.h +++ b/src/effects/native/flangereffect.h @@ -17,7 +17,8 @@ constexpr double kMaxDelayMs = 13.0; constexpr double kMinDelayMs = 0.22; constexpr double kCenterDelayMs = (kMaxDelayMs - kMinDelayMs) / 2 + kMinDelayMs; constexpr double kMaxLfoWidthMs = kMaxDelayMs - kMinDelayMs; -const SINT kBufferLenth = static_cast(ceil(kMaxDelayMs)) * 96; // for 96 kHz +// using + 1.0 instead of ceil() for Mac OS +constexpr SINT kBufferLenth = static_cast(kMaxDelayMs + 1.0) * 96; // for 96 kHz constexpr double kMinLfoBeats = 1/4.0; constexpr double kMaxLfoBeats = 32.0; } // anonymous namespace From 77789531b9adfccbc755fb0b132eae3a0d647ad3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Mon, 6 Nov 2017 21:52:30 +0100 Subject: [PATCH 27/31] Implement missing midi convert functions --- src/control/controlbehavior.cpp | 11 ++---- src/control/controlbehavior.h | 8 ++-- src/controllers/midi/midicontroller.cpp | 51 +++++++++++++------------ 3 files changed, 33 insertions(+), 37 deletions(-) diff --git a/src/control/controlbehavior.cpp b/src/control/controlbehavior.cpp index 50678e69c11a..9d39efdfbac0 100644 --- a/src/control/controlbehavior.cpp +++ b/src/control/controlbehavior.cpp @@ -12,10 +12,7 @@ double ControlNumericBehavior::valueToParameter(double dValue) { } double ControlNumericBehavior::midiToParameter(double midiValue) { - Q_UNUSED(midiValue); - DEBUG_ASSERT(false); - qWarning() << "midiToParameter not implemented"; - return 0; + return midiValue / 127.0; } double ControlNumericBehavior::parameterToValue(double dParam) { @@ -23,10 +20,8 @@ double ControlNumericBehavior::parameterToValue(double dParam) { } double ControlNumericBehavior::valueToMidiParameter(double dValue) { - Q_UNUSED(dValue); - DEBUG_ASSERT(false); - qWarning() << "valueToMidiParameter not implemented"; - return 0; + double dParam = valueToParameter(dValue); + return dParam * 127.0; } void ControlNumericBehavior::setValueFromMidi( diff --git a/src/control/controlbehavior.h b/src/control/controlbehavior.h index 4cece00a8129..9686794022f7 100644 --- a/src/control/controlbehavior.h +++ b/src/control/controlbehavior.h @@ -87,10 +87,10 @@ class ControlAudioTaperPotBehavior : public ControlPotmeterBehavior { ControlAudioTaperPotBehavior(double minDB, double maxDB, double neutralParameter); - double valueToParameter(double dValue); - double parameterToValue(double dParam); - double midiToParameter(double midiValue); - double valueToMidiParameter(double dValue); + double valueToParameter(double dValue) override; + double parameterToValue(double dParam) override; + double midiToParameter(double midiValue) override; + double valueToMidiParameter(double dValue) override; void setValueFromMidi( MidiOpCode o, double dParam, ControlDoublePrivate* pControl) override; diff --git a/src/controllers/midi/midicontroller.cpp b/src/controllers/midi/midicontroller.cpp index 23aa6994ef8e..8ebf383c6119 100644 --- a/src/controllers/midi/midicontroller.cpp +++ b/src/controllers/midi/midicontroller.cpp @@ -355,21 +355,22 @@ void MidiController::processInputMapping(const MidiInputMapping& mapping, pCO->setValueFromMidi(static_cast(opCode), newValue); } -double MidiController::computeValue(MidiOptions options, double _prevmidivalue, double _newmidivalue) { +double MidiController::computeValue( + MidiOptions options, double prevmidivalue, double newmidivalue) { double tempval = 0.; double diff = 0.; if (options.all == 0) { - return _newmidivalue; + return newmidivalue; } if (options.invert) { - return 127. - _newmidivalue; + return 127. - newmidivalue; } if (options.rot64 || options.rot64_inv) { - tempval = _prevmidivalue; - diff = _newmidivalue - 64.; + tempval = prevmidivalue; + diff = newmidivalue - 64.; if (diff == -1 || diff == 1) diff /= 16; else @@ -382,8 +383,8 @@ double MidiController::computeValue(MidiOptions options, double _prevmidivalue, } if (options.rot64_fast) { - tempval = _prevmidivalue; - diff = _newmidivalue - 64.; + tempval = prevmidivalue; + diff = newmidivalue - 64.; diff *= 1.5; tempval += diff; return (tempval < 0. ? 0. : (tempval > 127. ? 127.0 : tempval)); @@ -391,19 +392,19 @@ double MidiController::computeValue(MidiOptions options, double _prevmidivalue, if (options.diff) { //Interpret 7-bit signed value using two's compliment. - if (_newmidivalue >= 64.) - _newmidivalue = _newmidivalue - 128.; + if (newmidivalue >= 64.) + newmidivalue = newmidivalue - 128.; //Apply sensitivity to signed value. FIXME // if(sensitivity > 0) // _newmidivalue = _newmidivalue * ((double)sensitivity / 50.); //Apply new value to current value. - _newmidivalue = _prevmidivalue + _newmidivalue; + newmidivalue = prevmidivalue + newmidivalue; } if (options.selectknob) { //Interpret 7-bit signed value using two's compliment. - if (_newmidivalue >= 64.) - _newmidivalue = _newmidivalue - 128.; + if (newmidivalue >= 64.) + newmidivalue = newmidivalue - 128.; //Apply sensitivity to signed value. FIXME //if(sensitivity > 0) // _newmidivalue = _newmidivalue * ((double)sensitivity / 50.); @@ -411,11 +412,11 @@ double MidiController::computeValue(MidiOptions options, double _prevmidivalue, } if (options.button) { - _newmidivalue = _newmidivalue != 0; + newmidivalue = newmidivalue != 0; } if (options.sw) { - _newmidivalue = 1; + newmidivalue = 1; } if (options.spread64) { @@ -424,32 +425,32 @@ double MidiController::computeValue(MidiOptions options, double _prevmidivalue, // Uses a similar non-linear scaling formula as ControlTTRotary::getValueFromWidget() // but with added sensitivity adjustment. This formula is still experimental. - _newmidivalue = _newmidivalue - 64.; + newmidivalue = newmidivalue - 64.; //FIXME //double distance = _newmidivalue - 64.; // _newmidivalue = distance * distance * sensitivity / 50000.; //if (distance < 0.) - // _newmidivalue = -_newmidivalue; + // _newmidivalue = -newmidivalue; - //qDebug() << "Spread64: in " << distance << " out " << _newmidivalue; + //qDebug() << "Spread64: in " << distance << " out " << newmidivalue; } if (options.herc_jog) { - if (_newmidivalue > 64.) { - _newmidivalue -= 128.; + if (newmidivalue > 64.) { + newmidivalue -= 128.; } - _newmidivalue += _prevmidivalue; - //if (_prevmidivalue != 0.0) { qDebug() << "AAAAAAAAAAAA" << _prevmidivalue; } + newmidivalue += prevmidivalue; + //if (_prevmidivalue != 0.0) { qDebug() << "AAAAAAAAAAAA" << prevmidivalue; } } if (options.herc_jog_fast) { - if (_newmidivalue > 64.) { - _newmidivalue -= 128.; + if (newmidivalue > 64.) { + newmidivalue -= 128.; } - _newmidivalue = _prevmidivalue + (_newmidivalue * 3); + newmidivalue = prevmidivalue + (newmidivalue * 3); } - return _newmidivalue; + return newmidivalue; } void MidiController::receive(QByteArray data, mixxx::Duration timestamp) { From c3d91cc1a3646ad6f9e48a27ac1c1077583e7571 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Thu, 16 Nov 2017 23:07:58 +0100 Subject: [PATCH 28/31] make manual logarthmic --- src/effects/native/flangereffect.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/effects/native/flangereffect.cpp b/src/effects/native/flangereffect.cpp index f7159121e72e..f52cb08e585f 100644 --- a/src/effects/native/flangereffect.cpp +++ b/src/effects/native/flangereffect.cpp @@ -55,7 +55,7 @@ EffectManifest FlangerEffect::getManifest() { manual->setName(QObject::tr("Manual")); manual->setDescription(QObject::tr("Controls the delay offset of the LFO (low frequency oscillator).\n" "With width at zero, it allows to manual sweep over the entire delay range.")); - manual->setControlHint(EffectManifestParameter::ControlHint::KNOB_LINEAR); + manual->setControlHint(EffectManifestParameter::ControlHint::KNOB_LOGARITHMIC); manual->setSemanticHint(EffectManifestParameter::SemanticHint::UNKNOWN); manual->setUnitsHint(EffectManifestParameter::UnitsHint::UNKNOWN); manual->setDefault(kCenterDelayMs); From 78fe9fe42fad7b0c0b6ca7036a6af67a5c82e1ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Fri, 17 Nov 2017 00:51:30 +0100 Subject: [PATCH 29/31] Added gain correction --- src/effects/native/flangereffect.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/effects/native/flangereffect.cpp b/src/effects/native/flangereffect.cpp index f52cb08e585f..3fd7b6ad98ec 100644 --- a/src/effects/native/flangereffect.cpp +++ b/src/effects/native/flangereffect.cpp @@ -5,6 +5,10 @@ #include "util/math.h" namespace{ + +// Gain correction was verified with replay gain and default parameters +const double kGainCorrection = 1.4125375446227544; // 3 dB + inline CSAMPLE tanh_approx(CSAMPLE input) { // return tanhf(input); // 142ns for process; return input / (1 + input * input / (3 + input * input / 5)); // 119ns for process @@ -182,9 +186,7 @@ void FlangerEffect::processChannel(const ChannelHandle& handle, CSAMPLE* delayLeft = pState->delayLeft; CSAMPLE* delayRight = pState->delayRight; - QList(); - - for (unsigned int i = 0; i < numSamples; i += kChannels) { + for (unsigned int i = 0; i < numSamples; i += kChannels) { CSAMPLE_GAIN mix_ramped = mixRamped.getNext(); CSAMPLE_GAIN regen_ramped = regenRamped.getNext(); @@ -219,8 +221,9 @@ void FlangerEffect::processChannel(const ChannelHandle& handle, pState->delayPos = (pState->delayPos + 1) % kBufferLenth; - pOutput[i] = pInput[i] + mix_ramped * delayedSampleLeft; - pOutput[i+1] = pInput[i + 1] + mix_ramped * delayedSampleRight; + double gain = (1 - mix_ramped + kGainCorrection * mix_ramped); + pOutput[i] = (pInput[i] + mix_ramped * delayedSampleLeft) / gain; + pOutput[i + 1] = (pInput[i + 1] + mix_ramped * delayedSampleRight) / gain; } if (enableState == EffectProcessor::DISABLING) { From d492f574a350799f6319029beb26548d09b43e00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Fri, 17 Nov 2017 00:59:12 +0100 Subject: [PATCH 30/31] Fix typo to remove click noise when starting flanger --- src/effects/native/flangereffect.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/effects/native/flangereffect.cpp b/src/effects/native/flangereffect.cpp index 3fd7b6ad98ec..82ba0399f6b1 100644 --- a/src/effects/native/flangereffect.cpp +++ b/src/effects/native/flangereffect.cpp @@ -230,7 +230,7 @@ void FlangerEffect::processChannel(const ChannelHandle& handle, SampleUtil::clear(delayLeft, kBufferLenth); SampleUtil::clear(delayRight, kBufferLenth); pState->previousPeriodFrames = -1; - pState->prev_regen = -1; - pState->prev_mix = -1; + pState->prev_regen = 0; + pState->prev_mix = 0; } } From 4776def0ed1f77096fdf8b43bc8dadad9ef5c8c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Sch=C3=BCrmann?= Date: Sat, 18 Nov 2017 19:28:03 +0100 Subject: [PATCH 31/31] Fix typo in c-tor --- src/effects/native/flangereffect.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/effects/native/flangereffect.h b/src/effects/native/flangereffect.h index b044a3f1a7a9..642a5bc62c11 100644 --- a/src/effects/native/flangereffect.h +++ b/src/effects/native/flangereffect.h @@ -33,10 +33,10 @@ struct FlangerGroupState { prev_width(0), prev_manual(kCenterDelayMs) { SampleUtil::clear(delayLeft, kBufferLenth); - SampleUtil::clear(delayLeft, kBufferLenth); + SampleUtil::clear(delayRight, kBufferLenth); } - CSAMPLE delayRight[kBufferLenth]; CSAMPLE delayLeft[kBufferLenth]; + CSAMPLE delayRight[kBufferLenth]; unsigned int delayPos; unsigned int lfoFrames; double previousPeriodFrames;