Audacity 3.2.0
EqualizationPanel.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 EqualizationPanel.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*//****************************************************************//*******************************************************************/
21#include "EqualizationPanel.h"
23#include "EqualizationFilter.h"
24
25#include <wx/dcclient.h>
26#include <wx/dcmemory.h>
27#include <wx/settings.h>
28#include "AColor.h"
29#include "Envelope.h"
30#include "../EnvelopeEditor.h"
31#include "FFT.h"
32#include "Theme.h"
33#include "../TrackArtist.h"
34#include "ViewInfo.h"
35#include "../widgets/RulerPanel.h"
36#include "AllThemeResources.h"
37
38//----------------------------------------------------------------------------
39// EqualizationPanel
40//----------------------------------------------------------------------------
41
42BEGIN_EVENT_TABLE(EqualizationPanel, wxPanelWrapper)
44 EVT_MOUSE_EVENTS(EqualizationPanel::OnMouseEvent)
45 EVT_MOUSE_CAPTURE_LOST(EqualizationPanel::OnCaptureLost)
49
51 wxWindow *parent, wxWindowID winid, EqualizationCurvesList &curvesList,
52 RulerPanel &freqRuler, RulerPanel &dbRuler
53) : wxPanelWrapper(parent, winid)
54 , mCurvesList{ curvesList }
55 , mFreqRuler{ freqRuler }
56 , mdBRuler{ dbRuler }
57{
58 auto &parameters = mCurvesList.mParameters;
59 mParent = parent;
60
61 mBitmap = NULL;
62 mWidth = 0;
63 mHeight = 0;
64
65 mLinEditor = std::make_unique<EnvelopeEditor>(
66 parameters.mLinEnvelope, false);
67 mLogEditor = std::make_unique<EnvelopeEditor>(
68 parameters.mLogEnvelope, false);
69
70 // Initial Recalc() needed to populate mOutr before the first paint event
71 Recalc();
72}
73
75{
76 if(HasCapture())
77 ReleaseMouse();
78}
79
81{
82 auto &parameters = mCurvesList.mParameters;
83 const auto &windowSize = parameters.mWindowSize;
84
85 mOutr = Floats{ windowSize };
86
87 parameters.CalcFilter(); //to calculate the actual response
88 InverseRealFFT(windowSize,
89 parameters.mFilterFuncR.get(),
90 parameters.mFilterFuncI.get(), mOutr.get());
91}
92
93void EqualizationPanel::OnSize(wxSizeEvent & WXUNUSED(event))
94{
95 Refresh( false );
96}
97
98#include "../TrackPanelDrawingContext.h"
99void EqualizationPanel::OnPaint(wxPaintEvent & WXUNUSED(event))
100{
101 const auto &parameters = mCurvesList.mParameters;
102 const auto &dBMax = parameters.mdBMax;
103 const auto &dBMin = parameters.mdBMin;
104 const auto &M = parameters.mM;
105 const auto &drawMode = parameters.mDrawMode;
106 const auto &drawGrid = parameters.mDrawGrid;
107 const auto &loFreq = parameters.mLoFreq;
108 const auto &hiFreq = parameters.mHiFreq;
109 const auto &windowSize = parameters.mWindowSize;
110 const auto &filterFuncR = parameters.mFilterFuncR;
111 const auto &filterFuncI = parameters.mFilterFuncI;
112
113 wxPaintDC dc(this);
114 int width, height;
115 GetSize(&width, &height);
116
117 if (!mBitmap || mWidth!=width || mHeight!=height)
118 {
119 mWidth = width;
120 mHeight = height;
121 mBitmap = std::make_unique<wxBitmap>(mWidth, mHeight,24);
122 }
123
124 wxBrush bkgndBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_3DFACE));
125
126 wxMemoryDC memDC;
127 memDC.SelectObject(*mBitmap);
128
129 wxRect bkgndRect;
130 bkgndRect.x = 0;
131 bkgndRect.y = 0;
132 bkgndRect.width = mWidth;
133 bkgndRect.height = mHeight;
134 memDC.SetBrush(bkgndBrush);
135 memDC.SetPen(*wxTRANSPARENT_PEN);
136 memDC.DrawRectangle(bkgndRect);
137
138 bkgndRect.y = mHeight;
139 memDC.DrawRectangle(bkgndRect);
140
141 wxRect border;
142 border.x = 0;
143 border.y = 0;
144 border.width = mWidth;
145 border.height = mHeight;
146
147 memDC.SetBrush(*wxWHITE_BRUSH);
148 memDC.SetPen(*wxBLACK_PEN);
149 memDC.DrawRectangle(border);
150
151 mEnvRect = border;
153
154 // Pure blue x-axis line
155 memDC.SetPen(wxPen(theTheme.Colour( clrGraphLines ), 1, wxPENSTYLE_SOLID));
156 int center = (int) (mEnvRect.height * dBMax/(dBMax - dBMin) + .5);
157 AColor::Line(memDC,
158 mEnvRect.GetLeft(), mEnvRect.y + center,
159 mEnvRect.GetRight(), mEnvRect.y + center);
160
161 // Draw the grid, if asked for. Do it now so it's underneath the main plots.
162 if( drawGrid )
163 {
164 mFreqRuler.ruler.DrawGrid(memDC, mEnvRect.height, true, true, PANELBORDER, PANELBORDER);
165 mdBRuler.ruler.DrawGrid(memDC, mEnvRect.width, true, true, PANELBORDER, PANELBORDER);
166 }
167
168 // Med-blue envelope line
169 memDC.SetPen(wxPen(theTheme.Colour(clrGraphLines), 3, wxPENSTYLE_SOLID));
170
171 // Draw envelope
172 int x, y, xlast = 0, ylast = 0;
173 {
174 Doubles values{ size_t(mEnvRect.width) };
175 parameters.ChooseEnvelopeToPaint()
176 .GetValues(values.get(), mEnvRect.width, 0.0, 1.0 / mEnvRect.width);
177 bool off = false, off1 = false;
178 for (int i = 0; i < mEnvRect.width; i++)
179 {
180 x = mEnvRect.x + i;
181 y = lrint(mEnvRect.height*((dBMax - values[i]) / (dBMax - dBMin)) + .25); //needs more optimising, along with'what you get'?
182 if (y >= mEnvRect.height)
183 {
184 y = mEnvRect.height - 1;
185 off = true;
186 }
187 else
188 {
189 off = false;
190 off1 = false;
191 }
192 if ((i != 0) & (!off1))
193 {
194 AColor::Line(memDC, xlast, ylast,
195 x, mEnvRect.y + y);
196 }
197 off1 = off;
198 xlast = x;
199 ylast = mEnvRect.y + y;
200 }
201 }
202
203 //Now draw the actual response that you will get.
204 //mFilterFunc has a linear scale, window has a log one so we have to fiddle about
205 memDC.SetPen(wxPen(theTheme.Colour( clrResponseLines ), 1, wxPENSTYLE_SOLID));
206 double scale = (double)mEnvRect.height/(dBMax - dBMin); //pixels per dB
207 double yF; //gain at this freq
208 double delta = hiFreq / (((double)windowSize / 2.)); //size of each freq bin
209
210 bool lin = parameters.IsLinear(); // log or lin scale?
211
212 double loLog = log10(loFreq);
213 double step = lin ? hiFreq : (log10(hiFreq) - loLog);
214 step /= ((double)mEnvRect.width-1.);
215 double freq; //actual freq corresponding to x position
216 int halfM = (M - 1) / 2;
217 int n; //index to mFreqFunc
218 for(int i=0; i<mEnvRect.width; i++)
219 {
220 x = mEnvRect.x + i;
221 freq = lin ? step*i : pow(10., loLog + i*step); //Hz
222 if( ( lin ? step : (pow(10., loLog + (i+1)*step)-freq) ) < delta)
223 { //not enough resolution in FFT
224 // set up for calculating cos using recurrence - faster than calculating it directly each time
225 double theta = M_PI*freq/hiFreq; //radians, normalized
226 double wtemp = sin(0.5 * theta);
227 double wpr = -2.0 * wtemp * wtemp;
228 double wpi = -1.0 * sin(theta);
229 double wr = cos(theta*halfM);
230 double wi = sin(theta*halfM);
231
232 yF = 0.;
233 for(int j=0;j<halfM;j++)
234 {
235 yF += 2. * mOutr[j] * wr; // This works for me, compared to the previous version. Compare wr to cos(theta*(halfM-j)). Works for me. Keep everything as doubles though.
236 // do recurrence
237 wr = (wtemp = wr) * wpr - wi * wpi + wr;
238 wi = wi * wpr + wtemp * wpi + wi;
239 }
240 yF += mOutr[halfM];
241 yF = fabs(yF);
242 if(yF!=0.)
243 yF = LINEAR_TO_DB(yF);
244 else
245 yF = dBMin;
246 }
247 else
248 { //use FFT, it has enough resolution
249 n = (int)(freq/delta + .5);
250 if(pow(filterFuncR[n],2)+pow(filterFuncI[n],2)!=0.)
251 yF = 10.0*log10(pow(filterFuncR[n],2)+pow(filterFuncI[n],2)); //10 here, a power
252 else
253 yF = dBMin;
254 }
255 if(yF < dBMin)
256 yF = dBMin;
257 yF = center-scale*yF;
258 if(yF>mEnvRect.height)
259 yF = mEnvRect.height - 1;
260 if(yF<0.)
261 yF=0.;
262 y = (int)(yF+.5);
263
264 if (i != 0)
265 {
266 AColor::Line(memDC, xlast, ylast, x, mEnvRect.y + y);
267 }
268 xlast = x;
269 ylast = mEnvRect.y + y;
270 }
271
272 memDC.SetPen(*wxBLACK_PEN);
273 if( drawMode )
274 {
275 ZoomInfo zoomInfo( 0.0, mEnvRect.width-1 );
276
277 // Back pointer to TrackPanel won't be needed in the one drawing
278 // function we use here
279 TrackArtist artist( nullptr );
280
281 artist.pZoomInfo = &zoomInfo;
282 TrackPanelDrawingContext context{ memDC, {}, {}, &artist };
283 EnvelopeEditor::DrawPoints( parameters.ChooseEnvelopeToPaint(),
284 context, mEnvRect, false, 0.0,
285 dBMin, dBMax, false);
286 }
287
288 dc.Blit(0, 0, mWidth, mHeight, &memDC, 0, 0, wxCOPY, FALSE);
289}
290
291void EqualizationPanel::OnMouseEvent(wxMouseEvent & event)
292{
293 const auto &parameters = mCurvesList.mParameters;
294 const auto &dBMax = parameters.mdBMax;
295 const auto &dBMin = parameters.mdBMin;
296 const auto &drawMode = parameters.mDrawMode;
297 const auto &lin = parameters.mLin;
298
299 if (!drawMode)
300 {
301 return;
302 }
303
304 if (event.ButtonDown() && !HasCapture())
305 {
306 CaptureMouse();
307 }
308
309 auto &pEditor = (lin ? mLinEditor : mLogEditor);
310 if (pEditor->MouseEvent(event, mEnvRect, ZoomInfo(0.0, mEnvRect.width),
311 false, 0.0,
312 dBMin, dBMax)
313 )
315
316 if (event.ButtonUp() && HasCapture())
317 {
319 ReleaseMouse();
320 }
321}
322
323void EqualizationPanel::OnCaptureLost(wxMouseCaptureLostEvent & WXUNUSED(event))
324{
325 if (HasCapture())
326 {
327 ReleaseMouse();
328 }
329}
330
331void EqualizationPanel::OnIdle(wxIdleEvent &event)
332{
334 Recalc();
335 Refresh(false);
337 }
338}
END_EVENT_TABLE()
#define M_PI
Definition: Distortion.cpp:30
const wxChar * values
#define PANELBORDER
void InverseRealFFT(size_t NumSamples, const float *RealIn, const float *ImagIn, float *RealOut)
Definition: FFT.cpp:266
#define LINEAR_TO_DB(x)
Definition: MemoryX.h:338
THEME_API Theme theTheme
Definition: Theme.cpp:82
static void Line(wxDC &dc, wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
Definition: AColor.cpp:187
static void DrawPoints(const Envelope &env, TrackPanelDrawingContext &context, const wxRect &r, bool dB, double dBRange, float zoomMin, float zoomMax, bool mirrored, int origin=0)
EqualizationPanel is used with EqualizationDialog and controls a graph for EffectEqualization....
void OnSize(wxSizeEvent &event)
RulerPanel & mdBRuler
EqualizationCurvesList & mCurvesList
void OnCaptureLost(wxMouseCaptureLostEvent &event)
void OnIdle(wxIdleEvent &event)
std::unique_ptr< wxBitmap > mBitmap
std::unique_ptr< EnvelopeEditor > mLinEditor
void OnMouseEvent(wxMouseEvent &event)
void OnPaint(wxPaintEvent &event)
RulerPanel & mFreqRuler
std::unique_ptr< EnvelopeEditor > mLogEditor
void DrawGrid(wxDC &dc, int length, bool minor=true, bool major=true, int xOffset=0, int yOffset=0) const
Definition: Ruler.cpp:530
RulerPanel class allows you to work with a Ruler like any other wxWindow.
Definition: RulerPanel.h:19
Ruler ruler
Definition: RulerPanel.h:79
wxColour & Colour(int iIndex)
This class handles the actual rendering of WaveTracks (both waveforms and spectra),...
Definition: TrackArtist.h:40
ZoomInfo * pZoomInfo
Definition: TrackArtist.h:124
#define lrint(dbl)
Definition: float_cast.h:169
Maintains a list of preset curves for Equalization effects.
EqualizationFilter & mParameters