Audacity 3.2.0
EnvelopeEditor.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 EnvelopeEditor.cpp
6
7 Paul Licameli split this from Envelope.cpp
8
9**********************************************************************/
10
11
12#include "EnvelopeEditor.h"
13
14
15
16#include <wx/dc.h>
17#include <wx/event.h>
18
19#include "AColor.h"
20#include "Envelope.h"
21#include "TrackArt.h"
22#include "TrackArtist.h"
24#include "ViewInfo.h"
26
27namespace {
28void DrawPoint(wxDC & dc, const wxRect & r, int x, int y, bool top)
29{
30 if (y >= 0 && y <= r.height) {
31 wxRect circle(r.x + x, r.y + (top ? y - 1: y - 2), 4, 4);
32 dc.DrawEllipse(circle);
33 }
34}
35}
36
38 TrackPanelDrawingContext &context, const wxRect & r,
39 bool dB, double dBRange,
40 float zoomMin, float zoomMax, bool mirrored, int origin)
41{
42 auto &dc = context.dc;
43 const auto artist = TrackArtist::Get( context );
44 const auto &zoomInfo = *artist->pZoomInfo;
45
46 bool highlight = false;
47#ifdef EXPERIMENTAL_TRACK_PANEL_HIGHLIGHTING
48 auto target = dynamic_cast<EnvelopeHandle*>(context.target.get());
49 highlight = target && target->GetEnvelope() == &env;
50#endif
51 wxPen &pen = highlight ? AColor::uglyPen : AColor::envelopePen;
52 dc.SetPen( pen );
53 dc.SetBrush(*wxWHITE_BRUSH);
54
55 for (int i = 0; i < (int)env.GetNumberOfPoints(); i++) {
56 const double time = env[i].GetT() + env.GetOffset();
57 const wxInt64 position = zoomInfo.TimeToPosition(time, origin);
58 if (position >= 0 && position < r.width) {
59 // Change colour if this is the draggable point...
60 if (i == env.GetDragPoint()) {
61 dc.SetPen( pen );
62 dc.SetBrush(AColor::envelopeBrush);
63 }
64
65 double v = env[i].GetVal();
66 int x = (int)(position);
67 int y, y2;
68
69 y = GetWaveYPos(v, zoomMin, zoomMax, r.height, dB,
70 true, dBRange, false);
71 if (!mirrored) {
72 DrawPoint(dc, r, x, y, true);
73 }
74 else {
75 y2 = GetWaveYPos(-v-.000000001, zoomMin, zoomMax, r.height, dB,
76 true, dBRange, false);
77
78 // This follows the same logic as the envelop drawing in
79 // TrackArt::DrawEnvelope().
80 // TODO: make this calculation into a reusable function.
81 if (y2 - y < 9) {
82 int value = (int)((zoomMax / (zoomMax - zoomMin)) * r.height);
83 y = value - 4;
84 y2 = value + 4;
85 }
86
87 DrawPoint(dc, r, x, y, true);
88 DrawPoint(dc, r, x, y2, false);
89
90 // Contour
91 y = GetWaveYPos(v, zoomMin, zoomMax, r.height, dB,
92 false, dBRange, false);
93 y2 = GetWaveYPos(-v-.000000001, zoomMin, zoomMax, r.height, dB,
94 false, dBRange, false);
95 if (y <= y2) {
96 DrawPoint(dc, r, x, y, true);
97 DrawPoint(dc, r, x, y2, false);
98 }
99 }
100
101 // Change colour back again if was the draggable point.
102 if (i == env.GetDragPoint()) {
103 dc.SetPen( pen );
104 dc.SetBrush(*wxWHITE_BRUSH);
105 }
106 }
107 }
108}
109
111 : mEnvelope(envelope)
112 , mMirrored(mirrored)
113 , mContourOffset(-1)
114 // , mInitialVal(-1.0)
115 // , mInitialY(-1)
116 , mUpper(false)
117 , mButton(wxMOUSE_BTN_NONE)
118 , mDirty(false)
119{
120}
121
123{
124}
125
126namespace
127{
128inline int SQR(int x) { return x * x; }
129}
130
138float EnvelopeEditor::ValueOfPixel( int y, int height, bool upper,
139 bool dB, double dBRange,
140 float zoomMin, float zoomMax)
141{
142 float v = ::ValueOfPixel(y, height, 0 != mContourOffset, dB, dBRange, zoomMin, zoomMax);
143
144 // MB: this is mostly equivalent to what the old code did, I'm not sure
145 // if anything special is needed for asymmetric ranges
146 if(upper)
147 return mEnvelope.ClampValue(v);
148 else
149 return mEnvelope.ClampValue(-v);
150}
151
158bool EnvelopeEditor::HandleMouseButtonDown(const wxMouseEvent & event, wxRect & r,
159 const ZoomInfo &zoomInfo,
160 bool dB, double dBRange,
161 float zoomMin, float zoomMax)
162{
163 int ctr = (int)(r.height * zoomMax / (zoomMax - zoomMin));
164 bool upper = !mMirrored || (zoomMin >= 0.0) || (event.m_y - r.y < ctr);
165
166 int clip_y = event.m_y - r.y;
167 if(clip_y < 0) clip_y = 0; //keeps point in rect r, even if mouse isn't
168 if(clip_y > r.GetBottom()) clip_y = r.GetBottom();
169
170 int bestNum = -1;
171 int bestDistSqr = 100; // Must be within 10 pixel radius.
172
173 // Member variables hold state that will be needed in dragging.
174 mButton = event.GetButton();
175 mContourOffset = false;
176
177 // wxLogDebug(wxT("Y:%i Height:%i Offset:%i"), y, height, mContourOffset );
178 int len = mEnvelope.GetNumberOfPoints();
179
180 // TODO: extract this into a function FindNearestControlPoint()
181 // TODO: also fix it so that we can drag the last point on an envelope.
182 for (int i = 0; i < len; i++) { //search for control point nearest click
183 const double time = mEnvelope[i].GetT() + mEnvelope.GetOffset();
184 const wxInt64 position = zoomInfo.TimeToPosition(time);
185 if (position >= 0 && position < r.width) {
186
187 int x = (int)(position);
188 int y[4];
189 int numControlPoints;
190
191 // Outer control points
192 double value = mEnvelope[i].GetVal();
193 y[0] = GetWaveYPos(value, zoomMin, zoomMax, r.height,
194 dB, true, dBRange, false);
195 y[1] = GetWaveYPos(-value, zoomMin, zoomMax, r.height,
196 dB, true, dBRange, false);
197
198 // Inner control points(contour)
199 y[2] = GetWaveYPos(value, zoomMin, zoomMax, r.height,
200 dB, false, dBRange, false);
201 y[3] = GetWaveYPos(-value -.00000001, zoomMin, zoomMax,
202 r.height, dB, false, dBRange, false);
203
204 numControlPoints = 4;
205
206 if (y[2] > y[3])
207 numControlPoints = 2;
208
209 if (!mMirrored)
210 numControlPoints = 1;
211
212 const int deltaXSquared = SQR(x - (event.m_x - r.x));
213 for(int j=0; j<numControlPoints; j++){
214
215 const int dSqr = deltaXSquared + SQR(y[j] - (event.m_y - r.y));
216 if (dSqr < bestDistSqr) {
217 bestNum = i;
218 bestDistSqr = dSqr;
219 mContourOffset = (bool)(j > 1);
220 }
221 }
222 }
223 }
224
225 if (bestNum >= 0) {
226 mEnvelope.SetDragPoint(bestNum);
227 }
228 else {
229 // TODO: Extract this into a function CreateNewPoint
230 const double when = zoomInfo.PositionToTime(event.m_x, r.x);
231
232 // if (when <= 0 || when >= mTrackLen)
233 // return false;
234
235 const double v = mEnvelope.GetValue( when );
236
237 int ct = GetWaveYPos( v, zoomMin, zoomMax, r.height, dB,
238 false, dBRange, false) ;
239 int cb = GetWaveYPos( -v-.000000001, zoomMin, zoomMax, r.height, dB,
240 false, dBRange, false) ;
241 if (ct <= cb || !mMirrored) {
242 int t = GetWaveYPos( v, zoomMin, zoomMax, r.height, dB,
243 true, dBRange, false) ;
244 int b = GetWaveYPos( -v, zoomMin, zoomMax, r.height, dB,
245 true, dBRange, false) ;
246
247 ct = (t + ct) / 2;
248 cb = (b + cb) / 2;
249
250 if (mMirrored &&
251 (event.m_y - r.y) > ct &&
252 ((event.m_y - r.y) < cb))
253 mContourOffset = true;
254 else
255 mContourOffset = false;
256 }
257
258 double newVal = ValueOfPixel(clip_y, r.height, upper, dB, dBRange,
259 zoomMin, zoomMax);
260
262 mDirty = true;
263 }
264
265 mUpper = upper;
266
267 // const int dragPoint = mEnvelope.GetDragPoint();
268 // mInitialVal = mEnvelope[dragPoint].GetVal();
269 // mInitialY = event.m_y+mContourOffset;
270
271 return true;
272}
273
274void EnvelopeEditor::MoveDragPoint(const wxMouseEvent & event, wxRect & r,
275 const ZoomInfo &zoomInfo, bool dB, double dBRange,
276 float zoomMin, float zoomMax)
277{
278 int clip_y = event.m_y - r.y;
279 if(clip_y < 0) clip_y = 0;
280 if(clip_y > r.height) clip_y = r.height;
281 double newVal = ValueOfPixel(clip_y, r.height, mUpper, dB, dBRange,
282 zoomMin, zoomMax);
283
284 // We no longer tolerate multiple envelope points at the same t.
285 // epsilon is less than the time offset of a single sample
286 // TODO: However because mTrackEpsilon assumes 200KHz this use
287 // of epsilon is a tad bogus. What we need to do instead is DELETE
288 // a duplicated point on a mouse up.
289 double newWhen = zoomInfo.PositionToTime(event.m_x, r.x) - mEnvelope.GetOffset();
290 mEnvelope.MoveDragPoint(newWhen, newVal);
291}
292
293bool EnvelopeEditor::HandleDragging(const wxMouseEvent & event, wxRect & r,
294 const ZoomInfo &zoomInfo, bool dB, double dBRange,
295 float zoomMin, float zoomMax,
296 float WXUNUSED(eMin), float WXUNUSED(eMax))
297{
298 mDirty = true;
299
300 wxRect larger = r;
301 larger.Inflate(10, 10);
302
303 if (larger.Contains(event.m_x, event.m_y))
304 {
305 // IF we're in the rect THEN we're not deleting this point (anymore).
306 // ...we're dragging it.
307 MoveDragPoint( event, r, zoomInfo, dB, dBRange, zoomMin, zoomMax);
308 return true;
309 }
310
312 // IF we already know we're deleting THEN no envelope point to update.
313 return false;
314
315 // Invalidate the point
317 return true;
318}
319
320// Exit dragging mode and delete dragged point if necessary.
322{
324 mButton = wxMOUSE_BTN_NONE;
325 return true;
326}
327
328// Returns true if parent needs to be redrawn
329bool EnvelopeEditor::MouseEvent(const wxMouseEvent & event, wxRect & r,
330 const ZoomInfo &zoomInfo, bool dB, double dBRange,
331 float zoomMin, float zoomMax)
332{
333 if (event.ButtonDown() && mButton == wxMOUSE_BTN_NONE)
334 return HandleMouseButtonDown( event, r, zoomInfo, dB, dBRange,
335 zoomMin, zoomMax);
336 if (event.Dragging() && mEnvelope.GetDragPoint() >= 0)
337 return HandleDragging( event, r, zoomInfo, dB, dBRange,
338 zoomMin, zoomMax);
339 if (event.ButtonUp() && event.GetButton() == mButton)
340 return HandleMouseButtonUp();
341 return false;
342}
int GetWaveYPos(float value, float min, float max, int height, bool dB, bool outer, float dBr, bool clip)
Definition: TrackArt.cpp:66
static wxBrush envelopeBrush
Definition: AColor.h:118
static wxPen uglyPen
Definition: AColor.h:140
static wxPen envelopePen
Definition: AColor.h:116
EnvelopeEditor(Envelope &envelope, bool mirrored)
Envelope & mEnvelope
bool HandleDragging(const wxMouseEvent &event, wxRect &r, const ZoomInfo &zoomInfo, bool dB, double dBRange, float zoomMin=-1.0, float zoomMax=1.0, float eMin=0., float eMax=2.)
bool MouseEvent(const wxMouseEvent &event, wxRect &r, const ZoomInfo &zoomInfo, bool dB, double dBRange, float zoomMin=-1.0, float zoomMax=1.0)
void MoveDragPoint(const wxMouseEvent &event, wxRect &r, const ZoomInfo &zoomInfo, bool dB, double dBRange, float zoomMin, float zoomMax)
bool HandleMouseButtonDown(const wxMouseEvent &event, wxRect &r, const ZoomInfo &zoomInfo, bool dB, double dBRange, float zoomMin=-1.0, float zoomMax=1.0)
const bool mMirrored
bool HandleMouseButtonUp()
int mContourOffset
Number of pixels contour is from the true envelope.
float ValueOfPixel(int y, int height, bool upper, bool dB, double dBRange, float zoomMin, float zoomMax)
static void DrawPoints(const Envelope &env, TrackPanelDrawingContext &context, const wxRect &r, bool dB, double dBRange, float zoomMin, float zoomMax, bool mirrored, int origin=0)
Envelope * GetEnvelope() const
Piecewise linear or piecewise exponential function from double to double.
Definition: Envelope.h:72
double GetOffset() const
Definition: Envelope.h:90
double GetValue(double t, double sampleDur=0) const
Get envelope value at time t.
Definition: Envelope.cpp:880
int InsertOrReplace(double when, double value)
Add a point at a particular absolute time coordinate.
Definition: Envelope.h:169
void SetDragPoint(int dragPoint)
Definition: Envelope.cpp:148
double ClampValue(double value)
Definition: Envelope.h:102
void ClearDragPoint()
Definition: Envelope.cpp:221
size_t GetNumberOfPoints() const
Return number of points.
Definition: Envelope.cpp:723
void MoveDragPoint(double newWhen, double value)
Definition: Envelope.cpp:193
int GetDragPoint() const
Definition: Envelope.h:214
bool GetDragPointValid() const
Definition: Envelope.h:219
void SetDragPointValid(bool valid)
Definition: Envelope.cpp:154
static TrackArtist * Get(TrackPanelDrawingContext &)
Definition: TrackArtist.cpp:81
double PositionToTime(int64 position, int64 origin=0, bool ignoreFisheye=false) const
Definition: ZoomInfo.cpp:34
int64 TimeToPosition(double time, int64 origin=0, bool ignoreFisheye=false) const
STM: Converts a project time to screen x position.
Definition: ZoomInfo.cpp:44
void DrawPoint(wxDC &dc, const wxRect &r, int x, int y, bool top)