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