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->GetKind() == Track::Wave) {
182  WaveTrack *const wt = static_cast<WaveTrack*>(pTrack);
183  if (wt->GetDisplay() != WaveTrack::Waveform)
184  return Cancelled;
185 
186  if (!mEnvelope)
187  return Cancelled;
188 
189  mLog = !wt->GetWaveformSettings().isLinear();
190  wt->GetDisplayBounds(&mLower, &mUpper);
191  mdBRange = wt->GetWaveformSettings().dBRange;
192  mEnvelopeEditor =
193  std::make_unique< EnvelopeEditor >( *mEnvelope, true );
194  mEnvelopeEditorRight.reset();
195 
196  // Assume linked track is wave or null
197  auto partner = static_cast<WaveTrack*>(wt->GetLink());
198  if (partner)
199  {
200  auto clickedEnvelope = partner->GetEnvelopeAtX(event.GetX());
201  if (clickedEnvelope)
202  mEnvelopeEditorRight =
203  std::make_unique< EnvelopeEditor >( *clickedEnvelope, true );
204  }
205  }
206  else if (pTrack->GetKind() == Track::Time)
207  {
208  TimeTrack *const tt = static_cast<TimeTrack*>(pTrack);
209  if (!mEnvelope)
210  return Cancelled;
211  GetTimeTrackData( *pProject, *tt, mdBRange, mLog, mLower, mUpper);
212  mEnvelopeEditor =
213  std::make_unique< EnvelopeEditor >( *mEnvelope, false );
214  mEnvelopeEditorRight.reset();
215  }
216  else
217  return Cancelled;
218 
219  mRect = evt.rect;
220 
221  const bool needUpdate = ForwardEventToEnvelopes(event, viewInfo);
222  return needUpdate ? RefreshCell : RefreshNone;
223 }
224 
226 (const TrackPanelMouseEvent &evt, AudacityProject *pProject)
227 {
228  using namespace RefreshCode;
229  const wxMouseEvent &event = evt.event;
230  const ViewInfo &viewInfo = pProject->GetViewInfo();
231  const bool unsafe = pProject->IsAudioActive();
232  if (unsafe) {
233  this->Cancel(pProject);
234  return RefreshCell | Cancelled;
235  }
236 
237  const bool needUpdate = ForwardEventToEnvelopes(event, viewInfo);
238  return needUpdate ? RefreshCell : RefreshNone;
239 }
240 
242 (const TrackPanelMouseState &, const AudacityProject *pProject)
243 {
244  const bool unsafe = pProject->IsAudioActive();
245  static auto disabledCursor =
246  ::MakeCursor(wxCURSOR_NO_ENTRY, DisabledCursorXpm, 16, 16);
247  static auto envelopeCursor =
248  ::MakeCursor(wxCURSOR_ARROW, EnvCursorXpm, 16, 16);
249 
250  wxString message;
251  if (mTimeTrack)
252  message = _("Click and drag to warp playback time");
253  else
254  message = _("Click and drag to edit the amplitude envelope");
255 
256  return {
257  message,
258  (unsafe
259  ? &*disabledCursor
260  : &*envelopeCursor)
261  };
262 }
263 
265 (const TrackPanelMouseEvent &evt, AudacityProject *pProject,
266  wxWindow *)
267 {
268  const wxMouseEvent &event = evt.event;
269  const ViewInfo &viewInfo = pProject->GetViewInfo();
270  const bool unsafe = pProject->IsAudioActive();
271  if (unsafe)
272  return this->Cancel(pProject);
273 
274  const bool needUpdate = ForwardEventToEnvelopes(event, viewInfo);
275 
276  pProject->PushState(
277  /* i18n-hint: (verb) Audacity has just adjusted the envelope .*/
278  _("Adjusted envelope."),
279  /* i18n-hint: The envelope is a curve that controls the audio loudness.*/
280  _("Envelope")
281  );
282 
283  mEnvelopeEditor.reset();
284  mEnvelopeEditorRight.reset();
285 
286  using namespace RefreshCode;
287  return needUpdate ? RefreshCell : RefreshNone;
288 }
289 
291 {
292  pProject->RollbackState();
293  mEnvelopeEditor.reset();
294  mEnvelopeEditorRight.reset();
296 }
297 
299  (const wxMouseEvent &event, const ViewInfo &viewInfo)
300 {
304 
305  // AS: I'm not sure why we can't let the Envelope take care of
306  // redrawing itself. ?
307  bool needUpdate =
308  mEnvelopeEditor->MouseEvent(
309  event, mRect, viewInfo, mLog, mdBRange, mLower, mUpper);
310 
311  if (mEnvelopeEditorRight)
312  needUpdate |=
313  mEnvelopeEditorRight->MouseEvent(
314  event, mRect, viewInfo, mLog, mdBRange, mLower, mUpper);
315 
316  return needUpdate;
317 }
bool GetDisplayLog() const
Definition: TimeTrack.h:133
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:4735
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:101
double GetValue(double t, double sampleDur=0) const
Get envelope value at time t.
Definition: Envelope.cpp:1114
bool IsAudioActive() const
Definition: Project.cpp:1447
std::shared_ptr< UIHandle > UIHandlePtr
Definition: TrackPanel.h:59
_("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:4695
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:274
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)