summaryrefslogtreecommitdiffstats
path: root/src/effects/builtin/parametriceqeffect.cpp
blob: c58c6de042d99cbb4ac6588cb70972d06eb433cb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
#include "effects/builtin/parametriceqeffect.h"
#include "util/math.h"

namespace {
    constexpr int kBandCount = 2;
    constexpr double kDefaultCenter1 = 1000; // 1 kHz
    constexpr double kDefaultCenter2 = 3000; // 3 kHz
}

// static
QString ParametricEQEffect::getId() {
    return "org.mixxx.effects.parametriceq";
}

// static
EffectManifestPointer ParametricEQEffect::getManifest() {
    EffectManifestPointer pManifest(new EffectManifest());
    pManifest->setId(getId());
    pManifest->setName(QObject::tr("Parametric Equalizer"));
    pManifest->setShortName(QObject::tr("Param EQ"));
    pManifest->setAuthor("The Mixxx Team");
    pManifest->setVersion("1.0");
    pManifest->setDescription(QObject::tr(
        "An gentle 2-band parametric equalizer based on biquad filters.\n"
        "It is designed as a complement to the steep mixing equalizers."));
    pManifest->setEffectRampsFromDry(true);
    pManifest->setIsMasterEQ(true);

    EffectManifestParameterPointer gain1 = pManifest->addParameter();
    gain1->setId("gain1");
    gain1->setName(QObject::tr("Gain 1"));
    gain1->setShortName(QObject::tr("Gain 1"));
    gain1->setDescription(QObject::tr(
        "Gain for Filter 1"));
    gain1->setControlHint(EffectManifestParameter::ControlHint::KNOB_LINEAR);
    gain1->setSemanticHint(EffectManifestParameter::SemanticHint::UNKNOWN);
    gain1->setUnitsHint(EffectManifestParameter::UnitsHint::UNKNOWN);
    gain1->setNeutralPointOnScale(0.5);
    gain1->setDefault(0);
    gain1->setMinimum(-18);
    gain1->setMaximum(18); // dB

    EffectManifestParameterPointer q1 = pManifest->addParameter();
    q1->setId("q1");
    q1->setName(QObject::tr("Q 1"));
    q1->setShortName(QObject::tr("Q 1"));
    q1->setDescription(QObject::tr(
        "Controls the bandwidth of Filter 1.\n"
        "A lower Q affects a wider band of frequencies,\n"
        "a higher Q affects a narrower band of frequencies."));
    q1->setControlHint(EffectManifestParameter::ControlHint::KNOB_LINEAR);
    q1->setSemanticHint(EffectManifestParameter::SemanticHint::UNKNOWN);
    q1->setUnitsHint(EffectManifestParameter::UnitsHint::UNKNOWN);
    q1->setNeutralPointOnScale(0.5);
    q1->setDefault(1.75);
    q1->setMinimum(0.5);
    q1->setMaximum(3.0);

    EffectManifestParameterPointer center1 = pManifest->addParameter();
    center1->setId("center1");
    center1->setName(QObject::tr("Center 1"));
    center1->setShortName(QObject::tr("Center 1"));
    center1->setDescription(QObject::tr(
        "Center frequency for Filter 1, from 100 Hz to 14 kHz"));
    center1->setControlHint(EffectManifestParameter::ControlHint::KNOB_LOGARITHMIC);
    center1->setSemanticHint(EffectManifestParameter::SemanticHint::UNKNOWN);
    center1->setUnitsHint(EffectManifestParameter::UnitsHint::HERTZ);
    center1->setNeutralPointOnScale(0.5);
    center1->setDefault(kDefaultCenter1);
    center1->setMinimum(100);
    center1->setMaximum(14000);

    EffectManifestParameterPointer gain2 = pManifest->addParameter();
    gain2->setId("gain2");
    gain2->setName(QObject::tr("Gain 2"));
    gain2->setShortName(QObject::tr("Gain 2"));
    gain2->setDescription(QObject::tr(
        "Gain for Filter 2"));
    gain2->setControlHint(EffectManifestParameter::ControlHint::KNOB_LINEAR);
    gain2->setSemanticHint(EffectManifestParameter::SemanticHint::UNKNOWN);
    gain2->setUnitsHint(EffectManifestParameter::UnitsHint::UNKNOWN);
    gain2->setNeutralPointOnScale(0.5);
    gain2->setDefault(0);
    gain2->setMinimum(-18);
    gain2->setMaximum(18); // dB

    EffectManifestParameterPointer q2 = pManifest->addParameter();
    q2->setId("q2");
    q2->setName(QObject::tr("Q 2"));
    q2->setShortName(QObject::tr("Q 2"));
    q2->setDescription(QObject::tr(
        "Controls the bandwidth of Filter 2.\n"
        "A lower Q affects a wider band of frequencies,\n"
        "a higher Q affects a narrower band of frequencies."));
    q2->setControlHint(EffectManifestParameter::ControlHint::KNOB_LINEAR);
    q2->setSemanticHint(EffectManifestParameter::SemanticHint::UNKNOWN);
    q2->setUnitsHint(EffectManifestParameter::UnitsHint::UNKNOWN);
    q2->setNeutralPointOnScale(0.5);
    q2->setDefault(1.75);
    q2->setMinimum(0.5);
    q2->setMaximum(3.0);

    EffectManifestParameterPointer center2 = pManifest->addParameter();
    center2->setId("center2");
    center2->setName(QObject::tr("Center 2"));
    center2->setShortName(QObject::tr("Center 2"));
    center2->setDescription(QObject::tr(
        "Center frequency for Filter 2, from 100 Hz to 14 kHz"));
    center2->setControlHint(EffectManifestParameter::ControlHint::KNOB_LOGARITHMIC);
    center2->setSemanticHint(EffectManifestParameter::SemanticHint::UNKNOWN);
    center2->setUnitsHint(EffectManifestParameter::UnitsHint::HERTZ);
    center2->setNeutralPointOnScale(0.5);
    center2->setDefault(kDefaultCenter2);
    center2->setMinimum(100);
    center2->setMaximum(14000);

    return pManifest;
}

