Audacity 3.2.0
EqualizationBase.cpp
Go to the documentation of this file.
1#include "EqualizationBase.h"
2#include "BasicUI.h"
4#include "WaveClip.h"
5#include "WaveTrack.h"
6
8{
11 // CurveName,
13 // Pretty sure the interpolation name shouldn't have been interpreted when
14 // specified in chains, but must keep it that way for compatibility.
16 parameters { [](EqualizationBase& effect, EffectSettings&,
17 EqualizationParameters& params, bool updating) {
18 constexpr auto nInterpolations =
20 if (updating)
21 {
22 if (params.mInterp >= nInterpolations)
23 params.mInterp -= nInterpolations;
24 }
25 return true;
26 } };
27 return parameters;
28}
29
31// EqualizationBase
32//----------------------------------------------------------------------------
33
35
37 : mParameters { GetDefinition() }
38 , mOptions { Options }
39{
40 auto& hiFreq = mParameters.mHiFreq;
41 auto& curves = mCurvesList.mCurves;
42
43 Parameters().Reset(*this);
44
46
47 // Load the EQ curves
48 EQCurveReader { curves, GetName(), mOptions }.LoadCurves();
49
50 // Note: initial curve is set in TransferDataToWindow
51
52 // double loLog = log10(mLoFreq);
53 // double stepLog = (log10(hiFreq) - loLog)/((double)NUM_PTS-1.);
54
55 // We expect these Hi and Lo frequencies to be overridden by Init().
56 // Don't use inputTracks(). See bug 2321.
57#if 0
58 auto trackList = inputTracks();
59 const auto t = trackList
60 ? *trackList->Any<const WaveTrack>().first
61 : nullptr
62 ;
63 hiFreq =
64 (t
65 ? t->GetRate()
67 / 2.0;
68#endif
69 hiFreq = mProjectRate / 2.0;
70}
71
73{
74}
75
76// ComponentInterface implementation
77
79{
80 return XO("Adjusts the volume levels of particular frequencies");
81}
82
84{
85 // Bug 2509: Must use _ and not space in names.
87 return L"Graphic_EQ";
89 return L"Filter_Curve_EQ";
90 return L"Equalization";
91}
92
93// EffectDefinitionInterface implementation
94
96{
97 return EffectTypeProcess;
98}
99
101 ConstSettingsVisitor& visitor, const EffectSettings& settings) const
102{
103 const auto& curves = mCurvesList.mCurves;
105
106 // Curve point parameters -- how many isn't known statically
107 if (dynamic_cast<ShuttleGetAutomation*>(&visitor))
108 {
109 int numPoints = curves[0].points.size();
110 int point;
111 for (point = 0; point < numPoints; point++)
112 {
113 const wxString nameFreq = wxString::Format("f%i", point);
114 const wxString nameVal = wxString::Format("v%i", point);
115 visitor.Define(
116 curves[0].points[point].Freq, nameFreq, 0.0, 0.0, 0.0, 0.0);
117 visitor.Define(
118 curves[0].points[point].dB, nameVal, 0.0, 0.0, 0.0, 0.0);
119 }
120 }
121 return true;
122}
123
126{
127 auto& curves = mCurvesList.mCurves;
129
130 // Curve point parameters -- how many isn't known statically
131 {
132 curves[0].points.clear();
133
134 for (int i = 0; i < 200; i++)
135 {
136 const wxString nameFreq = wxString::Format("f%i", i);
137 const wxString nameVal = wxString::Format("v%i", i);
138 double f = -1000.0;
139 double d = 0.0;
140 visitor.Define(f, nameFreq, 0.0, -10000.0, 1000000.0, 0.0);
141 visitor.Define(d, nameVal, 0.0, -10000.0, 10000.0, 0.0);
142 if (f <= 0.0)
143 break;
144 curves[0].points.push_back(EQPoint(f, d));
145 }
147 }
148 return true;
149}
150
153{
154 // To do: externalize state so const_cast isn't needed
155 if (!const_cast<EqualizationBase&>(*this).DoLoadFactoryDefaults(settings))
156 return {};
157 return { nullptr };
158}
159
162{
165}
166
167// Constants determining who the prests are for.
168const bool kCURVE = false;
169const bool kBOTH = true;
170
171static const struct
172{
173 const bool bForBoth; // more extended set is used for Filter EQ
174 // See Bug 2254 for rationale.
176 const wxChar* values;
178 [] = {
179 { kCURVE, XO("100Hz Rumble"),
180 wxT(
181 "f0=\"20.0\" v0=\"-80.0\" f1=\"49.237316986327\" v1=\"-33.107692718506\" f2=\"54.196034330446\" v2=\"-29.553844451904\" f3=\"88.033573501041\" v3=\"-6.923076629639\" f4=\"95.871851182279\" v4=\"-4.523078918457\" f5=\"108.957037410504\" v5=\"-1.938461303711\" f6=\"123.828171198057\" v6=\"-0.73846244812\" f7=\"149.228077614658\" v7=\"-0.092308044434\"") },
182 { kCURVE, XO("AM Radio"),
183 wxT(
184 "f0=\"20.0\" v0=\"-63.67\" f1=\"31.0\" v1=\"-33.219\" f2=\"50.0\" v2=\"-3.01\" f3=\"63.0\" v3=\"-0.106\" f4=\"100.0\" v4=\"0.0\" f5=\"2500.0\" v5=\"0.0\" f6=\"4000.0\" v6=\"-0.614\" f7=\"5000.0\" v7=\"-8.059\" f8=\"8000.0\" v8=\"-39.981\" f9=\"20000.0\" v9=\"-103.651\" f10=\"48000.0\" v10=\"-164.485\"") },
185 { kBOTH, XO("Bass Boost"),
186 wxT("f0=\"100.0\" v0=\"9.0\" f1=\"500.0\" v1=\"0.0\"") },
187 { kBOTH, XO("Bass Cut"),
188 wxT("f0=\"150.0\" v0=\"-50.0\" f1=\"300.0\" v1=\"0.0\"") },
189 { kCURVE, XO("Low rolloff for speech"),
190 wxT(
191 "f0=\"50.0\" v0=\"-120.0\" f1=\"60.0\" v1=\"-50.0\" f2=\"65.0\" v2=\"-24.0\" f3=\"70.0\" v3=\"-12.0\" f4=\"80.0\" v4=\"-4.0\" f5=\"90.0\" v5=\"-1.0\" f6=\"100.0\" v6=\"0.0\"") },
192 { kBOTH, XO("RIAA"), wxT("f0=\"20.0\" v0=\"19.274\" f1=\"25.0\" v1=\"18.954\" f2=\"31.0\" v2=\"18.516\" f3=\"40.0\" v3=\"17.792\" f4=\"50.0\" v4=\"16.946\" f5=\"63.0\" v5=\"15.852\" f6=\"80.0\" v6=\"14.506\" f7=\"100.0\" v7=\"13.088\" f8=\"125.0\" v8=\"11.563\" f9=\"160.0\" v9=\"9.809\" f10=\"200.0\" v10=\"8.219\" f11=\"250.0\" v11=\"6.677\" f12=\"315.0\" v12=\"5.179\" f13=\"400.0\" v13=\"3.784\" f14=\"500.0\" v14=\"2.648\" f15=\"630.0\" v15=\"1.642\" f16=\"800.0\" v16=\"0.751\" f17=\"1000.0\" v17=\"0.0\" f18=\"1250.0\" v18=\"-0.744\" f19=\"1600.0\" v19=\"-1.643\" f20=\"2000.0\" v20=\"-2.589\" f21=\"2500.0\" v21=\"-3.7\" f22=\"3150.0\" v22=\"-5.038\" f23=\"4000.0\" v23=\"-6.605\" f24=\"5000.0\" v24=\"-8.21\" f25=\"6300.0\" v25=\"-9.98\" f26=\"8000.0\" v26=\"-11.894\" f27=\"10000.0\" v27=\"-13.734\" f28=\"12500.0\" v28=\"-15.609\" f29=\"16000.0\" v29=\"-17.708\" f30=\"20000.0\" v30=\"-19.62\" f31=\"25000.0\" v31=\"-21.542\" f32=\"48000.0\" v32=\"-27.187\"") },
193 { kCURVE, XO("Telephone"),
194 wxT(
195 "f0=\"20.0\" v0=\"-94.087\" f1=\"200.0\" v1=\"-14.254\" f2=\"250.0\" v2=\"-7.243\" f3=\"315.0\" v3=\"-2.245\" f4=\"400.0\" v4=\"-0.414\" f5=\"500.0\" v5=\"0.0\" f6=\"2500.0\" v6=\"0.0\" f7=\"3150.0\" v7=\"-0.874\" f8=\"4000.0\" v8=\"-3.992\" f9=\"5000.0\" v9=\"-9.993\" f10=\"48000.0\" v10=\"-88.117\"") },
196 { kBOTH, XO("Treble Boost"),
197 wxT("f0=\"4000.0\" v0=\"0.0\" f1=\"5000.0\" v1=\"9.0\"") },
198 { kBOTH, XO("Treble Cut"),
199 wxT("f0=\"6000.0\" v0=\"0.0\" f1=\"10000.0\" v1=\"-110.0\"") },
200 { kCURVE, XO("Walkie-talkie"),
201 wxT(
202 "f0=\"100.0\" v0=\"-120.0\" f1=\"101.0\" v1=\"0.0\" f2=\"2000.0\" v2=\"0.0\" f3=\"2001.0\" v3=\"-120.0\"") },
203 };
204
206{
208
209 for (size_t i = 0; i < WXSIZEOF(FactoryPresets); i++)
210 {
211 if (
213 (FactoryPresets[i].bForBoth == false))
214 continue;
215 names.push_back(FactoryPresets[i].name.Translation());
216 }
217
218 return names;
219}
220
223{
224 int index = -1;
225 for (size_t i = 0; i < WXSIZEOF(FactoryPresets); i++)
226 {
227 if (
229 (FactoryPresets[i].bForBoth == false))
230 continue;
231 if (id-- == 0)
232 {
233 index = i;
234 break;
235 }
236 }
237 if (index < 0)
238 return {};
239
240 // mParams =
241 wxString params = FactoryPresets[index].values;
242
245 S.SetForWriting(&eap);
246 // To do: externalize state so const_cast isn't needed
247 if (!const_cast<EqualizationBase*>(this)->VisitSettings(S, settings))
248 return {};
249 return { nullptr };
250}
251
252// Effect implementation
253
255{
256 constexpr auto loFreqI = EqualizationFilter::loFreqI;
257
258 const auto& lin = mParameters.mLin;
259 const auto& curveName = mParameters.mCurveName;
260 auto& loFreq = mParameters.mLoFreq;
261 auto& hiFreq = mParameters.mHiFreq;
262
263 int selcount = 0;
264 double rate = 0.0;
265
266 if (const auto project = FindProject())
267 {
268 auto trackRange = TrackList::Get(*project).Selected<const WaveTrack>();
269 if (trackRange)
270 {
271 rate = (*(trackRange.first++))->GetRate();
272 ++selcount;
273
274 for (auto track : trackRange)
275 {
276 if (track->GetRate() != rate)
277 {
279 "To apply Equalization, all selected tracks must have the same sample rate."));
280 return (false);
281 }
282 ++selcount;
283 }
284 }
285 }
286 else
287 // Editing macro parameters, use this default
288 rate = 44100.0;
289
290 hiFreq = rate / 2.0;
291 // Unlikely, but better than crashing.
292 if (hiFreq <= loFreqI)
293 {
295 XO("Track sample rate is too low for this effect."));
296 return (false);
297 }
298
299 loFreq = loFreqI;
300
301 mCurvesList.setCurve(curveName);
302
304
305 return (true);
306}
307
309{
310 EffectOutputTracks outputs { *mTracks, GetType(), { { mT0, mT1 } } };
312 bool bGoodResult = true;
313
314 int count = 0;
315 for (auto track : outputs.Get().Selected<WaveTrack>())
316 {
317 double trackStart = track->GetStartTime();
318 double trackEnd = track->GetEndTime();
319 double t0 = mT0 < trackStart ? trackStart : mT0;
320 double t1 = mT1 > trackEnd ? trackEnd : mT1;
321
322 if (t1 > t0)
323 {
324 auto start = track->TimeToLongSamples(t0);
325 auto end = track->TimeToLongSamples(t1);
326 auto len = end - start;
327
328 auto pTempTrack = track->EmptyCopy();
329 pTempTrack->ConvertToSampleFormat(floatSample);
330 auto iter0 = pTempTrack->Channels().begin();
331
332 for (const auto pChannel : track->Channels())
333 {
334 constexpr auto windowSize = EqualizationFilter::windowSize;
335 const auto& M = mParameters.mM;
336
337 wxASSERT(M - 1 < windowSize);
338 size_t L = windowSize - (M - 1); // Process L samples at a go
339 auto s = start;
340 auto idealBlockLen = pChannel->GetMaxBlockSize() * 4;
341 if (idealBlockLen % L != 0)
342 idealBlockLen += (L - (idealBlockLen % L));
343 auto pNewChannel = *iter0++;
344 Task task { M, idealBlockLen, *pNewChannel };
345 bGoodResult = ProcessOne(task, count, *pChannel, start, len);
346 if (!bGoodResult)
347 goto done;
348 }
349 pTempTrack->Flush();
350 // Remove trailing data from the temp track
351 pTempTrack->Clear(t1 - t0, pTempTrack->GetEndTime());
352 track->ClearAndPaste(t0, t1, *pTempTrack, true, true);
353 }
354
355 count++;
356 }
357done:
358
359 if (bGoodResult)
360 outputs.Commit();
361
362 return bGoodResult;
363}
364
365// EqualizationBase implementation
366
368 Task& task, int count, const WaveChannel& t, sampleCount start,
369 sampleCount len)
370{
371 constexpr auto windowSize = EqualizationFilter::windowSize;
372
373 const auto& M = mParameters.mM;
374
375 wxASSERT(M - 1 < windowSize);
376 size_t L = windowSize - (M - 1); // Process L samples at a go
377 auto s = start;
378
379 auto& buffer = task.buffer;
380 auto& window1 = task.window1;
381 auto& window2 = task.window2;
382 auto& thisWindow = task.thisWindow;
383 auto& lastWindow = task.lastWindow;
384
385 auto originalLen = len;
386
387 auto& output = task.output;
388
389 TrackProgress(count, 0.);
390 bool bLoopSuccess = true;
391 size_t wcopy = 0;
392
393 while (len != 0)
394 {
395 auto block = limitSampleBufferSize(task.idealBlockLen, len);
396
397 t.GetFloats(buffer.get(), s, block);
398
399 for (size_t i = 0; i < block;
400 i += L) // go through block in lumps of length L
401 {
402 wcopy = std::min<size_t>(L, block - i);
403 for (size_t j = 0; j < wcopy; j++)
404 thisWindow[j] = buffer[i + j]; // copy the L (or remaining) samples
405 for (auto j = wcopy; j < windowSize; j++)
406 thisWindow[j] = 0; // this includes the padding
407
408 mParameters.Filter(windowSize, thisWindow);
409
410 // Overlap - Add
411 for (size_t j = 0; (j < M - 1) && (j < wcopy); j++)
412 buffer[i + j] = thisWindow[j] + lastWindow[L + j];
413 for (size_t j = M - 1; j < wcopy; j++)
414 buffer[i + j] = thisWindow[j];
415
416 std::swap(thisWindow, lastWindow);
417 } // next i, lump of this block
418
419 task.AccumulateSamples((samplePtr)buffer.get(), block);
420 len -= block;
421 s += block;
422
423 if (TrackProgress(
424 count, (s - start).as_double() / originalLen.as_double()))
425 {
426 bLoopSuccess = false;
427 break;
428 }
429 }
430
431 if (bLoopSuccess)
432 {
433 // M-1 samples of 'tail' left in lastWindow, get them now
434 if (wcopy < (M - 1))
435 {
436 // Still have some overlap left to process
437 // (note that lastWindow and thisWindow have been exchanged at this
438 // point
439 // so that 'thisWindow' is really the window prior to 'lastWindow')
440 size_t j = 0;
441 for (; j < M - 1 - wcopy; j++)
442 buffer[j] = lastWindow[wcopy + j] + thisWindow[L + wcopy + j];
443 // And fill in the remainder after the overlap
444 for (; j < M - 1; j++)
445 buffer[j] = lastWindow[wcopy + j];
446 }
447 else
448 {
449 for (size_t j = 0; j < M - 1; j++)
450 buffer[j] = lastWindow[wcopy + j];
451 }
452 task.AccumulateSamples((samplePtr)buffer.get(), M - 1);
453 }
454 return bLoopSuccess;
455}
wxT("CloseDown"))
Toolkit-neutral facade for basic user interface services.
EffectDistortionSettings params
EffectType
@ EffectTypeProcess
std::optional< std::unique_ptr< EffectSettingsAccess::Message > > OptionalMessage
const bool kBOTH
const bool bForBoth
static const struct @104 FactoryPresets[]
const wxChar * values
const TranslatableString name
const bool kCURVE
const int kEqOptionCurve
const int kEqOptionGraphic
XO("Cut/Copy/Paste")
std::vector< RegistryPath > RegistryPaths
Definition: Identifier.h:219
size_t limitSampleBufferSize(size_t bufferSize, sampleCount limit)
Definition: SampleCount.cpp:22
char * samplePtr
Definition: SampleFormat.h:57
static TranslatableStrings names
Definition: TagsEditor.cpp:153
const auto project
#define S(N)
Definition: ToChars.cpp:64
static Settings & settings()
Definition: TrackInfo.cpp:51
Generates EffectParameterMethods overrides from variadic template arguments.
CommandParameters, derived from wxFileConfig, is essentially doing the same things as the SettingsVis...
TranslatableString GetName() const
ComponentInterfaceSymbol pairs a persistent string identifier used internally with an optional,...
Deserializer of curves from XML files.
One point in a curve.
double mT1
Definition: EffectBase.h:123
void SetLinearEffectFlag(bool linearEffectFlag)
Definition: EffectBase.cpp:210
const TrackList * inputTracks() const
Definition: EffectBase.h:102
double mProjectRate
Definition: EffectBase.h:119
std::shared_ptr< TrackList > mTracks
Definition: EffectBase.h:116
double mT0
Definition: EffectBase.h:122
const AudacityProject * FindProject() const
Definition: EffectBase.cpp:220
OptionalMessage LoadFactoryDefaults(EffectSettings &settings) const override
Definition: Effect.cpp:165
bool VisitSettings(SettingsVisitor &visitor, EffectSettings &settings) override
Definition: Effect.cpp:102
bool TrackProgress(int whichTrack, double frac, const TranslatableString &={}) const
Definition: Effect.cpp:343
Performs effect computation.
Use this object to copy the input tracks to tentative outputTracks.
Interface for manipulations of an Effect's settings.
virtual void Reset(Effect &effect) const =0
bool VisitSettings(SettingsVisitor &visitor, EffectSettings &settings) override
EqualizationBase(int Options=kEqLegacy)
bool ProcessOne(Task &task, int count, const WaveChannel &t, sampleCount start, sampleCount len)
bool Process(EffectInstance &instance, EffectSettings &settings) override
RegistryPaths GetFactoryPresets() const override
Report names of factory presets.
OptionalMessage DoLoadFactoryDefaults(EffectSettings &settings)
TranslatableString GetDescription() const override
ManualPageID ManualPage() const override
Name of a page in the Audacity alpha manual, default is empty.
EqualizationCurvesList mCurvesList
OptionalMessage LoadFactoryDefaults(EffectSettings &settings) const override
EqualizationFilter mParameters
virtual ~EqualizationBase()
OptionalMessage LoadFactoryPreset(int id, EffectSettings &settings) const override
static const ComponentInterfaceSymbol Symbol
const EffectParameterMethods & Parameters() const override
bool Init() override
EffectType GetType() const override
Type determines how it behaves.
Visitor of effect or command parameters. This is a base class with lots of virtual functions that do ...
virtual void Define(Arg< bool > var, const wxChar *key, bool vdefault, bool vmin=false, bool vmax=false, bool vscl=false)
SettingsVisitor that gets parameter values into a string.
SettingsVisitor that sets parameters to a value (from a string)
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:314
auto Selected() -> TrackIterRange< TrackType >
Definition: Track.h:967
Holds a msgid for the translation catalog; may also bind format arguments.
wxString Translation() const
bool GetFloats(float *buffer, sampleCount start, size_t len, fillFormat fill=FillFormat::fillZero, bool mayThrow=true, sampleCount *pNumWithinClips=nullptr) const
"narrow" overload fetches from the unique channel
Definition: WaveTrack.h:129
A Track that contains audio waveform data.
Definition: WaveTrack.h:203
double GetRate() const override
Definition: WaveTrack.cpp:821
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:19
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
void swap(std::unique_ptr< Alg_seq > &a, std::unique_ptr< Alg_seq > &b)
Definition: NoteTrack.cpp:634
const char * end(const char *str) noexcept
Definition: StringUtils.h:106
Externalized state of a plug-in.
void AccumulateSamples(constSamplePtr buffer, size_t len)
void setCurve(int currentCurve)
static constexpr int loFreqI
static constexpr size_t windowSize
void Filter(size_t len, float *buffer) const
Parameters of the Equalization effects that persist in configuration files.
static constexpr EnumParameter InterpMeth
static constexpr EffectParameter FilterLength
static constexpr EffectParameter InterpLin