Audacity  3.0.3
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 
12 #include "EnvelopeHandle.h"
13 
14 #include "TrackView.h"
15 
16 #include "../../Envelope.h"
17 #include "../../EnvelopeEditor.h"
18 #include "../../HitTestResult.h"
19 #include "../../prefs/WaveformSettings.h"
20 #include "../../ProjectAudioIO.h"
21 #include "../../ProjectHistory.h"
22 #include "../../RefreshCode.h"
23 #include "../../TimeTrack.h"
24 #include "../../TrackArtist.h"
25 #include "../../TrackPanelMouseEvent.h"
26 #include "../../ViewInfo.h"
27 #include "../../WaveTrack.h"
28 #include "../../../images/Cursors.h"
29 
31  : mEnvelope{ pEnvelope }
32 {
33 }
34 
36 {
37 #ifdef EXPERIMENTAL_TRACK_PANEL_HIGHLIGHTING
39 #endif
40 }
41 
43 {}
44 
46 (std::weak_ptr<EnvelopeHandle> & WXUNUSED(holder), Envelope *envelope, bool timeTrack)
47 {
48  auto result = std::make_shared<EnvelopeHandle>( envelope );
49  result->mTimeTrack = timeTrack;
50  return result;
51 }
52 
53 namespace {
55  (const AudacityProject &project, const TimeTrack &tt,
56  double &dBRange, bool &dB, float &zoomMin, float &zoomMax)
57  {
58  const auto &viewInfo = ViewInfo::Get( project );
59  dBRange = viewInfo.dBr;
60  dB = tt.GetDisplayLog();
61  zoomMin = tt.GetRangeLower(), zoomMax = tt.GetRangeUpper();
62  if (dB) {
63  // MB: silly way to undo the work of GetWaveYPos while still getting a logarithmic scale
64  zoomMin = LINEAR_TO_DB(std::max(1.0e-7, double(zoomMin))) / dBRange + 1.0;
65  zoomMax = LINEAR_TO_DB(std::max(1.0e-7, double(zoomMax))) / dBRange + 1.0;
66  }
67  }
68 }
69 
71 (std::weak_ptr<EnvelopeHandle> &holder,
72  const wxMouseState &state, const wxRect &rect,
73  const AudacityProject *pProject, const std::shared_ptr<TimeTrack> &tt)
74 {
75  auto envelope = tt->GetEnvelope();
76  if (!envelope)
77  return {};
78  bool dB;
79  double dBRange;
80  float zoomMin, zoomMax;
81  GetTimeTrackData( *pProject, *tt, dBRange, dB, zoomMin, zoomMax);
83  (holder, state, rect, pProject, envelope, zoomMin, zoomMax, dB, dBRange,
84  true);
85 }
86 
88 (std::weak_ptr<EnvelopeHandle> &holder,
89  const wxMouseState &state, const wxRect &rect,
90  const AudacityProject *pProject, const std::shared_ptr<WaveTrack> &wt)
91 {
94  auto &viewInfo = ViewInfo::Get(*pProject);
95  auto time = viewInfo.PositionToTime(state.m_x, rect.GetX());
96  Envelope *const envelope = wt->GetEnvelopeAtTime(time);
97 
98  if (!envelope)
99  return {};
100 
101  // Get envelope point, range 0.0 to 1.0
102  const bool dB = !wt->GetWaveformSettings().isLinear();
103 
104  float zoomMin, zoomMax;
105  wt->GetDisplayBounds(&zoomMin, &zoomMax);
106 
107  const float dBRange = wt->GetWaveformSettings().dBRange;
108 
110  (holder, state, rect, pProject, envelope, zoomMin, zoomMax, dB, dBRange, false);
111 }
112 
114 (std::weak_ptr<EnvelopeHandle> &holder,
115  const wxMouseState &state, const wxRect &rect, const AudacityProject *pProject,
116  Envelope *envelope, float zoomMin, float zoomMax,
117  bool dB, float dBRange, bool timeTrack)
118 {
119  const auto &viewInfo = ViewInfo::Get( *pProject );
120 
121  const double envValue =
122  envelope->GetValue(viewInfo.PositionToTime(state.m_x, rect.x));
123 
124  // Get y position of envelope point.
125  int yValue = GetWaveYPos(envValue,
126  zoomMin, zoomMax,
127  rect.height, dB, true, dBRange, false) + rect.y;
128 
129  // Get y position of center line
130  int ctr = GetWaveYPos(0.0,
131  zoomMin, zoomMax,
132  rect.height, dB, true, dBRange, false) + rect.y;
133 
134  // Get y distance of mouse from center line (in pixels).
135  int yMouse = abs(ctr - state.m_y);
136  // Get y distance of envelope from center line (in pixels)
137  yValue = abs(ctr - yValue);
138 
139  // JKC: It happens that the envelope is actually drawn offset from its
140  // 'true' position (it is 3 pixels wide). yMisalign is really a fudge
141  // factor to allow us to hit it exactly, but I wouldn't dream of
142  // calling it yFudgeFactor :)
143  const int yMisalign = 2;
144  // Perhaps yTolerance should be put into preferences?
145  const int yTolerance = 5; // how far from envelope we may be and count as a hit.
146  int distance;
147 
148  // For amplification using the envelope we introduced the idea of contours.
149  // The contours have the same shape as the envelope, which may be partially off-screen.
150  // The contours are closer in to the center line.
151  int ContourSpacing = (int)(rect.height / (2 * (zoomMax - zoomMin)));
152  const int MaxContours = 2;
153 
154  // Adding ContourSpacing/2 selects a region either side of the contour.
155  int yDisplace = yValue - yMisalign - yMouse + ContourSpacing / 2;
156  if (yDisplace > (MaxContours * ContourSpacing))
157  return {};
158  // Subtracting the ContourSpacing/2 we added earlier ensures distance is centred on the contour.
159  distance = abs((yDisplace % ContourSpacing) - ContourSpacing / 2);
160  if (distance >= yTolerance)
161  return {};
162 
163  return HitAnywhere(holder, envelope, timeTrack);
164 }
165 
167 (const TrackPanelMouseEvent &evt, AudacityProject *pProject)
168 {
169  using namespace RefreshCode;
170  const bool unsafe = ProjectAudioIO::Get( *pProject ).IsAudioActive();
171  if ( unsafe )
172  return Cancelled;
173 
174  const wxMouseEvent &event = evt.event;
175  const auto &viewInfo = ViewInfo::Get( *pProject );
176  const auto pView = std::static_pointer_cast<TrackView>(evt.pCell);
177  const auto pTrack = pView ? pView->FindTrack().get() : nullptr;
178 
179  mEnvelopeEditors.clear();
180 
181  unsigned result = Cancelled;
182  if (pTrack)
183  result = pTrack->TypeSwitch< decltype(RefreshNone) >(
184  [&](WaveTrack *wt) {
185  if (!mEnvelope)
186  return Cancelled;
187 
188  mLog = !wt->GetWaveformSettings().isLinear();
189  wt->GetDisplayBounds(&mLower, &mUpper);
190  mdBRange = wt->GetWaveformSettings().dBRange;
191  auto channels = TrackList::Channels( wt );
192  for ( auto channel : channels ) {
193  if (channel == wt)
194  mEnvelopeEditors.push_back(
195  std::make_unique< EnvelopeEditor >( *mEnvelope, true ) );
196  else {
197  auto time =
198  viewInfo.PositionToTime(event.GetX(), evt.rect.GetX());
199  auto e2 = channel->GetEnvelopeAtTime(time);
200  if (e2)
201  mEnvelopeEditors.push_back(
202  std::make_unique< EnvelopeEditor >( *e2, true ) );
203  else {
204  // There isn't necessarily an envelope there; no guarantee a
205  // linked track has the same WaveClip structure...
206  }
207  }
208  }
209 
210  return RefreshNone;
211  },
212  [&](TimeTrack *tt) {
213  if (!mEnvelope)
214  return Cancelled;
215  GetTimeTrackData( *pProject, *tt, mdBRange, mLog, mLower, mUpper);
216  mEnvelopeEditors.push_back(
217  std::make_unique< EnvelopeEditor >( *mEnvelope, false )
218  );
219 
220  return RefreshNone;
221  },
222  [](Track *) {
223  return Cancelled;
224  }
225  );
226 
227  if (result & Cancelled)
228  return result;
229 
230  mRect = evt.rect;
231 
232  const bool needUpdate = ForwardEventToEnvelopes(event, viewInfo);
233  return needUpdate ? RefreshCell : RefreshNone;
234 }
235 
237 (const TrackPanelMouseEvent &evt, AudacityProject *pProject)
238 {
239  using namespace RefreshCode;
240  const wxMouseEvent &event = evt.event;
241  const auto &viewInfo = ViewInfo::Get( *pProject );
242  const bool unsafe = ProjectAudioIO::Get( *pProject ).IsAudioActive();
243  if (unsafe) {
244  this->Cancel(pProject);
245  return RefreshCell | Cancelled;
246  }
247 
248  const bool needUpdate = ForwardEventToEnvelopes(event, viewInfo);
249  return needUpdate ? RefreshCell : RefreshNone;
250 }
251 
253 (const TrackPanelMouseState &, AudacityProject *pProject)
254 {
255  const bool unsafe = ProjectAudioIO::Get( *pProject ).IsAudioActive();
256  static auto disabledCursor =
257  ::MakeCursor(wxCURSOR_NO_ENTRY, DisabledCursorXpm, 16, 16);
258  static auto envelopeCursor =
259  ::MakeCursor(wxCURSOR_ARROW, EnvCursorXpm, 16, 16);
260 
261  auto message = mTimeTrack
262  ? XO("Click and drag to warp playback time")
263  : XO("Click and drag to edit the amplitude envelope");
264 
265  return {
266  message,
267  (unsafe
268  ? &*disabledCursor
269  : &*envelopeCursor)
270  };
271 }
272 
274 (const TrackPanelMouseEvent &evt, AudacityProject *pProject,
275  wxWindow *)
276 {
277  const wxMouseEvent &event = evt.event;
278  const auto &viewInfo = ViewInfo::Get( *pProject );
279  const bool unsafe = ProjectAudioIO::Get( *pProject ).IsAudioActive();
280  if (unsafe)
281  return this->Cancel(pProject);
282 
283  const bool needUpdate = ForwardEventToEnvelopes(event, viewInfo);
284 
285  ProjectHistory::Get( *pProject ).PushState(
286  /* i18n-hint: (verb) Audacity has just adjusted the envelope .*/
287  XO("Adjusted envelope."),
288  /* i18n-hint: The envelope is a curve that controls the audio loudness.*/
289  XO("Envelope")
290  );
291 
292  mEnvelopeEditors.clear();
293 
294  using namespace RefreshCode;
295  return needUpdate ? RefreshCell : RefreshNone;
296 }
297 
299 {
300  ProjectHistory::Get( *pProject ).RollbackState();
301  mEnvelopeEditors.clear();
303 }
304 
306  (const wxMouseEvent &event, const ViewInfo &viewInfo)
307 {
311 
312  // AS: I'm not sure why we can't let the Envelope take care of
313  // redrawing itself. ?
314  bool needUpdate = false;
315  for (const auto &pEditor : mEnvelopeEditors) {
316  needUpdate =
317  pEditor->MouseEvent(
318  event, mRect, viewInfo, mLog, mdBRange, mLower, mUpper)
319  || needUpdate;
320  }
321 
322  return needUpdate;
323 }
TrackPanelMouseEvent::pCell
std::shared_ptr< TrackPanelCell > pCell
Definition: TrackPanelMouseEvent.h:61
WaveformSettings::isLinear
bool isLinear() const
Definition: WaveformSettings.h:71
ViewInfo::Get
static ViewInfo & Get(AudacityProject &project)
Definition: ViewInfo.cpp:156
TimeTrack::GetRangeLower
double GetRangeLower() const
Definition: TimeTrack.cpp:119
WaveTrack
A Track that contains audio waveform data.
Definition: WaveTrack.h:69
TrackPanelMouseEvent::rect
const wxRect & rect
Definition: TrackPanelMouseEvent.h:59
RefreshCode::RefreshNone
@ RefreshNone
Definition: RefreshCode.h:21
TrackView.h
TimeTrack::GetRangeUpper
double GetRangeUpper() const
Definition: TimeTrack.cpp:124
EnvelopeHandle::WaveTrackHitTest
static UIHandlePtr WaveTrackHitTest(std::weak_ptr< EnvelopeHandle > &holder, const wxMouseState &state, const wxRect &rect, const AudacityProject *pProject, const std::shared_ptr< WaveTrack > &wt)
Definition: EnvelopeHandle.cpp:88
TrackList::Channels
static auto Channels(TrackType *pTrack) -> TrackIterRange< TrackType >
Definition: Track.h:1468
MakeCursor
std::unique_ptr< wxCursor > MakeCursor(int WXUNUSED(CursorId), const char *const pXpm[36], int HotX, int HotY)
Definition: TrackPanel.cpp:176
ViewInfo
Definition: ViewInfo.h:173
Envelope
Piecewise linear or piecewise exponential function from double to double.
Definition: Envelope.h:71
EnvelopeHandle::ForwardEventToEnvelopes
bool ForwardEventToEnvelopes(const wxMouseEvent &event, const ViewInfo &viewInfo)
Definition: EnvelopeHandle.cpp:306
EnvelopeHandle::~EnvelopeHandle
virtual ~EnvelopeHandle()
Definition: EnvelopeHandle.cpp:42
WaveTrack::GetEnvelopeAtTime
Envelope * GetEnvelopeAtTime(double time)
Definition: WaveTrack.cpp:2126
RefreshCode::Cancelled
@ Cancelled
Definition: RefreshCode.h:23
XO
#define XO(s)
Definition: Internat.h:31
EnvelopeHandle::mTimeTrack
bool mTimeTrack
Definition: EnvelopeHandle.h:90
EnvelopeHandle::mdBRange
double mdBRange
Definition: EnvelopeHandle.h:85
ProjectAudioIO::Get
static ProjectAudioIO & Get(AudacityProject &project)
Definition: ProjectAudioIO.cpp:23
EnvelopeHandle::mEnvelope
Envelope * mEnvelope
Definition: EnvelopeHandle.h:87
ProjectAudioIO::IsAudioActive
bool IsAudioActive() const
Definition: ProjectAudioIO.cpp:52
EnvelopeHandle::Preview
HitTestPreview Preview(const TrackPanelMouseState &state, AudacityProject *pProject) override
Definition: EnvelopeHandle.cpp:253
TimeTrack::GetDisplayLog
bool GetDisplayLog() const
Definition: TimeTrack.h:88
EnvelopeHandle::mLower
float mLower
Definition: EnvelopeHandle.h:84
EnvelopeHandle::Click
Result Click(const TrackPanelMouseEvent &event, AudacityProject *pProject) override
Definition: EnvelopeHandle.cpp:167
UIHandle::mChangeHighlight
Result mChangeHighlight
Definition: UIHandle.h:133
EnvelopeHandle::EnvelopeHandle
EnvelopeHandle(const EnvelopeHandle &)=delete
UIHandle::Result
unsigned Result
Definition: UIHandle.h:37
EnvelopeHandle::Release
Result Release(const TrackPanelMouseEvent &event, AudacityProject *pProject, wxWindow *pParent) override
Definition: EnvelopeHandle.cpp:274
TimeTrack::GetEnvelope
BoundedEnvelope * GetEnvelope()
Definition: TimeTrack.h:77
UIHandlePtr
std::shared_ptr< UIHandle > UIHandlePtr
Definition: CellularPanel.h:28
HitTestPreview
Definition: HitTestResult.h:20
EnvelopeHandle::Enter
void Enter(bool forward, AudacityProject *) override
Definition: EnvelopeHandle.cpp:35
WaveTrack::GetWaveformSettings
const WaveformSettings & GetWaveformSettings() const
Definition: WaveTrack.cpp:711
RefreshCode::RefreshCell
@ RefreshCell
Definition: RefreshCode.h:24
EnvelopeHandle::mLog
bool mLog
Definition: EnvelopeHandle.h:83
EnvelopeHandle::mEnvelopeEditors
std::vector< std::unique_ptr< EnvelopeEditor > > mEnvelopeEditors
Definition: EnvelopeHandle.h:88
Envelope::GetValue
double GetValue(double t, double sampleDur=0) const
Get envelope value at time t.
Definition: Envelope.cpp:827
ProjectHistory::PushState
void PushState(const TranslatableString &desc, const TranslatableString &shortDesc)
Definition: ProjectHistory.cpp:90
Track
Abstract base class for an object holding data associated with points on a time axis.
Definition: Track.h:239
EnvelopeHandle::Drag
Result Drag(const TrackPanelMouseEvent &event, AudacityProject *pProject) override
Definition: EnvelopeHandle.cpp:237
ProjectHistory::RollbackState
void RollbackState()
Definition: ProjectHistory.cpp:117
AudacityProject
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:113
anonymous_namespace{EnvelopeHandle.cpp}::GetTimeTrackData
void GetTimeTrackData(const AudacityProject &project, const TimeTrack &tt, double &dBRange, bool &dB, float &zoomMin, float &zoomMax)
Definition: EnvelopeHandle.cpp:55
EnvelopeHandle.h
WaveformSettings::dBRange
int dBRange
Definition: WaveformSettings.h:68
TrackPanelMouseEvent
Definition: TrackPanelMouseEvent.h:46
TrackPanelMouseState
Definition: TrackPanelMouseEvent.h:28
GetWaveYPos
int GetWaveYPos(float value, float min, float max, int height, bool dB, bool outer, float dBr, bool clip)
Definition: TrackArtist.cpp:135
LINEAR_TO_DB
#define LINEAR_TO_DB(x)
Definition: MemoryX.h:629
EnvelopeHandle::TimeTrackHitTest
static UIHandlePtr TimeTrackHitTest(std::weak_ptr< EnvelopeHandle > &holder, const wxMouseState &state, const wxRect &rect, const AudacityProject *pProject, const std::shared_ptr< TimeTrack > &tt)
Definition: EnvelopeHandle.cpp:71
WaveTrack::GetDisplayBounds
void GetDisplayBounds(float *min, float *max) const
Definition: WaveTrack.cpp:255
RefreshCode
Namespace containing an enum 'what to do on a refresh?'.
Definition: RefreshCode.h:16
EnvelopeHandle::HitEnvelope
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)
Definition: EnvelopeHandle.cpp:114
TrackPanelMouseEvent::event
wxMouseEvent & event
Definition: TrackPanelMouseEvent.h:58
TimeTrack
A kind of Track used to 'warp time'.
Definition: TimeTrack.h:24
EnvelopeHandle::mUpper
float mUpper
Definition: EnvelopeHandle.h:84
ProjectHistory::Get
static ProjectHistory & Get(AudacityProject &project)
Definition: ProjectHistory.cpp:26
EnvelopeHandle::HitAnywhere
static UIHandlePtr HitAnywhere(std::weak_ptr< EnvelopeHandle > &holder, Envelope *envelope, bool timeTrack)
Definition: EnvelopeHandle.cpp:46
EnvelopeHandle::Cancel
Result Cancel(AudacityProject *pProject) override
Definition: EnvelopeHandle.cpp:298
EnvelopeHandle::mRect
wxRect mRect
Definition: EnvelopeHandle.h:82