quantumVERB  1.0.0
A FOSS convolution reverb plugin
Equalizer.cpp
1 /*
2 ==============================================================================
3 
4 Equalizer.cpp
5 
6 ==============================================================================
7 */
8 
9 #include "Equalizer.h"
10 #include "PluginProcessor.h"
11 
12 namespace reverb {
13 
14  Equalizer::Equalizer(juce::AudioProcessor * processor, int numFilters)
15  : Task(processor)
16  {
17 
18  if (numFilters < 3) numFilters = 3;
19  if (numFilters > 6) numFilters = 6;
20 
21  filterSet.add(new LowShelfFilter(processor));
22 
23  for (int i = 0; i < numFilters - 2; i++)
24  {
25  filterSet.add(new PeakFilter(processor));
26  }
27 
28  filterSet.add(new HighShelfFilter(processor));
29 
30  for (int i = 0; i < numFilters; i++)
31  {
32  EQGains.push_back(1.0f);
33  }
34 
35  /*
36  * The following is the default configuration of the Equalizer class:
37  * all filter frequencies are equally spaced between 0 and 10000 Hz
38  * with a gain of 2, and a Q of 0.71 for the shelf filters and 4 for the peak filters.
39  */
40 
41  filterSet[0]->setQ(0.71);
42  filterSet[numFilters - 1]->setQ(0.71);
43 
44  for (int i = 0; i < numFilters; i++)
45  {
46  EQGains[i] = 2;
47  filterSet[i]->setGain(2);
48  filterSet[i]->setFrequency((i + 1) * 10000 / numFilters);
49 
50  if (i != 0 && i != numFilters - 1) filterSet[i]->setQ(4);
51  }
52 
53 
54 
55 
56  }
57 
58  //==============================================================================
59  /**
60  * @brief Read processor parameters and update block parameters as necessary
61  * @param [in] params the value tree containing the parameters
62  * @param [in] blockId the Id of the relevant parameter block
63  * @returns True if any parameters were changed, false otherwise.
64  */
65 
66  void Equalizer::updateParams(const juce::AudioProcessorValueTreeState& params,
67  const juce::String& blockId)
68  {
69  //Extract filter ID from block ID
70  int filterId = std::stoi(blockId.getLastCharacters(1).toStdString());
71 
72  if (filterId < 0 || filterId >= filterSet.size()) throw InvalidFilterException();
73 
74  // Gain
75  auto paramGain = params.getRawParameterValue(blockId + AudioProcessor::PID_FILTER_GAIN_SUFFIX);
76 
77  if (!paramGain)
78  {
79  throw std::invalid_argument("Parameter not found for gain factor in Filter block");
80  }
81 
82  if (*paramGain != EQGains[filterId])
83  {
84  EQGains[filterId] = *paramGain;
85  mustExec = true;
86  }
87 
88  filterSet[filterId]->updateParams(params, blockId);
89 
90  mustExec |= filterSet[filterId]->needsToRun();
91 
92  if (mustExec)
93  {
95  }
96 
97 
98  }
99 
100  /**
101  * @brief Processes the AudioBuffer input with the EQ filters
102  *
103  * @param [in] ir AudioBuffer to be processed
104  */
105 
107  {
108 
109  for (int i = 0; i < filterSet.size(); i++)
110  {
111  filterSet[i]->exec(ir);
112  }
113 
114  mustExec = false;
115 
116  return ir;
117  }
118 
119  void Equalizer::updateSampleRate(double sr) {
120  for (int i = 0; i < filterSet.size(); i++) {
121  filterSet[i]->updateSampleRate(sr);
122  }
123  }
124 
125  /**
126  * @brief Calibrates the individual filter gains so that the total gains are equal to the user defined values
127  *
128  * This function solves a linear equation system of N variables where N = number of filters in order to find the individual gains that, when stacked together,
129  * are equal to the user specified values at the frequencies of 0, 21000 Hz and at each single one of the peak filter center frequencies.
130  *
131  * The use of decibels is necessary for the gain stacks to behave linearly. The algorithm can be used iteratively and is detailed
132  * in the Standford University lecture note available at https://ccrma.stanford.edu/courses/424/handouts.2004/424_Handout22_Filters4_LectureNotes.pdf
133  *
134  * @throws ConvergenceException
135  */
136 
137 
139  {
140 
141 
142  //Update filter parameters before gain normalization
143 
144  const int dim = filterSet.size();
145 
146  std::vector<float> evalFrequencies(dim);
147 
148  //Set evaluation frequencies
149 
150  evalFrequencies[0] = 0;
151 
152  for (int i = 1; i < dim - 1; i++)
153  {
154  evalFrequencies[i] = filterSet[i]->frequency;
155  }
156 
157  evalFrequencies[(dim - 1)] = 21000;
158 
159  //Create Matrix objects
160 
161  juce::dsp::Matrix<float> B(dim, dim);
162  juce::dsp::Matrix<float> gamma(dim, 1);
163  juce::dsp::Matrix<float> lambda(dim, 1);
164 
165  float * B_data = B.getRawDataPointer();
166  float * gamma_data = gamma.getRawDataPointer();
167  float * lambda_data = lambda.getRawDataPointer();
168 
169  //Compute gamma column and set gains to 1 dB for B matrix
170 
171  for (int i = 0; i < dim; i++)
172  {
173  gamma_data[i] = Filter::todB(EQGains[i]);
174  filterSet[i]->setGain(Filter::invdB(1.0f));
175  }
176 
177 
178  bool unitaryGain = false;
179 
180  //Correction algorithm (5 iterations)
181 
182  for (int k = 0; k < 5; k++)
183  {
184 
185  memcpy(lambda_data, gamma_data, dim * sizeof(float));
186 
187  //Compute B matrix with new gains
188  for (int i = 0; i < dim; i++)
189  {
190 
191  for (int j = 0; j < dim; j++)
192  {
193 
194  B_data[j + B.getNumColumns() * i] = filterSet[j]->getdBAmplitude(evalFrequencies[i]);
195  }
196  }
197 
198  B.solve(lambda);
199 
200  for (int i = 0; i < dim; i++)
201  {
202 
203  if (std::abs(lambda_data[i]) < 0.001f)
204  {
205  unitaryGain = true;
206  }
207 
208  if (!(Filter::invdB(lambda_data[i] * Filter::todB(filterSet[i]->gainFactor)) > 0))
209  {
210  throw ConvergenceException();
211  }
212 
213 
214  filterSet[i]->setGain(Filter::invdB(lambda_data[i] * Filter::todB(filterSet[i]->gainFactor)));
215  }
216 
217  if (unitaryGain)
218  {
219  break;
220  }
221  }
222 
223 
224  }
225 
226  /**
227  * @brief Returns the Equalizer amplitude response in dB at a given frequency
228  *
229  * @param [in] freq Frequency at which the filter magnitude is evaluated
230  */
231 
232  float Equalizer::getdBAmplitude(float freq)
233  {
234  float dBAmplitude = 0;
235 
236  for (int i = 0; i < filterSet.size(); i++)
237  {
238  dBAmplitude += filterSet[i]->getdBAmplitude(freq);
239  }
240 
241  return dBAmplitude;
242 
243  }
244 
245  /**
246  * @brief Returns the number of filters in the equalizer
247  *
248  */
249 
251  {
252  return filterSet.size();
253  }
254 
255  bool Equalizer::needsToRun() const {
256 
257  return mustExec;
258  }
259 }
float getParam(const juce::AudioProcessorValueTreeState &params, const juce::String &blockId) const
Internal method used to get (and check) a parameter&#39;s value.
Definition: Task.h:111
void calibrateFilters()
Calibrates the individual filter gains so that the total gains are equal to the user defined values...
Definition: Equalizer.cpp:138
float getdBAmplitude(float freq)
Returns the Equalizer amplitude response in dB at a given frequency.
Definition: Equalizer.cpp:232
int getNumFilters()
Returns the number of filters in the equalizer.
Definition: Equalizer.cpp:250
virtual void updateSampleRate(double sr) override
Update sample rate for task block.
Definition: Equalizer.cpp:119
virtual bool needsToRun() const override
Tells caller whether block must be run for current block.
Definition: Equalizer.cpp:255
virtual AudioBlock exec(AudioBlock ir) override
Processes the AudioBuffer input with the EQ filters.
Definition: Equalizer.cpp:106
virtual void updateParams(const juce::AudioProcessorValueTreeState &params, const juce::String &blockId) override
Read processor parameters and update block parameters as necessary.
Definition: Equalizer.cpp:66