Audacity  2.2.2
EnvelopeHandle.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3 Audacity: A Digital Audio Editor
4 
5 EnvelopeHandle.cpp
6 
7 Paul Licameli split from TrackPanel.cpp
8 
9 **********************************************************************/
10 
11 #include "../../Audacity.h"
12 #include "EnvelopeHandle.h"
13 #include "../../Experimental.h"
14 
15 #include "../../MemoryX.h"
16 
17 #include "../../Envelope.h"
18 #include "../../HitTestResult.h"
19 #include "../../prefs/WaveformSettings.h"
20 #include "../../Project.h"
21 #include "../../RefreshCode.h"
22 #include "../../TimeTrack.h"
23 #include "../../TrackArtist.h"
24 #include "../../TrackPanelMouseEvent.h"
25 #include "../../ViewInfo.h"
26 #include "../../WaveTrack.h"
27 #include "../../../images/Cursors.h"
28 
30  : mEnvelope{ pEnvelope }
31 {
32 }
33 
35 {
36 #ifdef EXPERIMENTAL_TRACK_PANEL_HIGHLIGHTING
38 #endif
39 }
40 
42 {}
43 
45 (std::weak_ptr<EnvelopeHandle> & WXUNUSED(holder), Envelope *envelope, bool timeTrack)
46 {
47  auto result = std::make_shared<EnvelopeHandle>( envelope );
48  result->mTimeTrack = timeTrack;
49  return result;
50 }
51 
52 namespace {
53  void GetTimeTrackData
54  (const AudacityProject &project, const TimeTrack &tt,
55  double &dBRange, bool &dB, float &zoomMin, float &zoomMax)
56  {
57  const auto &viewInfo = project.GetViewInfo();
58  dBRange = viewInfo.dBr;
59  dB = tt.GetDisplayLog();
60  zoomMin = tt.GetRangeLower(), zoomMax = tt.GetRangeUpper();
61  if (dB) {
62  // MB: silly way to undo the work of GetWaveYPos while still getting a logarithmic scale
63  zoomMin = LINEAR_TO_DB(std::max(1.0e-7, double(dBRange))) / dBRange + 1.0;
64  zoomMax = LINEAR_TO_DB(std::max(1.0e-7, double(zoomMax))) / dBRange + 1.0;
65  }
66  }
67 }
68 
70 (std::weak_ptr<EnvelopeHandle> &holder,
71  const wxMouseState &state, const wxRect &rect,
72  const AudacityProject *pProject, const std::shared_ptr<TimeTrack> &tt)
73 {
74  auto envelope = tt->GetEnvelope();
75  if (!envelope)
76  return {};
77  bool dB;
78  double dBRange;
79  float zoomMin, zoomMax;
80  GetTimeTrackData( *pProject, *tt, dBRange, dB, zoomMin, zoomMax);
82  (holder, state, rect, pProject, envelope, zoomMin, zoomMax, dB, dBRange,
83  true);
84 }
85 
87 (std::weak_ptr<EnvelopeHandle> &holder,
88  const wxMouseState &state, const wxRect &rect,
89  const AudacityProject *pProject, const std::shared_ptr<WaveTrack> &wt)
90 {
93  Envelope *const envelope = wt->GetEnvelopeAtX(state.GetX());
94 
95  if (!envelope)
96  return {};
97 
98  const int displayType = wt->GetDisplay();
99  // Not an envelope hit, unless we're using a type of wavetrack display
100  // suitable for envelopes operations, ie one of the Wave displays.
101  if (displayType != WaveTrack::Waveform)
102  return {}; // No envelope, not a hit, so return.
103 
104  // Get envelope point, range 0.0 to 1.0
105  const bool dB = !wt->GetWaveformSettings().isLinear();
106 
107  float zoomMin, zoomMax;
108  wt->GetDisplayBounds(&zoomMin, &zoomMax);
109 
110  const float dBRange = wt->GetWaveformSettings().dBRange;
111 
113  (holder, state, rect, pProject, envelope, zoomMin, zoomMax, dB, dBRange, false);
114 }
115 
117 (std::weak_ptr<EnvelopeHandle> &holder,
118  const wxMouseState &state, const wxRect &rect, const AudacityProject *pProject,
119  Envelope *envelope, float zoomMin, float zoomMax,
120  bool dB, float dBRange, bool timeTrack)
121 {
122  const ViewInfo &viewInfo = pProject->GetViewInfo();
123 
124  const double envValue =
125  envelope->GetValue(viewInfo.PositionToTime(state.m_x, rect.x));
126 
127  // Get y position of envelope point.
128  int yValue = GetWaveYPos(envValue,
129  zoomMin, zoomMax,
130  rect.height, dB, true, dBRange, false) + rect.y;
131 
132  // Get y position of center line
133  int ctr = GetWaveYPos(0.0,
134  zoomMin, zoomMax,
135  rect.height, dB, true, dBRange, false) + rect.y;
136 
137  // Get y distance of mouse from center line (in pixels).
138  int yMouse = abs(ctr - state.m_y);
139  // Get y distance of envelope from center line (in pixels)
140  yValue = abs(ctr - yValue);
141 
142  // JKC: It happens that the envelope is actually drawn offset from its
143  // 'true' position (it is 3 pixels wide). yMisalign is really a fudge
144  // factor to allow us to hit it exactly, but I wouldn't dream of
145  // calling it yFudgeFactor :)
146  const int yMisalign = 2;
147  // Perhaps yTolerance should be put into preferences?
148  const int yTolerance = 5; // how far from envelope we may be and count as a hit.
149  int distance;
150 
151  // For amplification using the envelope we introduced the idea of contours.
152  // The contours have the same shape as the envelope, which may be partially off-screen.
153  // The contours are closer in to the center line.
154  int ContourSpacing = (int)(rect.height / (2 * (zoomMax - zoomMin)));
155  const int MaxContours = 2;
156 
157  // Adding ContourSpacing/2 selects a region either side of the contour.
158  int yDisplace = yValue - yMisalign - yMouse + ContourSpacing / 2;
159  if (yDisplace > (MaxContours * ContourSpacing))
160  return {};
161  // Subtracting the ContourSpacing/2 we added earlier ensures distance is centred on the contour.
162  distance = abs((yDisplace % ContourSpacing) - ContourSpacing / 2);
163  if (distance >= yTolerance)
164  return {};
165 
166  return HitAnywhere(holder, envelope, timeTrack);
167 }
168 
170 (const TrackPanelMouseEvent &evt, AudacityProject *pProject)
171 {
172  using namespace RefreshCode;
173  const bool unsafe = pProject->IsAudioActive();
174  if ( unsafe )
175  return Cancelled;
176 
177  const wxMouseEvent &event = evt.event;
178  const ViewInfo &viewInfo = pProject->GetViewInfo();
179  const auto pTrack = static_cast<Track*>(evt.pCell.get());
180 
181  if (pTrack &&
182  pTrack->GetKind() == Track::Wave) {
183  WaveTrack *const wt = static_cast<WaveTrack*>(pTrack);
184  if (wt->GetDisplay() != WaveTrack::Waveform)
185  return Cancelled;
186 
187  if (!mEnvelope)
188  return Cancelled;
189 
190  mLog = !wt->GetWaveformSettings().isLinear();
191  wt->GetDisplayBounds(&mLower, &mUpper);
192  mdBRange = wt->GetWaveformSettings().dBRange;
193  mEnvelopeEditor =
194  std::make_unique< EnvelopeEditor >( *mEnvelope, true );
195  mEnvelopeEditorRight.reset();
196 
197  // Assume linked track is wave or null
198  auto partner = static_cast<WaveTrack*>(wt->GetLink());
199  if (partner)
200  {
201  auto clickedEnvelope = partner->GetEnvelopeAtX(event.GetX());
202  if (clickedEnvelope)
203  mEnvelopeEditorRight =
204  std::make_unique< EnvelopeEditor >( *clickedEnvelope, true );
205  }
206  }
207  else if (pTrack &&
208  pTrack->GetKind() == Track::Time)
209  {
210  TimeTrack *const tt = static_cast<TimeTrack*>(pTrack);
211  if (!mEnvelope)
212  return Cancelled;
213  GetTimeTrackData( *pProject, *tt, mdBRange, mLog, mLower, mUpper);
214  mEnvelopeEditor =
215  std::make_unique< EnvelopeEditor >( *mEnvelope, false );
216  mEnvelopeEditorRight.reset();
217  }
218  else
219  return Cancelled;
220 
221  mRect = evt.rect;
222 
223  const bool needUpdate = ForwardEventToEnvelopes(event, viewInfo);
224  return needUpdate ? RefreshCell : RefreshNone;
225 }
226 
228 (const TrackPanelMouseEvent &evt, AudacityProject *pProject)
229 {
230  using namespace RefreshCode;
231  const wxMouseEvent &event = evt.event;
232  const ViewInfo &viewInfo = pProject->GetViewInfo();
233  const bool unsafe = pProject->IsAudioActive();
234  if (unsafe) {
235  this->Cancel(pProject);
236  return RefreshCell | Cancelled;
237  }
238 
239  const bool needUpdate = ForwardEventToEnvelopes(event, viewInfo);
240  return needUpdate ? RefreshCell : RefreshNone;
241 }
242 
244 (const TrackPanelMouseState &, const AudacityProject *pProject)
245 {
246  const bool unsafe = pProject->IsAudioActive();
247  static auto disabledCursor =
248  ::MakeCursor(wxCURSOR_NO_ENTRY, DisabledCursorXpm, 16, 16);
249  static auto envelopeCursor =
250  ::MakeCursor(wxCURSOR_ARROW, EnvCursorXpm, 16, 16);
251 
252  wxString message;
253  if (mTimeTrack)
254  message = _("Click and drag to warp playback time");
255  else
256  message = _("Click and drag to edit the amplitude envelope");
257 
258  return {
259  message,
260  (unsafe
261  ? &*disabledCursor
262  : &*envelopeCursor)
263  };
264 }
265 
267 (const TrackPanelMouseEvent &evt, AudacityProject *pProject,
268  wxWindow *)
269 {
270  const wxMouseEvent &event = evt.event;
271  const ViewInfo &viewInfo = pProject->GetViewInfo();
272  const bool unsafe = pProject->IsAudioActive();
273  if (unsafe)
274  return this->Cancel(pProject);
275 
276  const bool needUpdate = ForwardEventToEnvelopes(event, viewInfo);
277 
278  pProject->PushState(
279  /* i18n-hint: (verb) Audacity has just adjusted the envelope .*/
280  _("Adjusted envelope."),
281  /* i18n-hint: The envelope is a curve that controls the audio loudness.*/
282  _("Envelope")
283  );
284 
285  mEnvelopeEditor.reset();
286  mEnvelopeEditorRight.reset();
287 
288  using namespace RefreshCode;
289  return needUpdate ? RefreshCell : RefreshNone;
290 }
291 
293 {
294  pProject->RollbackState();
295  mEnvelopeEditor.reset();
296  mEnvelopeEditorRight.reset();
298 }
299 
301  (const wxMouseEvent &event, const ViewInfo &viewInfo)
302 {
306 
307  // AS: I'm not sure why we can't let the Envelope take care of
308  // redrawing itself. ?
309  bool needUpdate =
310  mEnvelopeEditor->MouseEvent(
311  event, mRect, viewInfo, mLog, mdBRange, mLower, mUpper);
312 
313  if (mEnvelopeEditorRight)
314  needUpdate |=
315  mEnvelopeEditorRight->MouseEvent(
316  event, mRect, viewInfo, mLog, mdBRange, mLower, mUpper);
317 
318  return needUpdate;
319 }
bool GetDisplayLog() const
Definition: TimeTrack.h:133
std::shared_ptr< UIHandle > UIHandlePtr
Definition: CellularPanel.h:25
std::unique_ptr< EnvelopeEditor > mEnvelopeEditor
std::unique_ptr< EnvelopeEditor > mEnvelopeEditorRight
bool isLinear() const
ViewInfo is used mainly to hold the zooming, selection and scroll information. It also has some statu...
Definition: ViewInfo.h:141
const WaveformSettings & GetWaveformSettings() const
Definition: WaveTrack.cpp:738
Envelope * GetEnvelopeAtX(int xcoord)
Definition: WaveTrack.cpp:2211
double GetRangeLower() const
Definition: TimeTrack.h:127
double PositionToTime(wxInt64 position, wxInt64 origin=0, bool ignoreFisheye=false) const
Definition: ViewInfo.cpp:49
Draggable curve used in TrackPanel for varying amplification.
Definition: Envelope.h:77
float dBr
Definition: ViewInfo.h:53
EnvelopeHandle(const EnvelopeHandle &)=delete
static UIHandlePtr WaveTrackHitTest(std::weak_ptr< EnvelopeHandle > &holder, const wxMouseState &state, const wxRect &rect, const AudacityProject *pProject, const std::shared_ptr< WaveTrack > &wt)
Result Cancel(AudacityProject *pProject) override
Result Release(const TrackPanelMouseEvent &event, AudacityProject *pProject, wxWindow *pParent) override
bool ForwardEventToEnvelopes(const wxMouseEvent &event, const ViewInfo &viewInfo)
Result Drag(const TrackPanelMouseEvent &event, AudacityProject *pProject) override
Result mChangeHighlight
Definition: UIHandle.h:150
void GetDisplayBounds(float *min, float *max) const
Definition: WaveTrack.cpp:325
AudacityProject provides the main window, with tools and tracks contained within it.
Definition: Project.h:176
A kind of Track used to 'warp time'.
Definition: TimeTrack.h:29
unsigned Result
Definition: UIHandle.h:37
double GetRangeUpper() const
Definition: TimeTrack.h:128
static UIHandlePtr HitEnvelope(std::weak_ptr< EnvelopeHandle > &holder, const wxMouseState &state, const wxRect &rect, const AudacityProject *pProject, Envelope *envelope, float zoomMin, float zoomMax, bool dB, float dBRange, bool timeTrack)
virtual ~EnvelopeHandle()
void RollbackState()
Definition: Project.cpp:4754
A Track that contains audio waveform data.
Definition: WaveTrack.h:60
Fundamental data object of Audacity, placed in the TrackPanel. Classes derived form it include the Wa...
Definition: Track.h:102
double GetValue(double t, double sampleDur=0) const
Get envelope value at time t.
Definition: Envelope.cpp:1121
bool IsAudioActive() const
Definition: Project.cpp:1457
_("Move Track &Down")+wxT("\t")+(GetActiveProject() -> GetCommandManager() ->GetKeyFromName(wxT("TrackMoveDown")).Raw()), OnMoveTrack) POPUP_MENU_ITEM(OnMoveTopID, _("Move Track to &Top")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveTop")).Raw()), OnMoveTrack) POPUP_MENU_ITEM(OnMoveBottomID, _("Move Track to &Bottom")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveBottom")).Raw()), OnMoveTrack)#define SET_TRACK_NAME_PLUGIN_SYMBOLclass SetTrackNameCommand:public AudacityCommand
static UIHandlePtr HitAnywhere(std::weak_ptr< EnvelopeHandle > &holder, Envelope *envelope, bool timeTrack)
#define LINEAR_TO_DB(x)
Definition: Audacity.h:217
void PushState(const wxString &desc, const wxString &shortDesc)
Definition: Project.cpp:4714
Track * GetLink() const
Definition: Track.cpp:269
std::shared_ptr< TrackPanelCell > pCell
Result Click(const TrackPanelMouseEvent &event, AudacityProject *pProject) override
std::unique_ptr< wxCursor > MakeCursor(int WXUNUSED(CursorId), const char *const pXpm[36], int HotX, int HotY)
Definition: TrackPanel.cpp:266
WaveTrackDisplay GetDisplay() const
Definition: WaveTrack.h:593
HitTestPreview Preview(const TrackPanelMouseState &state, const AudacityProject *pProject) override
int GetWaveYPos(float value, float min, float max, int height, bool dB, bool outer, float dBr, bool clip)
const ViewInfo & GetViewInfo() const
Definition: Project.h:207
void Enter(bool forward) override
static UIHandlePtr TimeTrackHitTest(std::weak_ptr< EnvelopeHandle > &holder, const wxMouseState &state, const wxRect &rect, const AudacityProject *pProject, const std::shared_ptr< TimeTrack > &tt)