Audacity  2.2.2
Snap.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  Snap.cpp
6 
7  Dominic Mazzoni
8 
9 **********************************************************************/
10 
11 #include "Audacity.h"
12 #include "Snap.h"
13 
14 #include <algorithm>
15 #include <cstdlib>
16 
17 #include "Project.h"
18 #include "LabelTrack.h"
19 #include "WaveTrack.h"
20 
21 inline bool operator < (SnapPoint s1, SnapPoint s2)
22 {
23  return s1.t < s2.t;
24 }
25 
27 {
28  track = origTrack = t;
29  dstTrack = NULL;
30  clip = c;
31 }
32 
34 {
35 
36 }
37 
39  const ZoomInfo *zoomInfo,
40  const TrackClipArray *clipExclusions,
41  const TrackArray *trackExclusions,
42  bool noTimeSnap,
43  int pixelTolerance)
44 : mConverter(NumericConverter::TIME)
45 {
46  mTracks = tracks;
47  mZoomInfo = zoomInfo;
48  mClipExclusions = clipExclusions;
49  mTrackExclusions = trackExclusions;
50  mPixelTolerance = pixelTolerance;
51  mNoTimeSnap = noTimeSnap;
52 
54  wxASSERT(mProject);
55 
56  mSnapTo = 0;
57  mRate = 0.0;
58  mFormat = {};
59 
60  // Two time points closer than this are considered the same
61  mEpsilon = 1 / 44100.0;
62 
63  Reinit();
64 }
65 
67 {
68 }
69 
71 {
72  int snapTo = mProject->GetSnapTo();
73  double rate = mProject->GetRate();
75 
76  // No need to reinit if these are still the same
77  if (snapTo == mSnapTo && rate == mRate && format == mFormat)
78  {
79  return;
80  }
81 
82  // Save NEW settings
83  mSnapTo = snapTo;
84  mRate = rate;
85  mFormat = format;
86 
87  mSnapPoints.clear();
88 
89  // Grab time-snapping prefs (unless otherwise requested)
90  mSnapToTime = false;
91 
92  // Look up the format string
93  if (mSnapTo != SNAP_OFF && !mNoTimeSnap)
94  {
95  mSnapToTime = true;
98  }
99 
100  // Add a SnapPoint at t=0
101  mSnapPoints.push_back(SnapPoint{});
102 
104  for (const Track *track = iter.First(); track; track = iter.Next())
105  {
106  if (mTrackExclusions &&
107  mTrackExclusions->end() !=
108  std::find(mTrackExclusions->begin(), mTrackExclusions->end(), track))
109  {
110  continue;
111  }
112 
113  if (track->GetKind() == Track::Label)
114  {
115  LabelTrack *labelTrack = (LabelTrack *)track;
116  for (int i = 0, cnt = labelTrack->GetNumLabels(); i < cnt; ++i)
117  {
118  const LabelStruct *label = labelTrack->GetLabel(i);
119  const double t0 = label->getT0();
120  const double t1 = label->getT1();
121  CondListAdd(t0, labelTrack);
122  if (t1 != t0)
123  {
124  CondListAdd(t1, labelTrack);
125  }
126  }
127  }
128  else if (track->GetKind() == Track::Wave)
129  {
130  auto waveTrack = static_cast<const WaveTrack *>(track);
131  for (const auto &clip: waveTrack->GetClips())
132  {
133  if (mClipExclusions)
134  {
135  bool skip = false;
136  for (size_t j = 0, cnt = mClipExclusions->size(); j < cnt; ++j)
137  {
138  if ((*mClipExclusions)[j].track == waveTrack &&
139  (*mClipExclusions)[j].clip == clip.get())
140  {
141  skip = true;
142  break;
143  }
144  }
145 
146  if (skip)
147  {
148  continue;
149  }
150  }
151 
152  CondListAdd(clip->GetStartTime(), waveTrack);
153  CondListAdd(clip->GetEndTime(), waveTrack);
154  }
155  }
156 #ifdef USE_MIDI
157  else if (track->GetKind() == Track::Note)
158  {
159  CondListAdd(track->GetStartTime(), track);
160  CondListAdd(track->GetEndTime(), track);
161  }
162 #endif
163  }
164 
165  // Sort all by time
166  std::sort(mSnapPoints.begin(), mSnapPoints.end());
167 }
168 
169 // Adds to mSnapPoints, filtering by TimeConverter
170 void SnapManager::CondListAdd(double t, const Track *track)
171 {
172  if (mSnapToTime)
173  {
174  mConverter.SetValue(t);
175  }
176 
177  if (!mSnapToTime || mConverter.GetValue() == t)
178  {
179  mSnapPoints.push_back(SnapPoint{ t, track });
180  }
181 }
182 
183 // Return the time of the SnapPoint at a given index
184 double SnapManager::Get(size_t index)
185 {
186  return mSnapPoints[index].t;
187 }
188 
189 // Returns the difference in time between t and the point at a given index
190 wxInt64 SnapManager::PixelDiff(double t, size_t index)
191 {
192  return std::abs(mZoomInfo->TimeToPosition(t, 0) -
193  mZoomInfo->TimeToPosition(Get(index), 0));
194 }
195 
196 // Find the index where this SnapPoint should go in
197 // sorted order, between i0 (inclusive) and i1 (exclusive).
198 size_t SnapManager::Find(double t, size_t i0, size_t i1)
199 {
200  if (i1 <= i0 + 1)
201  {
202  return i0;
203  }
204 
205  size_t half = (i0 + i1) / 2;
206 
207  if (t < Get(half))
208  {
209  return Find(t, i0, half);
210  }
211 
212  return Find(t, half, i1);
213 }
214 
215 // Find the SnapPoint nearest to time t
216 size_t SnapManager::Find(double t)
217 {
218  size_t cnt = mSnapPoints.size();
219  size_t index = Find(t, 0, cnt);
220 
221  // At this point, either index is the closest, or the next one
222  // to the right is. Keep moving to the right until we get a
223  // different value
224  size_t next = index + 1;
225  while (next + 1 < cnt && Get(next) == Get(index))
226  {
227  next++;
228  }
229 
230  // Now return whichever one is closer to time t
231  if (next < cnt && PixelDiff(t, next) < PixelDiff(t, index))
232  {
233  return next;
234  }
235 
236  return index;
237 }
238 
239 // Helper: performs snap-to-points for Snap(). Returns true if a snap happened.
240 bool SnapManager::SnapToPoints(Track *currentTrack,
241  double t,
242  bool rightEdge,
243  double *outT)
244 {
245  *outT = t;
246 
247  size_t cnt = mSnapPoints.size();
248  if (cnt == 0)
249  {
250  return false;
251  }
252 
253  // Find the nearest SnapPoint
254  size_t index = Find(t);
255 
256  // If it's too far away, just give up now
257  if (PixelDiff(t, index) >= mPixelTolerance)
258  {
259  return false;
260  }
261 
262  // Otherwise, search left and right for all of the points
263  // within the allowed range.
264  size_t left = index;
265  size_t right = index;
266  size_t i;
267 
268  while (left > 0 && PixelDiff(t, left - 1) < mPixelTolerance)
269  {
270  left--;
271  }
272 
273  while (right < cnt - 1 && PixelDiff(t, right + 1) < mPixelTolerance)
274  {
275  right++;
276  }
277 
278  if (left == index && right == index)
279  {
280  // Awesome, there's only one point that matches!
281  *outT = Get(index);
282  return true;
283  }
284 
285  size_t indexInThisTrack = 0;
286  size_t countInThisTrack = 0;
287  for (i = left; i <= right; ++i)
288  {
289  if (mSnapPoints[i].track == currentTrack)
290  {
291  indexInThisTrack = i;
292  countInThisTrack++;
293  }
294  }
295 
296  if (countInThisTrack == 1)
297  {
298  // Cool, only one of the points is in the same track, so
299  // we'll use that one.
300  *outT = Get(indexInThisTrack);
301  return true;
302  }
303 
304  if (Get(right) - Get(left) < mEpsilon)
305  {
306  // OK, they're basically the same point
307  if (rightEdge)
308  {
309  *outT = Get(right); // Return rightmost
310  }
311  else {
312  *outT = Get(left); // Return leftmost
313  }
314  return true;
315  }
316 
317  // None of the points matched, bummer.
318  return false;
319 }
320 
322 (Track *currentTrack, double t, bool rightEdge)
323 {
324 
325  SnapResults results;
326  // Check to see if we need to reinitialize
327  Reinit();
328 
329  results.timeSnappedTime = results.outTime = t;
330  results.outCoord = mZoomInfo->TimeToPosition(t);
331 
332  // First snap to points in mSnapPoints
333  results.snappedPoint =
334  SnapToPoints(currentTrack, t, rightEdge, &results.outTime);
335 
336  if (mSnapToTime) {
337  // Find where it would snap time to the grid
338  mConverter.ValueToControls(t, GetActiveProject()->GetSnapTo() == SNAP_NEAREST);
339  mConverter.ControlsToValue();
340  results.timeSnappedTime = mConverter.GetValue();
341  }
342 
343  results.snappedTime = false;
344  if (mSnapToTime)
345  {
346  if (results.snappedPoint)
347  {
348  // Since mSnapPoints only contains points on the grid, we're done
349  results.snappedTime = true;
350  }
351  else
352  {
353  results.outTime = results.timeSnappedTime;
354  results.snappedTime = true;
355  }
356  }
357 
358  if (results.Snapped())
359  results.outCoord = mZoomInfo->TimeToPosition(results.outTime);
360 
361  return results;
362 }
363 
364 /* static */ wxArrayString SnapManager::GetSnapLabels()
365 {
366  wxArrayString labels;
367 
368  labels.Add(_("Off"));
369  labels.Add(_("Nearest"));
370  labels.Add(_("Prior"));
371 
372  return labels;
373 }
374 
375 /* static */ wxArrayString SnapManager::GetSnapValues()
376 {
377  wxArrayString values;
378 
379  values.Add(wxT("Off"));
380  values.Add(wxT("Nearest"));
381  values.Add(wxT("Prior"));
382 
383  return values;
384 }
385 
386 /* static */ const wxString & SnapManager::GetSnapValue(int index)
387 {
388  wxArrayString values = SnapManager::GetSnapValues();
389 
390  if (index >= 0 && index < (int) values.GetCount())
391  {
392  return values[index];
393  }
394 
395  return values[SNAP_OFF];
396 }
397 
398 /* static */ int SnapManager::GetSnapIndex(const wxString & value)
399 {
400  wxArrayString values = SnapManager::GetSnapValues();
401  int index = values.Index(value);
402 
403  if (index != wxNOT_FOUND)
404  {
405  return index;
406  }
407 
408  return SNAP_OFF;
409 }
410 
411 #include "AColor.h"
412 
413 void SnapManager::Draw( wxDC *dc, wxInt64 snap0, wxInt64 snap1 )
414 {
416  if ( snap0 >= 0 ) {
417  AColor::Line(*dc, (int)snap0, 0, (int)snap0, 30000);
418  }
419  if ( snap1 >= 0 ) {
420  AColor::Line(*dc, (int)snap1, 0, (int)snap1, 30000);
421  }
422 }
A list of TrackListNode items.
Definition: Track.h:618
double mEpsilon
Definition: Snap.h:131
Definition: Snap.h:59
const TrackList * mTracks
Definition: Snap.h:124
const Track * First(const TrackList *val=NULL)
Definition: Track.h:464
bool mSnapToTime
Definition: Snap.h:136
wxString label
Definition: Tags.cpp:727
double Get(size_t index)
Definition: Snap.cpp:184
int GetNumLabels() const
SnapResults Snap(Track *currentTrack, double t, bool rightEdge)
Definition: Snap.cpp:322
bool Snapped() const
Definition: Snap.h:81
Definition: Snap.h:52
bool snappedPoint
Definition: Snap.h:78
static int GetSnapIndex(const wxString &value)
Definition: Snap.cpp:398
wxInt64 PixelDiff(double t, size_t index)
Definition: Snap.cpp:190
bool SnapToPoints(Track *currentTrack, double t, bool rightEdge, double *outT)
Definition: Snap.cpp:240
void SetFormatName(const NumericFormatId &formatName)
A LabelTrack is a Track that holds labels (LabelStruct).
Definition: LabelTrack.h:113
const Track * Next(bool skiplinked=false)
Definition: Track.h:468
~SnapManager()
Definition: Snap.cpp:66
const ZoomInfo * mZoomInfo
Definition: Snap.h:127
wxInt64 outCoord
Definition: Snap.h:77
static void Draw(wxDC *dc, wxInt64 snap0, wxInt64 snap1)
Definition: Snap.cpp:413
A LabelStruct holds information for ONE label in a LabelTrack.
Definition: LabelTrack.h:44
int format
Definition: ExportPCM.cpp:56
const AudacityProject * mProject
Definition: Snap.h:123
NumericConverter provides the advanced formatting control used in the selection bar of Audacity...
SnapPointArray mSnapPoints
Definition: Snap.h:132
This allows multiple clips to be a part of one WaveTrack.
Definition: WaveClip.h:176
const LabelStruct * GetLabel(int index) const
Track * track
Definition: Snap.h:41
WaveTrack * dstTrack
Definition: Snap.h:43
void Reinit()
Definition: Snap.cpp:70
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
const TrackArray * mTrackExclusions
Definition: Snap.h:126
int mPixelTolerance
Definition: Snap.h:128
wxInt64 TimeToPosition(double time, wxInt64 origin=0, bool ignoreFisheye=false) const
STM: Converts a project time to screen x position.
Definition: ViewInfo.cpp:59
double mRate
Definition: Snap.h:139
SnapManager(const TrackList *tracks, const ZoomInfo *zoomInfo, const TrackClipArray *clipExclusions=NULL, const TrackArray *trackExclusions=NULL, bool noTimeSnap=false, int pixelTolerance=kPixelTolerance)
Definition: Snap.cpp:38
double t
Definition: Snap.h:68
static void Line(wxDC &dc, wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
Definition: AColor.cpp:122
size_t Find(double t, size_t i0, size_t i1)
Definition: Snap.cpp:198
int GetSnapTo() const
Definition: Project.cpp:5646
~TrackClip()
Definition: Snap.cpp:33
_("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
const TrackClipArray * mClipExclusions
Definition: Snap.h:125
static void SnapGuidePen(wxDC *dc)
Definition: AColor.cpp:391
double getT1() const
Definition: LabelTrack.h:62
AUDACITY_DLL_API AudacityProject * GetActiveProject()
Definition: Project.cpp:308
static wxArrayString GetSnapLabels()
Definition: Snap.cpp:364
bool snappedTime
Definition: Snap.h:79
const NumericFormatId & GetSelectionFormat() const
Definition: Project.cpp:1678
void CondListAdd(double t, const Track *track)
Definition: Snap.cpp:170
double GetRate() const
Definition: Project.h:199
int mSnapTo
Definition: Snap.h:138
NumericFormatId mFormat
Definition: Snap.h:140
static wxArrayString GetSnapValues()
Definition: Snap.cpp:375
void SetValue(double newValue)
double getT0() const
Definition: LabelTrack.h:61
TrackClip(Track *t, WaveClip *c)
Definition: Snap.cpp:26
WaveClip * clip
Definition: Snap.h:44
bool mNoTimeSnap
Definition: Snap.h:129
double outTime
Definition: Snap.h:76
bool operator<(SnapPoint s1, SnapPoint s2)
Definition: Snap.cpp:21
static const wxString & GetSnapValue(int index)
Definition: Snap.cpp:386
Track * origTrack
Definition: Snap.h:42
void SetSampleRate(double sampleRate)
NumericConverter mConverter
Definition: Snap.h:135
double timeSnappedTime
Definition: Snap.h:75