diff --git a/build/depends.py b/build/depends.py index ec4f75dd779..a62fe488dd9 100644 --- a/build/depends.py +++ b/build/depends.py @@ -704,6 +704,7 @@ def sources(self, build): "effects/native/bessel4lvmixeqeffect.cpp", "effects/native/bessel8lvmixeqeffect.cpp", "effects/native/threebandbiquadeqeffect.cpp", + "effects/native/biquadfullkilleqeffect.cpp", "effects/native/loudnesscontoureffect.cpp", "effects/native/graphiceqeffect.cpp", "effects/native/flangereffect.cpp", diff --git a/src/control/controlbehavior.cpp b/src/control/controlbehavior.cpp index c6e3534ab10..a435bf7d973 100644 --- a/src/control/controlbehavior.cpp +++ b/src/control/controlbehavior.cpp @@ -195,10 +195,12 @@ double ControlAudioTaperPotBehavior::parameterToValue(double dParam) { } } else if (dParam == m_neutralParameter) { dValue = 1.0; - } else if (dParam <= 1.0) { + } else if (dParam < 1.0) { // m_maxDB = 1 // 0 dB = m_neutralParame; dValue = db2ratio((dParam - m_neutralParameter) * m_maxDB / (1 - m_neutralParameter)); + } else { + dValue = db2ratio(m_maxDB); } //qDebug() << "ControlAudioTaperPotBehavior::parameterToValue" << "dValue =" << dValue << "dParam =" << dParam; return dValue; diff --git a/src/effects/native/bessel4lvmixeqeffect.cpp b/src/effects/native/bessel4lvmixeqeffect.cpp index 57a5bc3ccc3..6f9ee1f6971 100644 --- a/src/effects/native/bessel4lvmixeqeffect.cpp +++ b/src/effects/native/bessel4lvmixeqeffect.cpp @@ -121,15 +121,13 @@ void Bessel4LVMixEQEffect::processChannel(const ChannelHandle& handle, Q_UNUSED(handle); Q_UNUSED(groupFeatures); - double fLow; - double fMid; - double fHigh; if (enableState == EffectProcessor::DISABLING) { // Ramp to dry, when disabling, this will ramp from dry when enabling as well - fLow = 1.0; - fMid = 1.0; - fHigh = 1.0; + pState->processChannelAndPause(pInput, pOutput, numSamples); } else { + double fLow; + double fMid; + double fHigh; if (!m_pKillLow->toBool()) { fLow = m_pPotLow->value(); } else { @@ -145,9 +143,8 @@ void Bessel4LVMixEQEffect::processChannel(const ChannelHandle& handle, } else { fHigh = 0; } + pState->processChannel(pInput, pOutput, numSamples, sampleRate, + fLow, fMid, fHigh, + m_pLoFreqCorner->get(), m_pHiFreqCorner->get()); } - - pState->processChannel(pInput, pOutput, numSamples, sampleRate, - fLow, fMid, fHigh, - m_pLoFreqCorner->get(), m_pHiFreqCorner->get()); } diff --git a/src/effects/native/bessel8lvmixeqeffect.cpp b/src/effects/native/bessel8lvmixeqeffect.cpp index c350a5ca25e..e57a17b8443 100644 --- a/src/effects/native/bessel8lvmixeqeffect.cpp +++ b/src/effects/native/bessel8lvmixeqeffect.cpp @@ -121,15 +121,14 @@ void Bessel8LVMixEQEffect::processChannel(const ChannelHandle& handle, Q_UNUSED(handle); Q_UNUSED(groupFeatures); - double fLow; - double fMid; - double fHigh; + if (enableState == EffectProcessor::DISABLING) { // Ramp to dry, when disabling, this will ramp from dry when enabling as well - fLow = 1.0; - fMid = 1.0; - fHigh = 1.0; + pState->processChannelAndPause(pInput, pOutput, numSamples); } else { + double fLow; + double fMid; + double fHigh; if (!m_pKillLow->toBool()) { fLow = m_pPotLow->value(); } else { @@ -145,9 +144,8 @@ void Bessel8LVMixEQEffect::processChannel(const ChannelHandle& handle, } else { fHigh = 0; } + pState->processChannel( + pInput, pOutput, numSamples, sampleRate, fLow, fMid, fHigh, + m_pLoFreqCorner->get(), m_pHiFreqCorner->get()); } - - pState->processChannel(pInput, pOutput, numSamples, sampleRate, - fLow, fMid, fHigh, - m_pLoFreqCorner->get(), m_pHiFreqCorner->get()); } diff --git a/src/effects/native/biquadfullkilleqeffect.cpp b/src/effects/native/biquadfullkilleqeffect.cpp new file mode 100644 index 00000000000..d5c71c6add1 --- /dev/null +++ b/src/effects/native/biquadfullkilleqeffect.cpp @@ -0,0 +1,472 @@ +#include +#include "util/math.h" + +namespace { + +static const double kMinimumFrequency = 10.0; +static const double kMaximumFrequency = kStartupSamplerate / 2; +static const double kStartupMidFreq = 1100.0; +static const double kQBoost = 0.3; +static const double kQKill = 0.9; +static const double kQLowKillShelve = 0.4; +static const double kQHighKillShelve = 0.4; +static const double kKillGain = -23; +static const double kBesselStartRatio = 0.25; + + +double getCenterFrequency(double low, double high) { + double scaleLow = log10(low); + double scaleHigh = log10(high); + + double scaleCenter = (scaleHigh - scaleLow) / 2 + scaleLow; + return pow(10, scaleCenter); +} + +double knobValueToBiquadGainDb (double value, bool kill) { + if (kill) { + return kKillGain; + } + if (value > kBesselStartRatio) { + return ratio2db(value); + } + double startDB = ratio2db(kBesselStartRatio); + value = 1 - (value / kBesselStartRatio); + return (kKillGain - startDB) * value + startDB; + +} + +double knobValueToBesselRatio (double value, bool kill) { + if (kill) { + return 0.0; + } + return math_min(value / kBesselStartRatio, 1.0); +} + +} // anonymous namesspace + + +// static +QString BiquadFullKillEQEffect::getId() { + return "org.mixxx.effects.dbiquadquadeq"; +} + +// static +EffectManifest BiquadFullKillEQEffect::getManifest() { + EffectManifest manifest; + manifest.setId(getId()); + manifest.setName(QObject::tr("Biquad Full Kill Equalizer")); + manifest.setShortName(QObject::tr("BQ EQ/ISO")); + manifest.setAuthor("The Mixxx Team"); + manifest.setVersion("1.0"); + manifest.setDescription(QObject::tr( + "A 3-band Equalizer that combines an Equalizer and an Isolator circuit to offer gentle slopes and full kill.") + + " " + QObject::tr( + "To adjust frequency shelves see the Equalizer preferences.")); + manifest.setEffectRampsFromDry(true); + manifest.setIsMixingEQ(true); + + EffectManifestParameter* low = manifest.addParameter(); + low->setId("low"); + low->setName(QObject::tr("Low")); + low->setDescription(QObject::tr("Gain for Low Filter")); + low->setControlHint(EffectManifestParameter::CONTROL_KNOB_LOGARITHMIC); + low->setSemanticHint(EffectManifestParameter::SEMANTIC_UNKNOWN); + low->setUnitsHint(EffectManifestParameter::UNITS_UNKNOWN); + low->setNeutralPointOnScale(0.5); + low->setDefault(1.0); + low->setMinimum(0); + low->setMaximum(4.0); + + EffectManifestParameter* killLow = manifest.addParameter(); + killLow->setId("killLow"); + killLow->setName(QObject::tr("Kill Low")); + killLow->setDescription(QObject::tr("Kill the Low Filter")); + killLow->setControlHint(EffectManifestParameter::CONTROL_TOGGLE_STEPPING); + killLow->setSemanticHint(EffectManifestParameter::SEMANTIC_UNKNOWN); + killLow->setUnitsHint(EffectManifestParameter::UNITS_UNKNOWN); + killLow->setDefault(0); + killLow->setMinimum(0); + killLow->setMaximum(1); + + EffectManifestParameter* mid = manifest.addParameter(); + mid->setId("mid"); + mid->setName(QObject::tr("Mid")); + mid->setDescription(QObject::tr("Gain for Mid Filter")); + mid->setControlHint(EffectManifestParameter::CONTROL_KNOB_LOGARITHMIC); + mid->setSemanticHint(EffectManifestParameter::SEMANTIC_UNKNOWN); + mid->setUnitsHint(EffectManifestParameter::UNITS_UNKNOWN); + mid->setNeutralPointOnScale(0.5); + mid->setDefault(1.0); + mid->setMinimum(0); + mid->setMaximum(4.0); + + EffectManifestParameter* killMid = manifest.addParameter(); + killMid->setId("killMid"); + killMid->setName(QObject::tr("Kill Mid")); + killMid->setDescription(QObject::tr("Kill the Mid Filter")); + killMid->setControlHint(EffectManifestParameter::CONTROL_TOGGLE_STEPPING); + killMid->setSemanticHint(EffectManifestParameter::SEMANTIC_UNKNOWN); + killMid->setUnitsHint(EffectManifestParameter::UNITS_UNKNOWN); + killMid->setDefault(0); + killMid->setMinimum(0); + killMid->setMaximum(1); + + EffectManifestParameter* high = manifest.addParameter(); + high->setId("high"); + high->setName(QObject::tr("High")); + high->setDescription(QObject::tr("Gain for High Filter")); + high->setControlHint(EffectManifestParameter::CONTROL_KNOB_LOGARITHMIC); + high->setSemanticHint(EffectManifestParameter::SEMANTIC_UNKNOWN); + high->setUnitsHint(EffectManifestParameter::UNITS_UNKNOWN); + high->setNeutralPointOnScale(0.5); + high->setDefault(1.0); + high->setMinimum(0); + high->setMaximum(4.0); + + EffectManifestParameter* killHigh = manifest.addParameter(); + killHigh->setId("killHigh"); + killHigh->setName(QObject::tr("Kill High")); + killHigh->setDescription(QObject::tr("Kill the High Filter")); + killHigh->setControlHint(EffectManifestParameter::CONTROL_TOGGLE_STEPPING); + killHigh->setSemanticHint(EffectManifestParameter::SEMANTIC_UNKNOWN); + killHigh->setUnitsHint(EffectManifestParameter::UNITS_UNKNOWN); + killHigh->setDefault(0); + killHigh->setMinimum(0); + killHigh->setMaximum(1); + + return manifest; +} + +BiquadFullKillEQEffectGroupState::BiquadFullKillEQEffectGroupState() + : m_oldLowBoost(0), + m_oldMidBoost(0), + m_oldHighBoost(0), + m_oldLowKill(0), + m_oldMidKill(0), + m_oldHighKill(0), + m_oldLow(1.0), + m_oldMid(1.0), + m_oldHigh(1.0), + m_loFreqCorner(0), + m_highFreqCorner(0), + m_rampHoldOff(kRampDone), + m_groupDelay(0), + m_oldSampleRate(kStartupSamplerate) { + + m_pLowBuf = std::make_unique(MAX_BUFFER_LEN); + m_pBandBuf = std::make_unique(MAX_BUFFER_LEN); + m_pHighBuf = std::make_unique(MAX_BUFFER_LEN); + m_pTempBuf = std::make_unique(MAX_BUFFER_LEN); + + // Initialize the filters with default parameters + + m_lowBoost = std::make_unique( + kStartupSamplerate, kStartupLoFreq, kQBoost); + m_midBoost = std::make_unique( + kStartupSamplerate, kStartupMidFreq, kQBoost); + m_highBoost = std::make_unique( + kStartupSamplerate, kStartupHiFreq, kQBoost); + m_lowKill = std::make_unique( + kStartupSamplerate, kStartupLoFreq * 2, kQLowKillShelve); + m_midKill = std::make_unique( + kStartupSamplerate, kStartupMidFreq, kQKill); + m_highKill = std::make_unique( + kStartupSamplerate, kStartupHiFreq / 2, kQHighKillShelve); + m_lvMixIso = std::make_unique>(); + + setFilters(kStartupSamplerate, kStartupLoFreq, kStartupHiFreq); +} + +BiquadFullKillEQEffectGroupState::~BiquadFullKillEQEffectGroupState() { +} + +void BiquadFullKillEQEffectGroupState::setFilters( + int sampleRate, double lowFreqCorner, double highFreqCorner) { + + double lowCenter = getCenterFrequency(kMinimumFrequency, lowFreqCorner); + double midCenter = getCenterFrequency(lowFreqCorner, highFreqCorner); + double highCenter = getCenterFrequency(highFreqCorner, kMaximumFrequency); + + + m_lowBoost->setFrequencyCorners( + sampleRate, lowCenter, kQBoost, m_oldLowBoost); + m_midBoost->setFrequencyCorners( + sampleRate, midCenter, kQBoost, m_oldMidBoost); + m_highBoost->setFrequencyCorners( + sampleRate, highCenter, kQBoost, m_oldHighBoost); + m_lowKill->setFrequencyCorners( + sampleRate, lowCenter * 2, kQLowKillShelve, m_oldLowKill); + m_midKill->setFrequencyCorners( + sampleRate, midCenter, kQKill, m_oldMidKill); + m_highKill->setFrequencyCorners( + sampleRate, highCenter / 2, kQHighKillShelve, m_oldHighKill); + + m_lvMixIso->setFilters(sampleRate, lowFreqCorner, highFreqCorner); +} + +BiquadFullKillEQEffect::BiquadFullKillEQEffect(EngineEffect* pEffect, + const EffectManifest& manifest) + : m_pPotLow(pEffect->getParameterById("low")), + m_pPotMid(pEffect->getParameterById("mid")), + m_pPotHigh(pEffect->getParameterById("high")), + m_pKillLow(pEffect->getParameterById("killLow")), + m_pKillMid(pEffect->getParameterById("killMid")), + m_pKillHigh(pEffect->getParameterById("killHigh")) { + Q_UNUSED(manifest); + m_pLoFreqCorner = std::make_unique("[Mixer Profile]", "LoEQFrequency"); + m_pHiFreqCorner = std::make_unique("[Mixer Profile]", "HiEQFrequency"); +} + +BiquadFullKillEQEffect::~BiquadFullKillEQEffect() { +} + +void BiquadFullKillEQEffect::processChannel( + const ChannelHandle& handle, + BiquadFullKillEQEffectGroupState* pState, + const CSAMPLE* pInput, + CSAMPLE* pOutput, + const unsigned int numSamples, + const unsigned int sampleRate, + const EffectProcessor::EnableState enableState, + const GroupFeatureState& groupFeatures) { + Q_UNUSED(handle); + Q_UNUSED(groupFeatures); + + if (pState->m_oldSampleRate != sampleRate || + (pState->m_loFreqCorner != m_pLoFreqCorner->get()) || + (pState->m_highFreqCorner != m_pHiFreqCorner->get())) { + pState->m_loFreqCorner = m_pLoFreqCorner->get(); + pState->m_highFreqCorner = m_pHiFreqCorner->get(); + pState->m_oldSampleRate = sampleRate; + pState->setFilters(sampleRate, pState->m_loFreqCorner, pState->m_highFreqCorner); + } + + // Ramp to dry, when disabling, this will ramp from dry when enabling as well + double bqGainLow = 0; + double bqGainMid = 0; + double bqGainHigh = 0; + if (enableState != EffectProcessor::DISABLING) { + bqGainLow = knobValueToBiquadGainDb( + m_pPotLow->value(), m_pKillLow->toBool()); + bqGainMid = knobValueToBiquadGainDb( + m_pPotMid->value(), m_pKillMid->toBool()); + bqGainHigh = knobValueToBiquadGainDb( + m_pPotHigh->value(), m_pKillHigh->toBool()); + } + + int activeFilters = 0; + + if (bqGainLow > 0.0 || pState->m_oldLowBoost > 0.0) { + ++activeFilters; + } + if (bqGainLow < 0.0 || pState->m_oldLowKill < 0.0) { + ++activeFilters; + } + if (bqGainMid > 0.0 || pState->m_oldMidBoost > 0.0) { + ++activeFilters; + } + if (bqGainMid < 0.0 || pState->m_oldMidKill < 0.0) { + ++activeFilters; + } + if (bqGainHigh > 0.0 || pState->m_oldHighBoost > 0.0) { + ++activeFilters; + } + if (bqGainHigh < 0.0 || pState->m_oldHighKill < 0.0) { + ++activeFilters; + } + + QVarLengthArray inBuffer; + QVarLengthArray outBuffer; + + if (activeFilters % 2 == 0) { + inBuffer.append(pInput); + outBuffer.append(pState->m_pTempBuf->data()); + + inBuffer.append(pState->m_pTempBuf->data()); + outBuffer.append(pOutput); + + inBuffer.append(pOutput); + outBuffer.append(pState->m_pTempBuf->data()); + + inBuffer.append(pState->m_pTempBuf->data()); + outBuffer.append(pOutput); + + inBuffer.append(pOutput); + outBuffer.append(pState->m_pTempBuf->data()); + + inBuffer.append(pState->m_pTempBuf->data()); + outBuffer.append(pOutput); + } + else + { + inBuffer.append(pInput); + outBuffer.append(pOutput); + + inBuffer.append(pOutput); + outBuffer.append(pState->m_pTempBuf->data()); + + inBuffer.append(pState->m_pTempBuf->data()); + outBuffer.append(pOutput); + + inBuffer.append(pOutput); + outBuffer.append(pState->m_pTempBuf->data()); + + inBuffer.append(pState->m_pTempBuf->data()); + outBuffer.append(pOutput); + + inBuffer.append(pOutput); + outBuffer.append(pState->m_pTempBuf->data()); + } + + int bufIndex = 0; + + if (bqGainLow > 0.0 || pState->m_oldLowBoost > 0.0) { + if (bqGainLow != pState->m_oldLowBoost) { + double lowCenter = getCenterFrequency( + kMinimumFrequency, pState->m_loFreqCorner); + pState->m_lowBoost->setFrequencyCorners( + sampleRate, lowCenter, kQBoost, bqGainLow); + pState->m_oldLowBoost = bqGainLow; + } + if (bqGainLow > 0.0) { + pState->m_lowBoost->process( + inBuffer[bufIndex], outBuffer[bufIndex], numSamples); + } else { + pState->m_lowBoost->processAndPauseFilter( + inBuffer[bufIndex], outBuffer[bufIndex], numSamples); + } + ++bufIndex; + } else { + pState->m_lowBoost->pauseFilter(); + } + + + if (bqGainLow < 0.0 || pState->m_oldLowKill < 0.0) { + if (bqGainLow != pState->m_oldLowKill) { + double lowCenter = getCenterFrequency( + kMinimumFrequency, pState->m_loFreqCorner); + pState->m_lowKill->setFrequencyCorners( + sampleRate, lowCenter * 2, kQLowKillShelve, bqGainLow); + pState->m_oldLowKill = bqGainLow; + } + if (bqGainLow < 0.0) { + pState->m_lowKill->process( + inBuffer[bufIndex], outBuffer[bufIndex], numSamples); + } else { + pState->m_lowKill->processAndPauseFilter( + inBuffer[bufIndex], outBuffer[bufIndex], numSamples); + } + ++bufIndex; + } else { + pState->m_lowKill->pauseFilter(); + + } + + if (bqGainMid > 0.0 || pState->m_oldMidBoost > 0.0) { + if (bqGainMid != pState->m_oldMidBoost) { + double midCenter = getCenterFrequency( + pState->m_loFreqCorner, pState->m_highFreqCorner); + pState->m_midBoost->setFrequencyCorners( + sampleRate, midCenter, kQBoost, bqGainMid); + pState->m_oldMidBoost = bqGainMid; + } + if (bqGainMid > 0.0) { + pState->m_midBoost->process( + inBuffer[bufIndex], outBuffer[bufIndex], numSamples); + } else { + pState->m_midBoost->processAndPauseFilter( + inBuffer[bufIndex], outBuffer[bufIndex], numSamples); + } + ++bufIndex; + } else { + pState->m_midBoost->pauseFilter(); + } + + if (bqGainMid < 0.0 || pState->m_oldMidKill < 0.0) { + if (bqGainMid != pState->m_oldMidKill) { + double midCenter = getCenterFrequency( + pState->m_loFreqCorner, pState->m_highFreqCorner); + pState->m_midKill->setFrequencyCorners( + sampleRate, midCenter, kQKill, bqGainMid); + pState->m_oldMidKill = bqGainMid; + } + if (bqGainMid < 0.0) { + pState->m_midKill->process( + inBuffer[bufIndex], outBuffer[bufIndex], numSamples); + } else { + pState->m_midKill->processAndPauseFilter( + inBuffer[bufIndex], outBuffer[bufIndex], numSamples); + } + ++bufIndex; + } else { + pState->m_midKill->pauseFilter(); + } + + if (bqGainHigh > 0.0 || pState->m_oldHighBoost > 0.0) { + if (bqGainHigh != pState->m_oldHighBoost) { + double highCenter = getCenterFrequency( + pState->m_highFreqCorner, kMaximumFrequency); + pState->m_highBoost->setFrequencyCorners( + sampleRate, highCenter, kQBoost, bqGainHigh); + pState->m_oldHighBoost = bqGainHigh; + } + if (bqGainHigh > 0.0) { + pState->m_highBoost->process( + inBuffer[bufIndex], outBuffer[bufIndex], numSamples); + } else { + pState->m_highBoost->processAndPauseFilter( + inBuffer[bufIndex], outBuffer[bufIndex], numSamples); + } + ++bufIndex; + } else { + pState->m_highBoost->pauseFilter(); + } + + if (bqGainHigh < 0.0 || pState->m_oldHighKill < 0.0) { + if (bqGainHigh != pState->m_oldHighKill) { + double highCenter = getCenterFrequency( + pState->m_highFreqCorner, kMaximumFrequency); + pState->m_highKill->setFrequencyCorners( + sampleRate, highCenter / 2, kQHighKillShelve, bqGainHigh); + pState->m_oldHighKill = bqGainHigh; + } + if (bqGainHigh < 0.0) { + pState->m_highKill->process( + inBuffer[bufIndex], outBuffer[bufIndex], numSamples); + } else { + pState->m_highKill->processAndPauseFilter( + inBuffer[bufIndex], outBuffer[bufIndex], numSamples); + } + ++bufIndex; + } else { + pState->m_highKill->pauseFilter(); + } + + if (activeFilters == 0) { + SampleUtil::copy(pOutput, pInput, numSamples); + } + + if (enableState == EffectProcessor::DISABLING) { + pState->m_lowBoost->pauseFilter(); + pState->m_midBoost->pauseFilter(); + pState->m_highBoost->pauseFilter(); + pState->m_lowKill->pauseFilter(); + pState->m_midKill->pauseFilter(); + pState->m_highKill->pauseFilter(); + } + + if (enableState == EffectProcessor::DISABLING) { + pState->m_lvMixIso->processChannelAndPause(pOutput, pOutput, numSamples); + } else { + double fLow = knobValueToBesselRatio( + m_pPotLow->value(), m_pKillLow->toBool()); + double fMid = knobValueToBesselRatio( + m_pPotMid->value(), m_pKillMid->toBool()); + double fHigh = knobValueToBesselRatio( + m_pPotHigh->value(), m_pKillHigh->toBool()); + pState->m_lvMixIso->processChannel( + pInput, pOutput, numSamples, sampleRate, fLow, fMid, fHigh, + m_pLoFreqCorner->get(), m_pHiFreqCorner->get()); + } +} + diff --git a/src/effects/native/biquadfullkilleqeffect.h b/src/effects/native/biquadfullkilleqeffect.h new file mode 100644 index 00000000000..28d657cea17 --- /dev/null +++ b/src/effects/native/biquadfullkilleqeffect.h @@ -0,0 +1,101 @@ +#ifndef BIQUADFULLKILLEQEFFECT_H +#define BIQUADFULLKILLEQEFFECT_H + +#include "control/controlproxy.h" +#include "effects/effect.h" +#include "effects/effectprocessor.h" +#include "engine/effects/engineeffect.h" +#include "engine/effects/engineeffectparameter.h" +#include "engine/enginefilterbiquad1.h" +#include "engine/enginefilterbessel4.h" +#include "effects/native/lvmixeqbase.h" +#include "engine/enginefilterdelay.h" +#include "util/class.h" +#include "util/defs.h" +#include "util/sample.h" +#include "util/types.h" +#include "util/memory.h" +#include "util/samplebuffer.h" + +static const int kMaxDelay2 = 3300; // allows a 30 Hz filter at 97346; + +class BiquadFullKillEQEffectGroupState final { + public: + BiquadFullKillEQEffectGroupState(); + ~BiquadFullKillEQEffectGroupState(); + + void setFilters( + int sampleRate, double lowFreqCorner, double highFreqCorner); + + std::unique_ptr m_lowBoost; + std::unique_ptr m_midBoost; + std::unique_ptr m_highBoost; + std::unique_ptr m_lowKill; + std::unique_ptr m_midKill; + std::unique_ptr m_highKill; + std::unique_ptr> m_lvMixIso; + std::unique_ptr m_pLowBuf; + std::unique_ptr m_pBandBuf; + std::unique_ptr m_pHighBuf; + std::unique_ptr m_pTempBuf; + + + double m_oldLowBoost; + double m_oldMidBoost; + double m_oldHighBoost; + double m_oldLowKill; + double m_oldMidKill; + double m_oldHighKill; + double m_oldLow; + double m_oldMid; + double m_oldHigh; + + double m_loFreqCorner; + double m_highFreqCorner; + + int m_rampHoldOff; + int m_groupDelay; + + unsigned int m_oldSampleRate; +}; + +class BiquadFullKillEQEffect : public PerChannelEffectProcessor { + public: + BiquadFullKillEQEffect(EngineEffect* pEffect, const EffectManifest& manifest); + ~BiquadFullKillEQEffect() override; + + static QString getId(); + static EffectManifest getManifest(); + + void setFilters(int sampleRate, double lowFreqCorner, double highFreqCorner); + + // See effectprocessor.h + void processChannel(const ChannelHandle& handle, + BiquadFullKillEQEffectGroupState* pState, + const CSAMPLE* pInput, CSAMPLE *pOutput, + const unsigned int numSamples, + const unsigned int sampleRate, + const EffectProcessor::EnableState enableState, + const GroupFeatureState& groupFeatureState); + + private: + BiquadFullKillEQEffect(const BiquadFullKillEQEffect&) = delete; + void operator=(const BiquadFullKillEQEffect&) = delete; + + QString debugString() const { + return getId(); + } + + EngineEffectParameter* m_pPotLow; + EngineEffectParameter* m_pPotMid; + EngineEffectParameter* m_pPotHigh; + + EngineEffectParameter* m_pKillLow; + EngineEffectParameter* m_pKillMid; + EngineEffectParameter* m_pKillHigh; + + std::unique_ptr m_pLoFreqCorner; + std::unique_ptr m_pHiFreqCorner; +}; + +#endif // BIQUADFULLKILLEQEFFECT_H diff --git a/src/effects/native/lvmixeqbase.h b/src/effects/native/lvmixeqbase.h index 6f9729bb0f0..c8d9e384c80 100644 --- a/src/effects/native/lvmixeqbase.h +++ b/src/effects/native/lvmixeqbase.h @@ -158,6 +158,82 @@ class LVMixEQEffectGroupState { } } + void processChannelAndPause( + const CSAMPLE* pInput, CSAMPLE* pOutput, const int numSamples) { + + // Note: We do not call pauseFilter() here because this will introduce a + // buffer size-dependent start delay. During such start delay some unwanted + // frequencies are slipping though or wanted frequencies are damped. + // We know the exact group delay here so we can just hold off the ramping. + m_delay3->processAndPauseFilter(pInput, m_pHighBuf, numSamples); + + if (m_oldMid) { + m_delay2->processAndPauseFilter(pInput, m_pBandBuf, numSamples); + m_low2->processAndPauseFilter(m_pBandBuf, m_pBandBuf, numSamples); + } + + if (m_oldLow) { + m_low1->processAndPauseFilter(pInput, m_pLowBuf, numSamples); + } + + SampleUtil::copy3WithRampingGain(pOutput, + m_pLowBuf, m_oldLow, 0.0, + m_pBandBuf, m_oldMid, 0.0, + m_pHighBuf, m_oldHigh, 1.0, + numSamples); + } + + /* + + int copySamples = 0; + int rampingSamples = numSamples; + if ((fLow && !m_oldLow) || + (fMid && !m_oldMid) || + (fHigh && !m_oldHigh)) { + // we have just switched at least one filter on + // Hold off ramping for the group delay + if (m_rampHoldOff == kRampDone) { + // multiply the group delay * 2 to ensure that the filter is + // settled it is actually at a factor of 1,8 at default setting + m_rampHoldOff = m_groupDelay * 2; + // ensure that we have at least 128 samples for ramping + // (the smallest buffer, that suits for de-clicking) + int rampingSamples = numSamples - (m_rampHoldOff % numSamples); + if (rampingSamples < 128) { + m_rampHoldOff += rampingSamples; + } + } + + // ramping is done in one of the following calls if + // pState->m_rampHoldOff >= numSamples; + copySamples = math_min(m_rampHoldOff, numSamples); + m_rampHoldOff -= copySamples; + rampingSamples = numSamples - copySamples; + + SampleUtil::copy3WithGain(pOutput, + m_pLowBuf, m_oldLow, + m_pBandBuf, m_oldMid, + m_pHighBuf, m_oldHigh, + copySamples); + } + + if (rampingSamples) { + SampleUtil::copy3WithRampingGain(&pOutput[copySamples], + &m_pLowBuf[copySamples], m_oldLow, fLow, + &m_pBandBuf[copySamples], m_oldMid, fMid, + &m_pHighBuf[copySamples], m_oldHigh, fHigh, + rampingSamples); + + m_oldLow = fLow; + m_oldMid = fMid; + m_oldHigh = fHigh; + m_rampHoldOff = kRampDone; + + } + } + +*/ + private: LPF* m_low1; LPF* m_low2; diff --git a/src/effects/native/nativebackend.cpp b/src/effects/native/nativebackend.cpp index 676cd879e0f..15fadd8a2da 100644 --- a/src/effects/native/nativebackend.cpp +++ b/src/effects/native/nativebackend.cpp @@ -1,4 +1,4 @@ -#include + #include #include "effects/native/nativebackend.h" @@ -7,6 +7,8 @@ #include "effects/native/linkwitzriley8eqeffect.h" #include "effects/native/bessel8lvmixeqeffect.h" #include "effects/native/bessel4lvmixeqeffect.h" +#include +#include #include "effects/native/graphiceqeffect.h" #include "effects/native/filtereffect.h" #include "effects/native/moogladder4filtereffect.h" @@ -26,6 +28,7 @@ NativeBackend::NativeBackend(QObject* pParent) registerEffect(); registerEffect(); registerEffect(); + registerEffect(); // Compensations EQs registerEffect(); registerEffect(); diff --git a/src/effects/native/threebandbiquadeqeffect.cpp b/src/effects/native/threebandbiquadeqeffect.cpp index 91d99dd0c84..e53cfd447d4 100644 --- a/src/effects/native/threebandbiquadeqeffect.cpp +++ b/src/effects/native/threebandbiquadeqeffect.cpp @@ -15,7 +15,7 @@ static const double kQBoost = 0.3; static const double kQKill = 0.9; static const double kQKillShelve = 0.4; static const double kBoostGain = 12; -static const double kKillGain = 23; +static const double kKillGain = -23; double getCenterFrequency(double low, double high) { @@ -26,6 +26,20 @@ double getCenterFrequency(double low, double high) { return pow(10, scaleCenter); } +double knobValueToBiquadGainDb (double value, bool kill) { + if (kill) { + return kKillGain; + } + + double ret = value - 1; + if (value >= 0) { + ret *= kBoostGain; + } else { + ret *= -kKillGain; + } + return ret; +} + } // anonymous namesspace @@ -132,20 +146,25 @@ ThreeBandBiquadEQEffectGroupState::ThreeBandBiquadEQEffectGroupState() m_highFreqCorner(0), m_oldSampleRate(kStartupSamplerate) { - m_pBuf = SampleUtil::alloc(MAX_BUFFER_LEN); + m_pTempBuf = std::make_unique(MAX_BUFFER_LEN); // Initialize the filters with default parameters - m_lowBoost = std::make_unique(kStartupSamplerate , kStartupLoFreq, kQBoost); - m_midBoost = std::make_unique(kStartupSamplerate , kStartupMidFreq, kQBoost); - m_highBoost = std::make_unique(kStartupSamplerate , kStartupHiFreq, kQBoost); - m_lowCut = std::make_unique(kStartupSamplerate , kStartupLoFreq, kQKill); - m_midCut = std::make_unique(kStartupSamplerate , kStartupMidFreq, kQKill); - m_highCut = std::make_unique(kStartupSamplerate , kStartupHiFreq / 2, kQKillShelve); + m_lowBoost = std::make_unique( + kStartupSamplerate , kStartupLoFreq, kQBoost); + m_midBoost = std::make_unique( + kStartupSamplerate , kStartupMidFreq, kQBoost); + m_highBoost = std::make_unique( + kStartupSamplerate , kStartupHiFreq, kQBoost); + m_lowCut = std::make_unique( + kStartupSamplerate , kStartupLoFreq, kQKill); + m_midCut = std::make_unique( + kStartupSamplerate , kStartupMidFreq, kQKill); + m_highCut = std::make_unique( + kStartupSamplerate , kStartupHiFreq / 2, kQKillShelve); } ThreeBandBiquadEQEffectGroupState::~ThreeBandBiquadEQEffectGroupState() { - SampleUtil::free(m_pBuf); } void ThreeBandBiquadEQEffectGroupState::setFilters( @@ -208,170 +227,202 @@ void ThreeBandBiquadEQEffect::processChannel( pState->setFilters(sampleRate, pState->m_loFreqCorner, pState->m_highFreqCorner); } - float fLowBoost; - float fMidBoost; - float fHighBoost; - float fLowKill; - float fMidKill; - float fHighKill; - - if (enableState == EffectProcessor::DISABLING) { - // Ramp to dry, when disabling, this will ramp from dry when enabling as well - fLowBoost = 0; - fMidBoost = 0; - fHighBoost = 0; - fLowKill = 0; - fMidKill = 0; - fHighKill = 0; - } else { - float fLow = -1.f, fMid = -1.f, fHigh = -1.f; - if (!m_pKillLow->toBool()) { - fLow = m_pPotLow->value() -1; - } - if (!m_pKillMid->toBool()) { - fMid = m_pPotMid->value() -1; - } - if (!m_pKillHigh->toBool()) { - fHigh = m_pPotHigh->value() -1; - } - - if (fLow >= 0) { - fLowBoost = fLow * kBoostGain; - fLowKill = 0; - } else { - fLowBoost = 0; - fLowKill = fLow * kKillGain; - } - if (fMid >= 0) { - fMidBoost = fMid * kBoostGain; - fMidKill = 0; - } else { - fMidBoost = 0; - fMidKill = fMid * kKillGain; - } - if (fHigh >= 0) { - fHighBoost = fHigh * kBoostGain; - fHighKill = 0; - } else { - fHighBoost = 0; - fHighKill = fHigh * kKillGain; - } - } - - double lowCenter = getCenterFrequency(kMinimumFrequency, pState->m_loFreqCorner); - double midCenter = getCenterFrequency(pState->m_loFreqCorner, pState->m_highFreqCorner); - double highCenter = getCenterFrequency(pState->m_highFreqCorner, kMaximumFrequency); - - if (fLowBoost != pState->m_oldLowBoost) { - pState->m_lowBoost->setFrequencyCorners( - sampleRate, lowCenter, kQBoost, fLowBoost); - pState->m_oldLowBoost = fLowBoost; - } - if (fMidBoost != pState->m_oldMidBoost) { - pState->m_midBoost->setFrequencyCorners( - sampleRate, midCenter, kQBoost, fMidBoost); - pState->m_oldMidBoost = fMidBoost; - } - if (fHighBoost != pState->m_oldHighBoost) { - pState->m_highBoost->setFrequencyCorners( - sampleRate, highCenter, kQBoost, fHighBoost); - pState->m_oldHighBoost = fHighBoost; - } - if (fLowKill != pState->m_oldLowCut) { - pState->m_lowCut->setFrequencyCorners( - sampleRate, lowCenter, kQKill, fLowKill); - pState->m_oldLowCut = fLowKill; - } - if (fMidKill != pState->m_oldMidCut) { - pState->m_midCut->setFrequencyCorners( - sampleRate, midCenter, kQKill, fMidKill); - pState->m_oldMidCut = fMidKill; - } - if (fHighKill != pState->m_oldHighCut) { - pState->m_highCut->setFrequencyCorners( - sampleRate, highCenter / 2, kQKillShelve , fHighKill); - pState->m_oldHighCut = fHighKill; + // Ramp to dry, when disabling, this will ramp from dry when enabling as well + double bqGainLow = 0; + double bqGainMid = 0; + double bqGainHigh = 0; + if (enableState != EffectProcessor::DISABLING) { + bqGainLow = knobValueToBiquadGainDb( + m_pPotLow->value(), m_pKillLow->toBool()); + bqGainMid = knobValueToBiquadGainDb( + m_pPotMid->value(), m_pKillMid->toBool()); + bqGainHigh = knobValueToBiquadGainDb( + m_pPotHigh->value(), m_pKillHigh->toBool()); } int activeFilters = 0; - if (fLowBoost) { + if (bqGainLow > 0.0 || pState->m_oldLowBoost > 0.0) { ++activeFilters; } - if (fMidBoost) { + if (bqGainLow < 0.0 || pState->m_oldLowCut < 0.0) { ++activeFilters; } - if (fHighBoost) { + if (bqGainMid > 0.0 || pState->m_oldMidBoost > 0.0) { ++activeFilters; } - if (fLowKill) { + if (bqGainMid < 0.0 || pState->m_oldMidCut < 0.0) { ++activeFilters; } - if (fMidKill) { + if (bqGainHigh > 0.0 || pState->m_oldHighBoost > 0.0) { ++activeFilters; } - if (fHighKill) { + if (bqGainHigh < 0.0 || pState->m_oldHighCut < 0.0) { ++activeFilters; } - QVarLengthArray inBuffer; - QVarLengthArray outBuffer; + QVarLengthArray inBuffer; + QVarLengthArray outBuffer; - if (activeFilters == 2) { + if (activeFilters % 2 == 0) { inBuffer.append(pInput); - outBuffer.append(pState->m_pBuf); - inBuffer.append(pState->m_pBuf); + outBuffer.append(pState->m_pTempBuf->data()); + + inBuffer.append(pState->m_pTempBuf->data()); + outBuffer.append(pOutput); + + inBuffer.append(pOutput); + outBuffer.append(pState->m_pTempBuf->data()); + + inBuffer.append(pState->m_pTempBuf->data()); + outBuffer.append(pOutput); + + inBuffer.append(pOutput); + outBuffer.append(pState->m_pTempBuf->data()); + + inBuffer.append(pState->m_pTempBuf->data()); outBuffer.append(pOutput); } else { inBuffer.append(pInput); outBuffer.append(pOutput); + inBuffer.append(pOutput); - outBuffer.append(pState->m_pBuf); - inBuffer.append(pState->m_pBuf); + outBuffer.append(pState->m_pTempBuf->data()); + + inBuffer.append(pState->m_pTempBuf->data()); outBuffer.append(pOutput); + + inBuffer.append(pOutput); + outBuffer.append(pState->m_pTempBuf->data()); + + inBuffer.append(pState->m_pTempBuf->data()); + outBuffer.append(pOutput); + + inBuffer.append(pOutput); + outBuffer.append(pState->m_pTempBuf->data()); } int bufIndex = 0; - if (fLowBoost) { - pState->m_lowBoost->process(inBuffer[bufIndex], outBuffer[bufIndex], numSamples); + + if (bqGainLow > 0.0 || pState->m_oldLowBoost > 0.0) { + if (bqGainLow != pState->m_oldLowBoost) { + double lowCenter = getCenterFrequency( + kMinimumFrequency, pState->m_loFreqCorner); + pState->m_lowBoost->setFrequencyCorners( + sampleRate, lowCenter, kQBoost, bqGainLow); + pState->m_oldLowBoost = bqGainLow; + } + if (bqGainLow > 0.0) { + pState->m_lowBoost->process( + inBuffer[bufIndex], outBuffer[bufIndex], numSamples); + } else { + pState->m_lowBoost->processAndPauseFilter( + inBuffer[bufIndex], outBuffer[bufIndex], numSamples); + } ++bufIndex; } else { pState->m_lowBoost->pauseFilter(); } - if (fMidBoost) { - pState->m_midBoost->process(inBuffer[bufIndex], outBuffer[bufIndex], numSamples); + if (bqGainLow < 0.0 || pState->m_oldLowCut < 0.0) { + if (bqGainLow != pState->m_oldLowCut) { + double lowCenter = getCenterFrequency( + kMinimumFrequency, pState->m_loFreqCorner); + pState->m_lowCut->setFrequencyCorners( + sampleRate, lowCenter, kQKill, bqGainLow); + pState->m_oldLowCut = bqGainLow; + } + if (bqGainLow < 0.0) { + pState->m_lowCut->process( + inBuffer[bufIndex], outBuffer[bufIndex], numSamples); + } else { + pState->m_lowCut->processAndPauseFilter( + inBuffer[bufIndex], outBuffer[bufIndex], numSamples); + } ++bufIndex; } else { - pState->m_midBoost->pauseFilter(); + pState->m_lowCut->pauseFilter(); } - if (fHighBoost) { - pState->m_highBoost->process(inBuffer[bufIndex], outBuffer[bufIndex], numSamples); + if (bqGainMid > 0.0 || pState->m_oldMidBoost > 0.0) { + if (bqGainMid != pState->m_oldMidBoost) { + double midCenter = getCenterFrequency( + pState->m_loFreqCorner, pState->m_highFreqCorner); + pState->m_midBoost->setFrequencyCorners( + sampleRate, midCenter, kQBoost, bqGainMid); + pState->m_oldMidBoost = bqGainMid; + } + if (bqGainMid > 0.0) { + pState->m_midBoost->process( + inBuffer[bufIndex], outBuffer[bufIndex], numSamples); + } else { + pState->m_midBoost->processAndPauseFilter( + inBuffer[bufIndex], outBuffer[bufIndex], numSamples); + } ++bufIndex; } else { - pState->m_highBoost->pauseFilter(); + pState->m_midBoost->pauseFilter(); } - if (fLowKill) { - pState->m_lowCut->process(inBuffer[bufIndex], outBuffer[bufIndex], numSamples); + + if (bqGainMid < 0.0 || pState->m_oldMidCut < 0.0) { + if (bqGainMid != pState->m_oldMidCut) { + double midCenter = getCenterFrequency( + pState->m_loFreqCorner, pState->m_highFreqCorner); + pState->m_midCut->setFrequencyCorners( + sampleRate, midCenter, kQKill, bqGainMid); + pState->m_oldMidCut = bqGainMid; + } + if (bqGainMid < 0.0) { + pState->m_midCut->process( + inBuffer[bufIndex], outBuffer[bufIndex], numSamples); + } else { + pState->m_midCut->processAndPauseFilter( + inBuffer[bufIndex], outBuffer[bufIndex], numSamples); + } ++bufIndex; } else { - pState->m_lowCut->pauseFilter(); + pState->m_midCut->pauseFilter(); } - if (fMidKill) { - pState->m_midCut->process(inBuffer[bufIndex], outBuffer[bufIndex], numSamples); + if (bqGainHigh > 0.0 || pState->m_oldHighBoost > 0.0) { + if (bqGainHigh != pState->m_oldHighBoost) { + double highCenter = getCenterFrequency( + pState->m_highFreqCorner, kMaximumFrequency); + pState->m_highBoost->setFrequencyCorners( + sampleRate, highCenter, kQBoost, bqGainHigh); + pState->m_oldHighBoost = bqGainHigh; + } + if (bqGainHigh > 0.0) { + pState->m_highBoost->process( + inBuffer[bufIndex], outBuffer[bufIndex], numSamples); + } else { + pState->m_highBoost->processAndPauseFilter( + inBuffer[bufIndex], outBuffer[bufIndex], numSamples); + } ++bufIndex; } else { - pState->m_midCut->pauseFilter(); + pState->m_highBoost->pauseFilter(); } - if (fHighKill) { - pState->m_highCut->process(inBuffer[bufIndex], outBuffer[bufIndex], numSamples); + if (bqGainHigh < 0.0 || pState->m_oldHighCut < 0.0) { + if (bqGainHigh != pState->m_oldHighCut) { + double highCenter = getCenterFrequency( + pState->m_highFreqCorner, kMaximumFrequency); + pState->m_highCut->setFrequencyCorners( + sampleRate, highCenter / 2, kQKillShelve, bqGainHigh); + pState->m_oldHighCut = bqGainHigh; + } + if (bqGainHigh < 0.0) { + pState->m_highCut->process( + inBuffer[bufIndex], outBuffer[bufIndex], numSamples); + } else { + pState->m_highCut->processAndPauseFilter( + inBuffer[bufIndex], outBuffer[bufIndex], numSamples); + } ++bufIndex; } else { pState->m_highCut->pauseFilter(); diff --git a/src/effects/native/threebandbiquadeqeffect.h b/src/effects/native/threebandbiquadeqeffect.h index ee5195d74c9..5349ea79448 100644 --- a/src/effects/native/threebandbiquadeqeffect.h +++ b/src/effects/native/threebandbiquadeqeffect.h @@ -1,8 +1,6 @@ #ifndef THREEBANDBIQUADEQEFFECT_H #define THREEBANDBIQUADEQEFFECT_H -#include - #include "control/controlproxy.h" #include "effects/effect.h" #include "effects/effectprocessor.h" @@ -14,6 +12,7 @@ #include "util/sample.h" #include "util/types.h" #include "util/memory.h" +#include "util/samplebuffer.h" class ThreeBandBiquadEQEffectGroupState final { public: @@ -29,7 +28,7 @@ class ThreeBandBiquadEQEffectGroupState final { std::unique_ptr m_lowCut; std::unique_ptr m_midCut; std::unique_ptr m_highCut; - CSAMPLE* m_pBuf; + std::unique_ptr m_pTempBuf; double m_oldLowBoost; double m_oldMidBoost; double m_oldHighBoost; diff --git a/src/engine/enginefilterdelay.h b/src/engine/enginefilterdelay.h index 60165cc6c5a..8071338c568 100644 --- a/src/engine/enginefilterdelay.h +++ b/src/engine/enginefilterdelay.h @@ -12,7 +12,6 @@ class EngineFilterDelay : public EngineObjectConstIn { : m_delaySamples(0), m_oldDelaySamples(0), m_delayPos(0), - m_doRamping(false), m_doStart(false) { // Set the current buffers to 0 memset(m_buf, 0, sizeof(m_buf)); @@ -24,19 +23,18 @@ class EngineFilterDelay : public EngineObjectConstIn { // Set the current buffers to 0 if (!m_doStart) { memset(m_buf, 0, sizeof(m_buf)); + m_oldDelaySamples = 0; m_doStart = true; } } void setDelay(unsigned int delaySamples) { - m_oldDelaySamples = m_delaySamples; m_delaySamples = delaySamples; - m_doRamping = true; } virtual void process(const CSAMPLE* pIn, CSAMPLE* pOutput, const int iBufferSize) { - if (!m_doRamping) { + if (m_oldDelaySamples == m_delaySamples) { int delaySourcePos = (m_delayPos + SIZE - m_delaySamples) % SIZE; VERIFY_OR_DEBUG_ASSERT(delaySourcePos >= 0) { @@ -91,26 +89,37 @@ class EngineFilterDelay : public EngineObjectConstIn { // only ramp the second half of the buffer, because we do // the same in the IIR filter to wait for settling pOutput[i] = m_buf[oldDelaySourcePos]; - oldDelaySourcePos = (oldDelaySourcePos + 1) % SIZE; } else { - pOutput[i] = m_buf[delaySourcePos] * cross_mix; + pOutput[i] = m_buf[oldDelaySourcePos] * (1.0 - cross_mix); + pOutput[i] += m_buf[delaySourcePos] * cross_mix; delaySourcePos = (delaySourcePos + 1) % SIZE; - pOutput[i] += m_buf[oldDelaySourcePos] * (1.0 - cross_mix); - oldDelaySourcePos = (oldDelaySourcePos + 1) % SIZE; cross_mix += cross_inc; } + oldDelaySourcePos = (oldDelaySourcePos + 1) % SIZE; } - m_doRamping = false; + m_oldDelaySamples = m_delaySamples; } m_doStart = false; } + + // this is can be used instead off a final process() call before pause + // It fades to dry or 0 according to the m_startFromDry parameter + // it is an alternative for using pauseFillter() calls + void processAndPauseFilter(const CSAMPLE* pIn, CSAMPLE* pOutput, + const int iBufferSize) { + double oldDelay = m_delaySamples; + m_delaySamples = 0; + process(pIn, pOutput, iBufferSize); + m_delaySamples = oldDelay; + pauseFilter(); + } + protected: int m_delaySamples; int m_oldDelaySamples; int m_delayPos; double m_buf[SIZE]; - bool m_doRamping; bool m_doStart; }; diff --git a/src/preferences/dialog/dlgprefeq.cpp b/src/preferences/dialog/dlgprefeq.cpp index bc981db1329..41a32b5950f 100644 --- a/src/preferences/dialog/dlgprefeq.cpp +++ b/src/preferences/dialog/dlgprefeq.cpp @@ -31,7 +31,7 @@ const QString kConfigKey = "[Mixer Profile]"; const QString kEnableEqs = "EnableEQs"; const QString kEqsOnly = "EQsOnly"; const QString kSingleEq = "SingleEQEffect"; -const QString kDefaultEqId = "org.mixxx.effects.bessel8lvmixeq"; +const QString kDefaultEqId = "org.mixxx.effects.dbiquadquadeq"; const QString kDefaultMasterEqId = QString(); const QString kDefaultQuickEffectId = "org.mixxx.effects.filter";