ParametricEQEffectGroupState::ParametricEQEffectGroupState(
      const mixxx::EngineParameters& bufferParameters)
      : EffectState(bufferParameters) {
    for (int i = 0; i < kBandCount; i++) {
        m_oldGain.append(1.0);
        m_oldQ.append(1.75);
    }

    m_oldCenter.append(kDefaultCenter1);
    m_oldCenter.append(kDefaultCenter2);

    // Initialize the filters with default parameters
    for (int i = 0; i < kBandCount; i++) {
        m_bands.push_back(std::make_unique<EngineFilterBiquad1Peaking>(
                bufferParameters.sampleRate(), m_oldCenter[i], m_oldQ[i]));
    }
}

void ParametricEQEffectGroupState::setFilters(int sampleRate) {
    for (int i = 0; i < kBandCount; i++) {
        m_bands[i]->setFrequencyCorners(
                sampleRate, m_oldCenter[i], m_oldQ[i], m_oldGain[i]);
    }
}

ParametricEQEffect::ParametricEQEffect(EngineEffect* pEffect)
        : m_oldSampleRate(44100) {
    m_pPotGain.append(pEffect->getParameterById("gain1"));
    m_pPotQ.append(pEffect->getParameterById("q1"));
    m_pPotCenter.append(pEffect->getParameterById("center1"));
    m_pPotGain.append(pEffect->getParameterById("gain2"));
    m_pPotQ.append(pEffect->getParameterById("q2"));
    m_pPotCenter.append(pEffect->getParameterById("center2"));
}

ParametricEQEffect::~ParametricEQEffect() {
}

void ParametricEQEffect::processChannel(const ChannelHandle& handle,
                                     ParametricEQEffectGroupState* pState,
                                     const CSAMPLE* pInput, CSAMPLE* pOutput,
                                     const mixxx::EngineParameters& bufferParameters,
                                     const EffectEnableState enableState,
                                     const GroupFeatureState& groupFeatures) {
    Q_UNUSED(handle);
    Q_UNUSED(groupFeatures);

    // If the sample rate has changed, initialize the filters using the new
    // sample rate
    if (m_oldSampleRate != bufferParameters.sampleRate()) {
        m_oldSampleRate = bufferParameters.sampleRate();
        pState->setFilters(bufferParameters.sampleRate());
    }

    CSAMPLE_GAIN fGain[2];
    CSAMPLE_GAIN fQ[2];
    CSAMPLE_GAIN fCenter[2];

    for (int i = 0; i < kBandCount; i++) {
        if (enableState == EffectEnableState::Disabling) {
            // Ramp to dry, when disabling, this will ramp from dry when enabling as well
            fGain[i] = 1.0;
        } else {
            fGain[i] = m_pPotGain[i]->value();
        }
        fQ[i] = m_pPotQ[i]->value();
        fCenter[i] = m_pPotCenter[i]->value();
        if (fGain[i] != pState->m_oldGain[i] ||
                fQ[i] != pState->m_oldQ[i] ||
                fCenter[i] != pState->m_oldCenter[i]) {
            pState->m_bands[i]->setFrequencyCorners(
                    bufferParameters.sampleRate(), fCenter[i], fQ[i], fGain[i]);
        }
    }

    if (fGain[0]) {
        pState->m_bands[0]->process(pInput, pOutput, bufferParameters.samplesPerBuffer());
        if (fGain[1]) {
            pState->m_bands[1]->process(pOutput, pOutput, bufferParameters.samplesPerBuffer());
        } else {
            pState->m_bands[1]->pauseFilter();
        }
    } else {
        pState->m_bands[0]->pauseFilter();
        if (fGain[1]) {
            pState->m_bands[1]->process(pInput, pOutput, bufferParameters.samplesPerBuffer());
        } else {
            pState->m_bands[1]->pauseFilter();
            SampleUtil::copy(pOutput, pInput, bufferParameters.samplesPerBuffer());
        }
    }

    for (int i = 0; i < kBandCount; i++) {
        pState->m_oldGain[i] = fGain[i];
        pState->m_oldQ[i] = fQ[i];
        pState->m_oldCenter[i]