Audacity 3.2.0
EqualizationCurvesList.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 EqualizationCurvesList.cpp
6
7 Mitch Golden
8 Vaughan Johnson (Preview)
9 Martyn Shaw (FIR filters, response curve, graphic EQ)
10
11 Paul Licameli split from Equalization.cpp
12
13 /*********************************************************************/
15#include "BasicUI.h"
16#include "EqualizationFilter.h"
17#include "Envelope.h"
18#include "SampleFormat.h"
19
20//
21// Set NEW curve selection (safe to call outside of the UI)
22//
24{
25 mParameters.mCurveName = mCurves[ curve ].Name;
26}
27
28//
29// Capture updated envelope
30//
32{
35 else
37}
38
40{
41 const auto &hiFreq = mParameters.mHiFreq;
42 const auto &drawMode = mParameters.mDrawMode;
43 auto &logEnvelope = mParameters.mLogEnvelope;
44
45 // Allocate and populate point arrays
46 size_t numPoints = env.GetNumberOfPoints();
47 Doubles when{ numPoints };
48 Doubles value{ numPoints };
49 env.GetPoints( when.get(), value.get(), numPoints );
50
51 // Clear the unnamed curve
52 int curve = mCurves.size() - 1;
53 mCurves[ curve ].points.clear();
54
55 if(lin)
56 {
57 // Copy and convert points
58 for (size_t point = 0; point < numPoints; point++)
59 {
60 double freq = when[ point ] * hiFreq;
61 double db = value[ point ];
62
63 // Add it to the curve
64 mCurves[ curve ].points.push_back( EQPoint( freq, db ) );
65 }
66 }
67 else
68 {
69 double loLog = log10( 20. );
70 double hiLog = log10( hiFreq );
71 double denom = hiLog - loLog;
72
73 // Copy and convert points
74 for (size_t point = 0; point < numPoints; point++)
75 {
76 double freq = pow( 10., ( ( when[ point ] * denom ) + loLog ));
77 double db = value[ point ];
78
79 // Add it to the curve
80 mCurves[ curve ].points.push_back( EQPoint( freq, db ) );
81 }
82 }
83
84 // Update unnamed curve (so it's there for next time)
85 //(done in a hurry, may not be the neatest -MJS)
86 if (!drawMode)
87 {
88 size_t numPoints = logEnvelope.GetNumberOfPoints();
89 Doubles when{ numPoints };
90 Doubles value{ numPoints };
91 logEnvelope.GetPoints(when.get(), value.get(), numPoints);
92 for (size_t i = 0, j = 0; j + 2 < numPoints; i++, j++)
93 {
94 if ((value[i] < value[i + 1] + .05) && (value[i] > value[i + 1] - .05) &&
95 (value[i + 1] < value[i + 2] + .05) && (value[i + 1] > value[i + 2] - .05))
96 { // within < 0.05 dB?
97 logEnvelope.Delete(j + 1);
98 numPoints--;
99 j--;
100 }
101 }
102 Select((int) mCurves.size() - 1);
103 }
104
105 // set 'unnamed' as the selected curve
106 Select( (int) mCurves.size() - 1 );
107}
108
109//
110// Make the passed curve index the active one
111//
113{
114 constexpr auto loFreqI = EqualizationFilter::loFreqI;
115
116 const auto &lin = mParameters.mLin;
117 const auto &hiFreq = mParameters.mHiFreq;
118
119 // Set current choice
120 wxASSERT( currentCurve < (int) mCurves.size() );
121 Select(currentCurve);
122
123 int numPoints = (int) mCurves[currentCurve].points.size();
124
125 auto &env = mParameters.ChooseEnvelope();
126 env.Flatten(0.);
127 env.SetTrackLen(1.0);
128
129 // Handle special case of no points.
130 if (numPoints == 0) {
131 ForceRecalc();
132 return;
133 }
134
135 double when, value;
136
137 // Handle special case 1 point.
138 if (numPoints == 1) {
139 // only one point, so ensure it is in range then return.
140 when = mCurves[currentCurve].points[0].Freq;
141 if (lin) {
142 when = when / hiFreq;
143 }
144 else { // log scale
145 // We don't go below loFreqI (20 Hz) in log view.
146 double loLog = log10((double)loFreqI);
147 double hiLog = log10(hiFreq);
148 double denom = hiLog - loLog;
149 when =
150 (log10(std::max<double>(loFreqI, when))
151 - loLog) / denom;
152 }
153 value = mCurves[currentCurve].points[0].dB;
154 env.Insert(std::min(1.0, std::max(0.0, when)), value);
155 ForceRecalc();
156 return;
157 }
158
159 // We have at least two points, so ensure they are in frequency order.
160 std::sort(mCurves[currentCurve].points.begin(),
161 mCurves[currentCurve].points.end());
162
163 if (mCurves[currentCurve].points[0].Freq < 0) {
164 // Corrupt or invalid curve, so bail.
165 ForceRecalc();
166 return;
167 }
168
169 if(lin) { // linear Hz scale
170 for(int pointCount = 0; pointCount < numPoints; pointCount++) {
171 when = mCurves[currentCurve].points[pointCount].Freq / hiFreq;
172 value = mCurves[currentCurve].points[pointCount].dB;
173 if(when <= 1) {
174 env.Insert(when, value);
175 if (when == 1)
176 break;
177 }
178 else {
179 // There are more points at higher freqs,
180 // so interpolate next one then stop.
181 when = 1.0;
182 double nextDB = mCurves[currentCurve].points[pointCount].dB;
183 if (pointCount > 0) {
184 double nextF = mCurves[currentCurve].points[pointCount].Freq;
185 double lastF = mCurves[currentCurve].points[pointCount-1].Freq;
186 double lastDB = mCurves[currentCurve].points[pointCount-1].dB;
187 value = lastDB +
188 ((nextDB - lastDB) *
189 ((hiFreq - lastF) / (nextF - lastF)));
190 }
191 else
192 value = nextDB;
193 env.Insert(when, value);
194 break;
195 }
196 }
197 }
198 else { // log Hz scale
199 double loLog = log10((double) loFreqI);
200 double hiLog = log10(hiFreq);
201 double denom = hiLog - loLog;
202 int firstAbove20Hz;
203
204 // log scale EQ starts at 20 Hz (threshold of hearing).
205 // so find the first point (if any) above 20 Hz.
206 for (firstAbove20Hz = 0; firstAbove20Hz < numPoints; firstAbove20Hz++) {
207 if (mCurves[currentCurve].points[firstAbove20Hz].Freq > loFreqI)
208 break;
209 }
210
211 if (firstAbove20Hz == numPoints) {
212 // All points below 20 Hz, so just use final point.
213 when = 0.0;
214 value = mCurves[currentCurve].points[numPoints-1].dB;
215 env.Insert(when, value);
216 ForceRecalc();
217 return;
218 }
219
220 if (firstAbove20Hz > 0) {
221 // At least one point is before 20 Hz and there are more
222 // beyond 20 Hz, so interpolate the first
223 double prevF = mCurves[currentCurve].points[firstAbove20Hz-1].Freq;
224 prevF = log10(std::max(1.0, prevF)); // log zero is bad.
225 double prevDB = mCurves[currentCurve].points[firstAbove20Hz-1].dB;
226 double nextF = log10(mCurves[currentCurve].points[firstAbove20Hz].Freq);
227 double nextDB = mCurves[currentCurve].points[firstAbove20Hz].dB;
228 when = 0.0;
229 value = nextDB - ((nextDB - prevDB) * ((nextF - loLog) / (nextF - prevF)));
230 env.Insert(when, value);
231 }
232
233 // Now get the rest.
234 for(int pointCount = firstAbove20Hz; pointCount < numPoints; pointCount++)
235 {
236 double flog = log10(mCurves[currentCurve].points[pointCount].Freq);
237 wxASSERT(mCurves[currentCurve].points[pointCount].Freq >= loFreqI);
238
239 when = (flog - loLog)/denom;
240 value = mCurves[currentCurve].points[pointCount].dB;
241 if(when <= 1.0) {
242 env.Insert(when, value);
243 }
244 else {
245 // This looks weird when adjusting curve in Draw mode if
246 // there is a point off-screen.
247
248 /*
249 // we have a point beyond fs/2. Insert it so that env code can use it.
250 // but just this one, we have no use for the rest
251 env.SetTrackLen(when); // can't Insert if the envelope isn't long enough
252 env.Insert(when, value);
253 break;
254 */
255
256 // interpolate the final point instead
257 when = 1.0;
258 if (pointCount > 0) {
259 double lastDB = mCurves[currentCurve].points[pointCount-1].dB;
260 double logLastF =
261 log10(mCurves[currentCurve].points[pointCount-1].Freq);
262 value = lastDB +
263 ((value - lastDB) *
264 ((log10(hiFreq) - logLastF) / (flog - logLastF)));
265 }
266 env.Insert(when, value);
267 break;
268 }
269 }
270 }
271 ForceRecalc();
272}
273
275{
276 setCurve((int) mCurves.size() - 1);
277}
278
279void EqualizationCurvesList::setCurve(const wxString &curveName)
280{
281 unsigned i = 0;
282 for( i = 0; i < mCurves.size(); i++ )
283 if( curveName == mCurves[ i ].Name )
284 break;
285 if( i == mCurves.size())
286 {
287 using namespace BasicUI;
289 XO("Requested curve not found, using 'unnamed'"),
290 MessageBoxOptions {}.IconStyle(Icon::Error));
291 setCurve();
292 }
293 else
294 setCurve( i );
295}
Toolkit-neutral facade for basic user interface services.
int min(int a, int b)
XO("Cut/Copy/Paste")
One point in a curve.
Piecewise linear or piecewise exponential function from double to double.
Definition: Envelope.h:72
size_t GetNumberOfPoints() const
Return number of points.
Definition: Envelope.cpp:723
void GetPoints(double *bufferWhen, double *bufferValue, int bufferLen) const
Returns the sets of when and value pairs.
Definition: Envelope.cpp:738
void Flatten(double value)
Definition: Envelope.cpp:140
MessageBoxResult ShowMessageBox(const TranslatableString &message, MessageBoxOptions options={})
Show a modal message box with either Ok or Yes and No, and optionally Cancel.
Definition: BasicUI.h:287
MessageBoxOptions && IconStyle(Icon style) &&
Definition: BasicUI.h:104
EqualizationFilter & mParameters
static constexpr int loFreqI
const Envelope & ChooseEnvelope() const