Audacity  3.0.3
Scrubbing.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3 Audacity: A Digital Audio Editor
4 
5 Scrubbing.cpp
6 
7 Paul Licameli split from TrackPanel.cpp
8 
9 **********************************************************************/
10 
11 
12 #include "Scrubbing.h"
13 
14 #include <functional>
15 
16 #include "../../AudioIO.h"
17 #include "../../CommonCommandFlags.h"
18 #include "Project.h"
19 #include "../../ProjectAudioIO.h"
20 #include "../../ProjectAudioManager.h"
21 #include "../../ProjectHistory.h"
22 #include "../../ProjectWindows.h"
23 #include "../../ProjectSettings.h"
24 #include "ProjectStatus.h"
25 #include "../../ScrubState.h"
26 #include "../../Track.h"
27 #include "ViewInfo.h"
28 #include "../../WaveTrack.h"
29 #include "../../prefs/PlaybackPrefs.h"
30 #include "../../prefs/TracksPrefs.h"
31 #include "../../toolbars/ToolManager.h"
32 
33 #undef USE_TRANSCRIPTION_TOOLBAR
34 
35 
36 #include <algorithm>
37 
38 #include <wx/app.h>
39 #include <wx/menu.h>
40 
41 // Yet another experimental scrub would drag the track under a
42 // stationary play head
43 #undef DRAG_SCRUB
44 
45 enum {
46  // PRL:
47  // Mouse must move at least this far to distinguish ctrl-drag to scrub
48  // from ctrl+click for playback.
50 
51 #ifdef EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL
52  ScrubSpeedStepsPerOctave = 4,
53 #endif
54 
56 };
57 
58 static const double MinStutter = 0.2;
59 // static const double MaxDragSpeed = 1.0;
60 
61 namespace {
62  double FindScrubbingSpeed(const ViewInfo &viewInfo, double maxScrubSpeed, double screen, double timeAtMouse)
63  {
64  // Map a time (which was mapped from a mouse position)
65  // to a speed.
66  // Map times to positive and negative speeds,
67  // with the time at the midline of the screen mapping to 0,
68  // and the extremes to the maximum scrub speed.
69 
70  auto partScreen = screen * TracksPrefs::GetPinnedHeadPositionPreference();
71  const double origin = viewInfo.h + partScreen;
72  if (timeAtMouse >= origin)
73  partScreen = screen - partScreen;
74 
75  // There are various snapping zones that are this fraction of screen:
76  const double snap = 0.05;
77 
78  // By shrinking denom a bit, we make margins left and right
79  // that snap to maximum and negative maximum speeds.
80  const double factor = 1.0 - (snap * 2);
81  const double denom = factor * partScreen;
82  double fraction = (denom <= 0.0) ? 0.0 :
83  std::min(1.0, fabs(timeAtMouse - origin) / denom);
84 
85  // Snap to 1.0 and -1.0
86  const double unity = 1.0 / maxScrubSpeed;
87  const double tolerance = snap / factor;
88  // Make speeds near 1 available too by remapping fractions outside
89  // this snap zone
90  if (fraction <= unity - tolerance)
91  fraction *= unity / (unity - tolerance);
92  else if (fraction < unity + tolerance)
93  fraction = unity;
94  else
95  fraction = unity + (fraction - (unity + tolerance)) *
96  (1.0 - unity) / (1.0 - (unity + tolerance));
97 
98  double result = fraction * maxScrubSpeed;
99  if (timeAtMouse < origin)
100  result *= -1.0;
101  return result;
102  }
103 
104  double FindSeekSpeed(const ViewInfo &viewInfo, double maxScrubSpeed, double screen, double timeAtMouse)
105  {
106  // Map a time (which was mapped from a mouse position)
107  // to a signed skip speed: a multiplier of the stutter duration,
108  // by which to advance the play position.
109  // (The stutter will play at unit speed.)
110 
111  // Times near the midline of the screen map to skip-less play,
112  // and the extremes to a value proportional to maximum scrub speed.
113 
114  // If the maximum scrubbing speed defaults to 1.0 when you begin to scroll-scrub,
115  // the extreme skipping for scroll-seek needs to be larger to be useful.
116  static const double ARBITRARY_MULTIPLIER = 10.0;
117  const double extreme = std::max(1.0, maxScrubSpeed * ARBITRARY_MULTIPLIER);
118 
119  // Width of visible track area, in time terms:
120  auto partScreen = screen * TracksPrefs::GetPinnedHeadPositionPreference();
121  const double origin = viewInfo.h + partScreen;
122  if (timeAtMouse >= origin)
123  partScreen = screen - partScreen;
124 
125  // The snapping zone is this fraction of screen, on each side of the
126  // center line:
127  const double snap = 0.05;
128  const double fraction = (partScreen <= 0.0) ? 0.0 :
129  std::max(snap, std::min(1.0, fabs(timeAtMouse - origin) / partScreen));
130 
131  double result = 1.0 + ((fraction - snap) / (1.0 - snap)) * (extreme - 1.0);
132  if (timeAtMouse < origin)
133  result *= -1.0;
134  return result;
135  }
136 }
137 
138 #ifdef USE_SCRUB_THREAD
139 
140 class Scrubber::ScrubPollerThread final : public wxThread {
141 public:
143  : wxThread { }
144  , mScrubber(scrubber)
145  {}
146  ExitCode Entry() override;
147 
148 private:
150 };
151 
153 {
154  while( !TestDestroy() )
155  {
156  wxThread::Sleep(ScrubPollInterval_ms);
157  mScrubber.ContinueScrubbingPoll();
158  }
159  return 0;
160 }
161 
162 #endif
163 
165 {
168 }
169 
170 class Scrubber::ScrubPoller : public wxTimer
171 {
172 public:
173  ScrubPoller(Scrubber &scrubber) : mScrubber( scrubber ) {}
174 
175 private:
176  void Notify() override;
177 
179 };
180 
182 {
183  // Call Continue functions here in a timer handler
184  // rather than in SelectionHandleDrag()
185  // so that even without drag events, we can instruct the play head to
186  // keep approaching the mouse cursor, when its maximum speed is limited.
187 
188 #ifndef USE_SCRUB_THREAD
189  // If there is no helper thread, this main thread timer is responsible
190  // for playback and for UI
192 #endif
194 }
195 
197  []( AudacityProject &parent ){
198  return std::make_shared< Scrubber >( &parent ); }
199 };
200 
202 {
203  return project.AttachedObjects::Get< Scrubber >( key );
204 }
205 
206 const Scrubber &Scrubber::Get( const AudacityProject &project )
207 {
208  return Get( const_cast< AudacityProject & >( project ) );
209 }
210 
212  : mScrubToken(-1)
213  , mPaused(true)
215  , mScrubStartPosition(-1)
216 #ifdef EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL
217  , mSmoothScrollingScrub(false)
218  , mLogMaxScrubSpeed(0)
219 #endif
220 
221  , mProject(project)
222  , mPoller { std::make_unique<ScrubPoller>(*this) }
223  , mOptions {}
224 
225 {
226  if (wxTheApp)
227  wxTheApp->Bind
228  (wxEVT_ACTIVATE_APP,
230 
231  UpdatePrefs();
232 }
233 
235 {
236 #ifdef USE_SCRUB_THREAD
237  if (mpThread)
238  mpThread->Delete();
239 #endif
240 }
241 
242 static const auto HasWaveDataPred =
243  [](const AudacityProject &project){
244  auto range = TrackList::Get( project ).Any<const WaveTrack>()
245  + [](const WaveTrack *pTrack){
246  return pTrack->GetEndTime() > pTrack->GetStartTime();
247  };
248  return !range.empty();
249  };
250 
251 static const ReservedCommandFlag
254 }; return flag; } // jkc
255 
256 namespace {
257  struct MenuItem {
262  void (Scrubber::*memFn)(const CommandContext&);
263  bool seek;
264  bool (Scrubber::*StatusTest)() const;
265 
266  const TranslatableString &GetStatus() const { return status; }
267  };
268  using MenuItems = std::vector< MenuItem >;
270  {
271  static MenuItems theItems{
272  /* i18n-hint: These commands assist the user in finding a sound by ear. ...
273  "Scrubbing" is variable-speed playback, ...
274  "Seeking" is normal speed playback but with skips, ...
275  */
276  { wxT("Scrub"), XXO("&Scrub"), XO("Scrubbing"),
279  },
280 
281  /* i18n-hint: These commands assist the user in finding a sound by ear. ...
282  "Scrubbing" is variable-speed playback, ...
283  "Seeking" is normal speed playback but with skips, ...
284  */
285  { wxT("Seek"), XXO("See&k"), XO("Seeking"),
288  },
289 
290  /* i18n-hint: These commands assist the user in finding a sound by ear. ...
291  "Scrubbing" is variable-speed playback, ...
292  "Seeking" is normal speed playback but with skips, ...
293  */
294  { wxT("ToggleScrubRuler"), XXO("Scrub &Ruler"), {},
297  },
298  };
299  return theItems;
300  };
301 
302  inline const MenuItem &FindMenuItem(bool seek)
303  {
304  return *std::find_if(menuItems().begin(), menuItems().end(),
305  [=](const MenuItem &item) {
306  return seek == item.seek;
307  }
308  );
309  }
310 
311 }
312 
314  // Assume xx is relative to the left edge of TrackPanel!
315  wxCoord xx, bool smoothScrolling, bool seek
316 )
317 {
318  // Don't actually start scrubbing, but collect some information
319  // needed for the decision to start scrubbing later when handling
320  // drag events.
321  mSmoothScrollingScrub = smoothScrolling;
322 
323  auto &projectAudioManager = ProjectAudioManager::Get( *mProject );
324 
325  // Stop any play in progress
326  // Bug 1492: mCancelled to stop us collapsing the selected region.
327  mCancelled = true;
328  projectAudioManager.Stop();
329  mCancelled = false;
330 
331  // Usually the timer handler of TrackPanel does this, but we do this now,
332  // so that same timer does not StopPlaying() again after this function and destroy
333  // scrubber state
334  ProjectAudioIO::Get( *mProject ).SetAudioIOToken(0);
335 
336  mSeeking = seek;
337  CheckMenuItems();
338 
339  // Commented out for Bug 1421
340  // mSeeking
341  // ? ControlToolBar::PlayAppearance::Seek
342  // : ControlToolBar::PlayAppearance::Scrub);
343 
344  mScrubStartPosition = xx;
345  mCancelled = false;
346 }
347 
350 {
351  return [options]() -> std::unique_ptr<PlaybackPolicy>
352  {
353  return std::make_unique<ScrubbingPlaybackPolicy>(options);
354  };
355 }
356 
357 
358 #ifdef EXPERIMENTAL_SCRUBBING_SUPPORT
359 // Assume xx is relative to the left edge of TrackPanel!
360 bool Scrubber::MaybeStartScrubbing(wxCoord xx)
361 {
362  if (mScrubStartPosition < 0)
363  return false;
364  if (IsScrubbing())
365  return false;
366  else {
367  const auto state = ::wxGetMouseState();
368  mDragging = state.LeftIsDown();
369 
370  auto gAudioIO = AudioIO::Get();
371  const bool busy = gAudioIO->IsBusy();
372  if (busy && gAudioIO->GetNumCaptureChannels() > 0) {
373  // Do not stop recording, and don't try to start scrubbing after
374  // recording stops
375  mScrubStartPosition = -1;
376  return false;
377  }
378 
379  wxCoord position = xx;
380  if (abs(mScrubStartPosition - position) >= SCRUBBING_PIXEL_TOLERANCE) {
381  auto &viewInfo = ViewInfo::Get( *mProject );
382  auto &projectAudioManager = ProjectAudioManager::Get( *mProject );
383  double maxTime = TrackList::Get( *mProject ).GetEndTime();
384  const int leftOffset = viewInfo.GetLeftOffset();
385  double time0 = std::min(maxTime,
386  viewInfo.PositionToTime(mScrubStartPosition, leftOffset)
387  );
388  double time1 = std::min(maxTime,
389  viewInfo.PositionToTime(position, leftOffset)
390  );
391  if (time1 != time0) {
392  if (busy) {
393  position = mScrubStartPosition;
394  projectAudioManager.Stop();
395  mScrubStartPosition = position;
396  }
397 
398 #ifdef DRAG_SCRUB
400  auto delta = time0 - time1;
401  time0 = std::max(0.0, std::min(maxTime,
402  viewInfo.h +
403  (viewInfo.GetScreenEndTime() - viewInfo.h)
405  ));
406  time1 = time0 + delta;
407  }
408 #endif
409  mSpeedPlaying = false;
410  mKeyboardScrubbing = false;
411  auto options =
413 
414 #ifndef USE_SCRUB_THREAD
415  // Yuck, we either have to poll "by hand" when scrub polling doesn't
416  // work with a thread, or else yield to timer messages, but that would
417  // execute too much else
418  options.playbackStreamPrimer = [this](){
420  return ScrubPollInterval_ms;
421  };
422 #endif
423  options.playNonWaveTracks = false;
424  options.envelope = nullptr;
425  mOptions.delay = (ScrubPollInterval_ms / 1000.0);
426  mOptions.isPlayingAtSpeed = false;
428  mOptions.initSpeed = 0;
429  mOptions.minSpeed = 0.0;
430 #ifdef USE_TRANSCRIPTION_TOOLBAR
431  if (!mAlwaysSeeking) {
432  // Take the starting speed limit from the transcription toolbar,
433  // but it may be varied during the scrub.
435  ProjectSettings::Get( *mProject ).GetPlaySpeed();
436  }
437 #else
438  // That idea seems unpopular... just make it one for move-scrub,
439  // but big for drag-scrub
440 #ifdef DRAG_SCRUB
441  mMaxSpeed = mOptions.maxSpeed = mDragging ? MaxDragSpeed : 1.0;
442 #else
443  mMaxSpeed = mOptions.maxSpeed = 1.0;
444 #endif
445 
446 #endif
447  mOptions.minTime = 0;
448  mOptions.maxTime =
449  std::max(0.0, TrackList::Get( *mProject ).GetEndTime());
451 #ifdef DRAG_SCRUB
452  mDragging ? 0.0 :
453 #endif
454  std::max(0.0, MinStutter);
455 
456  const bool backwards = time1 < time0;
457 #ifdef EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL
458  static const double maxScrubSpeedBase =
459  pow(2.0, 1.0 / ScrubSpeedStepsPerOctave);
460  mLogMaxScrubSpeed = floor(0.5 +
461  log(mMaxSpeed) / log(maxScrubSpeedBase)
462  );
463 #endif
465 
466  // Must start the thread and poller first or else PlayPlayRegion
467  // will insert some silence
468  StartPolling();
469  auto cleanup = finally([this]{
470  if (mScrubToken < 0)
471  StopPolling();
472  });
473 
474  options.policyFactory = ScrubbingPlaybackPolicyFactory(mOptions);
475  mScrubToken =
476  projectAudioManager.PlayPlayRegion(
477  SelectedRegion(time0, time1), options,
478  PlayMode::normalPlay, backwards);
479  if (mScrubToken <= 0) {
480  // Bug1627 (part of it):
481  // infinite error spew when trying to start scrub:
482  // If failed for reasons of audio device problems, do not try
483  // again with repeated timer ticks.
484  mScrubStartPosition = -1;
485  return false;
486  }
487  }
488  }
489  else
490  // Wait to test again
491  ;
492 
493  if (IsScrubbing()) {
494  mLastScrubPosition = xx;
495  }
496 
497  // Return true whether we started scrub, or are still waiting to decide.
498  return true;
499  }
500 }
501 
502 
503 
504 bool Scrubber::StartSpeedPlay(double speed, double time0, double time1)
505 {
506  if (IsScrubbing())
507  return false;
508 
509  auto gAudioIO = AudioIO::Get();
510  const bool busy = gAudioIO->IsBusy();
511  if (busy && gAudioIO->GetNumCaptureChannels() > 0) {
512  // Do not stop recording, and don't try to start scrubbing after
513  // recording stops
514  mScrubStartPosition = -1;
515  return false;
516  }
517 
518  auto &projectAudioManager = ProjectAudioManager::Get( *mProject );
519  if (busy) {
520  projectAudioManager.Stop();
521  }
523  mSpeedPlaying = true;
524  mKeyboardScrubbing = false;
525  mMaxSpeed = speed;
526  mDragging = false;
527 
528  auto options = DefaultSpeedPlayOptions( *mProject );
529 
530 #ifndef USE_SCRUB_THREAD
531  // Yuck, we either have to poll "by hand" when scrub polling doesn't
532  // work with a thread, or else yield to timer messages, but that would
533  // execute too much else
534  options.playbackStreamPrimer = [this](){
536  return ScrubPollInterval_ms;
537  };
538 #endif
539 
540  options.playNonWaveTracks = false;
541  options.envelope = nullptr;
542  mOptions.delay = (ScrubPollInterval_ms / 1000.0);
543  mOptions.initSpeed = speed;
544  mOptions.minSpeed = speed -0.01;
545  mOptions.maxSpeed = speed +0.01;
546 
547  if (time1 == time0)
548  time1 = std::max(0.0, TrackList::Get( *mProject ).GetEndTime());
549  mOptions.minTime = 0;
550  mOptions.maxTime = time1;
551  mOptions.minStutterTime = std::max(0.0, MinStutter);
552  mOptions.bySpeed = true;
553  mOptions.adjustStart = false;
554  mOptions.isPlayingAtSpeed = true;
556 
557  const bool backwards = time1 < time0;
558 #ifdef EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL
559  static const double maxScrubSpeedBase =
560  pow(2.0, 1.0 / ScrubSpeedStepsPerOctave);
561  mLogMaxScrubSpeed = floor(0.5 +
562  log(mMaxSpeed) / log(maxScrubSpeedBase)
563  );
564 #endif
565 
566  // Must start the thread and poller first or else PlayPlayRegion
567  // will insert some silence
568  StartPolling();
569  auto cleanup = finally([this]{
570  if (mScrubToken < 0)
571  StopPolling();
572  });
573 
575  // Aim to stop within 20 samples of correct position.
576  double stopTolerance = 20.0 / options.rate;
577  options.policyFactory = ScrubbingPlaybackPolicyFactory(mOptions);
578  mScrubToken =
579  // Reduce time by 'stopTolerance' fudge factor, so that the Play will stop.
580  projectAudioManager.PlayPlayRegion(
581  SelectedRegion(time0, time1-stopTolerance), options,
582  PlayMode::normalPlay, backwards);
583 
584  if (mScrubToken >= 0) {
585  mLastScrubPosition = 0;
586  }
587 
588  return true;
589 }
590 
591 
592 bool Scrubber::StartKeyboardScrubbing(double time0, bool backwards)
593 {
594  if (HasMark() || AudioIO::Get()->IsBusy())
595  return false;
596 
597  mScrubStartPosition = 0; // so that HasMark() is true
598  mSpeedPlaying = false;
599  mKeyboardScrubbing = true;
600  mBackwards = backwards;
602  mDragging = false;
603 
604  auto options = DefaultSpeedPlayOptions(*mProject);
605 
606 #ifndef USE_SCRUB_THREAD
607  // Yuck, we either have to poll "by hand" when scrub polling doesn't
608  // work with a thread, or else yield to timer messages, but that would
609  // execute too much else
610  options.playbackStreamPrimer = [this]() {
612  return ScrubPollInterval_ms;
613  };
614 #endif
615 
616  options.playNonWaveTracks = false;
617  options.envelope = nullptr;
618 
619  // delay and minStutterTime are used in AudioIO::AllocateBuffers() for setting the
620  // values of mPlaybackQueueMinimum and mPlaybackSamplesToCopy respectively.
621  mOptions.delay = (ScrubPollInterval_ms / 1000.0);
623 
625  if (backwards)
626  mOptions.initSpeed *= -1.0;
629  mOptions.minTime = 0;
630  mOptions.maxTime = std::max(0.0, TrackList::Get(*mProject).GetEndTime());
631  mOptions.bySpeed = true;
632  mOptions.adjustStart = false;
633  mOptions.isPlayingAtSpeed = false;
635 
636  // Must start the thread and poller first or else PlayPlayRegion
637  // will insert some silence
638  StartPolling();
639  auto cleanup = finally([this] {
640  if (mScrubToken < 0)
641  StopPolling();
642  });
643 
644  options.policyFactory = ScrubbingPlaybackPolicyFactory(mOptions);
645  mScrubToken =
647  SelectedRegion(time0, backwards ? mOptions.minTime : mOptions.maxTime),
648  options,
650  backwards);
651 
652  return true;
653 }
654 
655 
657 {
658  const double speedAtDefaultZoom = 0.5;
659  const double maxSpeed = 3.0;
660  const double minSpeed = 0.0625;
661 
662  auto &viewInfo = ViewInfo::Get(*mProject);
663  double speed = speedAtDefaultZoom*viewInfo.GetDefaultZoom() / viewInfo.GetZoom();
664  speed = std::min(speed, maxSpeed);
665  speed = std::max(speed, minSpeed);
666  return speed;
667 }
668 
669 
671 {
672  // Thus scrubbing relies mostly on periodic polling of mouse and keys,
673  // not event notifications. But there are a few event handlers that
674  // leave messages for this routine, in mScrubSeekPress and in mPaused.
675 
676  // Decide whether to skip play, because either mouse is down now,
677  // or there was a left click event. (This is then a delayed reaction, in a
678  // timer callback, to a left click event detected elsewhere.)
679  const bool seek = TemporarilySeeks() || Seeks();
680 
681  auto gAudioIO = AudioIO::Get();
682  if (mPaused) {
683  // When paused, make silent scrubs.
684  mOptions.minSpeed = 0.0;
686  mOptions.adjustStart = false;
687  mOptions.bySpeed = true;
689  }
690  else if (mSpeedPlaying) {
691  // default speed of 1.3 set, so that we can hear there is a problem
692  // when playAtSpeedTB not found.
693  double speed = 1.3;
694  const auto &settings = ProjectSettings::Get( *mProject );
695  speed = settings.GetPlaySpeed();
696  mOptions.minSpeed = speed -0.01;
697  mOptions.maxSpeed = speed +0.01;
698  mOptions.adjustStart = false;
699  mOptions.bySpeed = true;
701  }
702  else if (mKeyboardScrubbing) {
705  mOptions.adjustStart = false;
706  mOptions.bySpeed = true;
707  double speed = GetKeyboardScrubbingSpeed();
708  if (mBackwards)
709  speed *= -1.0;
711  } else {
712  const wxMouseState state(::wxGetMouseState());
713  auto &trackPanel = GetProjectPanel( *mProject );
714  const wxPoint position = trackPanel.ScreenToClient(state.GetPosition());
715  auto &viewInfo = ViewInfo::Get( *mProject );
716 #ifdef DRAG_SCRUB
718  const auto lastTime = ScrubState::GetLastScrubTime();
719  const auto delta = mLastScrubPosition - position.x;
720  const double time = viewInfo.OffsetTimeByPixels(lastTime, delta);
721  mOptions.minSpeed = 0.0;
723  mOptions.adjustStart = true;
724  mOptions.bySpeed = false;
725  gAudioIO->UpdateScrub(time, mOptions);
726  mLastScrubPosition = position.x;
727  }
728  else
729 #endif
730  {
731  const auto origin = viewInfo.GetLeftOffset();
732  auto xx = position.x;
733  if (!seek && !mSmoothScrollingScrub) {
734  // If mouse is out-of-bounds, so that we scrub at maximum speed
735  // toward the mouse position, then move the target time to a more
736  // extreme position to avoid catching-up and halting before the
737  // screen scrolls.
738  auto width = viewInfo.GetTracksUsableWidth();
739  auto delta = xx - origin;
740  if (delta < 0)
741  delta -= width;
742  else if (delta >= width)
743  delta += width;
744  xx = origin + delta;
745  }
746  const double time = viewInfo.PositionToTime(xx, origin);
747  mOptions.adjustStart = seek;
748  mOptions.minSpeed = seek ? 1.0 : 0.0;
749  mOptions.maxSpeed = seek ? 1.0 : mMaxSpeed;
750 
751  if (mSmoothScrollingScrub) {
752  const double speed = FindScrubSpeed(seek, time);
753  mOptions.bySpeed = true;
755  }
756  else {
757  mOptions.bySpeed = false;
759  }
760  }
761  }
762 
763  mScrubSeekPress = false;
764 
765  // else, if seek requested, try again at a later time when we might
766  // enqueue a long enough stutter
767 }
768 
770 {
771  const wxMouseState state(::wxGetMouseState());
772 
773  if (mDragging && !state.LeftIsDown()) {
774  // Dragging scrub can stop with mouse up
775  // Stop and set cursor
776  bool bShift = state.ShiftDown();
777  auto &projectAudioManager = ProjectAudioManager::Get( *mProject );
778  projectAudioManager.DoPlayStopSelect( true, bShift );
779  projectAudioManager.Stop();
780  return;
781  }
782 
783  const bool seek = Seeks() || TemporarilySeeks();
784 
785  {
786  // Show the correct status for seeking.
787  bool backup = mSeeking;
788  mSeeking = seek;
789  mSeeking = backup;
790  }
791 
792  if (seek)
794 
796  ;
797  else {
800  }
801 }
802 
804 {
806  return false;
807  return
808  !(HasMark() &&
809  !WasSpeedPlaying() &&
810  !ShouldScrubPinned());
811 }
812 
814 {
815  mPaused = false;
816 
817 #ifdef USE_SCRUB_THREAD
818  // Detached thread is self-deleting, after it receives the Delete() message
819  mpThread = safenew ScrubPollerThread{ *this };
820  mpThread->Create(4096);
821  mpThread->Run();
822 #endif
823 
824  mPoller->Start(ScrubPollInterval_ms * 0.9);
825 }
826 
828 {
829  mPaused = true;
830 
831 #ifdef USE_SCRUB_THREAD
832  if (mpThread) {
833  mpThread->Delete();
834  mpThread = nullptr;
835  }
836 #endif
837 
838  mPoller->Stop();
839 }
840 
842 {
843  auto gAudioIO = AudioIO::Get();
845  StopPolling();
846 
847  if (HasMark() && !mCancelled) {
848  const wxMouseState state(::wxGetMouseState());
849  // Stop and set cursor
850  bool bShift = state.ShiftDown();
851  auto &projectAudioManager = ProjectAudioManager::Get( *mProject );
852  projectAudioManager.DoPlayStopSelect(true, bShift);
853  }
854 
855  mScrubStartPosition = -1;
856  mDragging = false;
857  mSeeking = false;
858 
859  CheckMenuItems();
860 }
861 
862 bool Scrubber::ShowsBar() const
863 {
864  return mShowScrubbing;
865 }
866 
867 bool Scrubber::IsScrubbing() const
868 {
869  if (mScrubToken <= 0)
870  return false;
871  auto &projectAudioIO = ProjectAudioIO::Get( *mProject );
872  if (mScrubToken == projectAudioIO.GetAudioIOToken() &&
873  projectAudioIO.IsAudioActive())
874  return true;
875  else {
876  const_cast<Scrubber&>(*this).mScrubToken = -1;
877  const_cast<Scrubber&>(*this).mScrubStartPosition = -1;
878  const_cast<Scrubber&>(*this).mSmoothScrollingScrub = false;
879  return false;
880  }
881 }
882 
883 bool Scrubber::ChoseSeeking() const
884 {
885  return
886 #if !defined(DRAG_SCRUB)
887  // Drag always seeks
888  mDragging ||
889 #endif
890  mSeeking;
891 }
892 
893 bool Scrubber::TemporarilySeeks() const
894 {
895  return mScrubSeekPress ||
896  (::wxGetMouseState().LeftIsDown() && MayDragToSeek());
897 }
898 
899 bool Scrubber::Seeks() const
900 {
901  return (HasMark() || IsScrubbing()) && ChoseSeeking();
902 }
903 
904 bool Scrubber::Scrubs() const
905 {
906  if( Seeks() )
907  return false;
908  return (HasMark() || IsScrubbing()) && !ChoseSeeking();
909 }
910 
912 {
913  return IsScrubbing() &&
914  !mPaused && (
915  // Draw for (non-scroll) scrub, sometimes, but never for seek
917  // Draw always for scroll-scrub and for scroll-seek
919  );
920 }
921 
922 double Scrubber::FindScrubSpeed(bool seeking, double time) const
923 {
924  auto &viewInfo = ViewInfo::Get( *mProject );
925  const double screen =
926  viewInfo.GetScreenEndTime() - viewInfo.h;
927  return (seeking ? FindSeekSpeed : FindScrubbingSpeed)
928  (viewInfo, mMaxSpeed, screen, time);
929 }
930 
931 void Scrubber::HandleScrollWheel(int steps)
932 {
933  if (steps == 0)
934  return;
935 
936  const int newLogMaxScrubSpeed = mLogMaxScrubSpeed + steps;
937  static const double maxScrubSpeedBase =
938  pow(2.0, 1.0 / ScrubSpeedStepsPerOctave);
939  double newSpeed = pow(maxScrubSpeedBase, newLogMaxScrubSpeed);
940  if (newSpeed >= ScrubbingOptions::MinAllowedScrubSpeed() &&
942  mLogMaxScrubSpeed = newLogMaxScrubSpeed;
943  mMaxSpeed = newSpeed;
945  // Show the speed for one second
947  }
948 }
949 
950 void Scrubber::Pause( bool paused )
951 {
952  mPaused = paused;
953 }
954 
955 bool Scrubber::IsPaused() const
956 {
957  return mPaused;
958 }
959 
960 void Scrubber::OnActivateOrDeactivateApp(wxActivateEvent &event)
961 {
962  // First match priority logic...
963  // Pause if Pause down, or not scrubbing.
964  if (!mProject)
965  Pause(true);
966  else if (ProjectAudioManager::Get( *mProject ).Paused())
967  Pause( true );
968  else if (!IsScrubbing())
969  Pause( true );
970 
971  // Stop keyboard scrubbing if losing focus
972  else if (mKeyboardScrubbing && !event.GetActive()) {
973  Cancel();
974  ProjectAudioManager::Get(*mProject).Stop();
975  }
976 
977  // Speed playing does not pause if losing focus.
978  else if (mSpeedPlaying)
979  Pause( false );
980 
981  // But scrub and seek do.
982  else if (!event.GetActive())
983  Pause( true );
984  else
985  Pause(false);
986 
987  event.Skip();
988 }
989 
990 void Scrubber::DoScrub(bool seek)
991 {
992  if( !CanScrub() )
993  return;
994  const bool wasScrubbing = HasMark() || IsScrubbing();
995  const bool scroll = ShouldScrubPinned();
996  if (!wasScrubbing) {
997  auto &tp = GetProjectPanel( *mProject );
998  const auto &viewInfo = ViewInfo::Get( *mProject );
999  wxCoord xx = tp.ScreenToClient(::wxGetMouseState().GetPosition()).x;
1000 
1001  // Limit x
1002  auto width = viewInfo.GetTracksUsableWidth();
1003  const auto offset = viewInfo.GetLeftOffset();
1004  xx = (std::max(offset, std::min(offset + width - 1, xx)));
1005 
1006  MarkScrubStart(xx, scroll, seek);
1007  }
1008  else if (mSeeking != seek) {
1009  // just switching mode
1010  }
1011  else {
1012  auto &projectAudioManager = ProjectAudioManager::Get( *mProject );
1013  projectAudioManager.Stop();
1014  }
1015 }
1016 
1017 void Scrubber::OnScrubOrSeek(bool seek)
1018 {
1019  DoScrub(seek);
1020 
1021  mSeeking = seek;
1022  CheckMenuItems();
1023 }
1024 
1025 void Scrubber::OnScrub(const CommandContext&)
1026 {
1027  OnScrubOrSeek(false);
1028  CheckMenuItems();
1029 }
1030 
1031 void Scrubber::OnSeek(const CommandContext&)
1032 {
1033  OnScrubOrSeek(true);
1034  CheckMenuItems();
1035 }
1036 
1037 #if 1
1038 namespace {
1039  static const wxChar *scrubEnabledPrefName = wxT("/QuickPlay/ScrubbingEnabled");
1040 
1041  bool ReadScrubEnabledPref()
1042  {
1043  bool result {};
1044  gPrefs->Read(scrubEnabledPrefName, &result, false);
1045 
1046  return result;
1047  }
1048 
1049  void WriteScrubEnabledPref(bool value)
1050  {
1051  gPrefs->Write(scrubEnabledPrefName, value);
1052  }
1053 }
1054 #endif
1055 
1056 void Scrubber::UpdatePrefs()
1057 {
1058  mShowScrubbing = ReadScrubEnabledPref();
1059 }
1060 
1062 {
1064  WriteScrubEnabledPref(mShowScrubbing);
1065  gPrefs->Flush();
1066  const auto toolbar =
1067  ToolManager::Get( *mProject ).GetToolBar( ScrubbingBarID );
1068  toolbar->EnableDisableButtons();
1069  CheckMenuItems();
1070 }
1071 
1072 enum { CMD_ID = 8000 };
1073 
1074 #define THUNK(Name) Scrubber::Thunk<&Scrubber::Name>
1075 
1076 BEGIN_EVENT_TABLE(Scrubber, wxEvtHandler)
1077  EVT_MENU(CMD_ID, THUNK(OnScrub))
1078  EVT_MENU(CMD_ID + 1, THUNK(OnSeek))
1079  EVT_MENU(CMD_ID + 2, THUNK(OnToggleScrubRuler))
1081 
1082 //static_assert(menuItems().size() == 3, "wrong number of items");
1083 
1084 static auto sPlayAtSpeedStatus = XO("Playing at Speed");
1085 
1086 static auto sKeyboardScrubbingStatus = XO("Scrubbing");
1087 
1088 
1089 const TranslatableString &Scrubber::GetUntranslatedStateString() const
1090 {
1091  static TranslatableString empty;
1092 
1093  if (IsSpeedPlaying()) {
1094  return sPlayAtSpeedStatus;
1095  }
1096  else if (IsKeyboardScrubbing()) {
1097  return sKeyboardScrubbingStatus;
1098  }
1099  else if (HasMark()) {
1100  auto &item = FindMenuItem(Seeks() || TemporarilySeeks());
1101  return item.status;
1102  }
1103  else
1104  return empty;
1105 }
1106 
1107 wxString Scrubber::StatusMessageForWave() const
1108 {
1109  wxString result;
1110 
1111  if( Seeks() )
1112  result = _("Move mouse pointer to Seek");
1113  else if( Scrubs() )
1114  result = _("Move mouse pointer to Scrub");
1115  return result;
1116 }
1117 
1118 
1119 
1122  []( const AudacityProject &, StatusBarField field )
1124  {
1125  if ( field == stateStatusBarField ) {
1126  TranslatableStrings strings;
1127  // Note that Scrubbing + Paused is not allowed.
1128  for (const auto &item : menuItems())
1129  strings.push_back( item.GetStatus() );
1130  strings.push_back(
1131  XO("%s Paused.").Format( sPlayAtSpeedStatus )
1132  );
1133  // added constant needed because xMax isn't large enough for some reason, plus some space.
1134  return { std::move( strings ), 30 };
1135  }
1136  return {};
1137  }
1138 };
1139 
1140 bool Scrubber::CanScrub() const
1141 {
1142  // Recheck the same condition as enables the Scrub/Seek menu item.
1143  auto gAudioIO = AudioIO::Get();
1144  return !( gAudioIO->IsBusy() && gAudioIO->GetNumCaptureChannels() > 0 ) &&
1146 }
1147 
1148 void Scrubber::DoKeyboardScrub(bool backwards, bool keyUp)
1149 {
1150  auto &project = *mProject;
1151 
1152  static double initT0 = 0;
1153  static double initT1 = 0;
1154 
1155  if (keyUp) {
1156  auto &scrubber = Scrubber::Get(project);
1157  if (scrubber.IsKeyboardScrubbing() && scrubber.IsBackwards() == backwards) {
1158  auto gAudioIO = AudioIO::Get();
1159  auto time = gAudioIO->GetStreamTime();
1160  auto &viewInfo = ViewInfo::Get(project);
1161  auto &selection = viewInfo.selectedRegion;
1162 
1163  // If the time selection has not changed during scrubbing
1164  // set the cursor position
1165  if (selection.t0() == initT0 && selection.t1() == initT1) {
1166  double endTime = TrackList::Get(project).GetEndTime();
1167  time = std::min(time, endTime);
1168  time = std::max(time, 0.0);
1169  selection.setTimes(time, time);
1170  ProjectHistory::Get(project).ModifyState(false);
1171  }
1172 
1173  scrubber.Cancel();
1174  ProjectAudioManager::Get(project).Stop();
1175  }
1176  }
1177  else { // KeyDown
1178  auto gAudioIO = AudioIOBase::Get();
1179  auto &scrubber = Scrubber::Get(project);
1180  if (scrubber.IsKeyboardScrubbing() && scrubber.IsBackwards() != backwards) {
1181  // change direction
1182  scrubber.SetBackwards(backwards);
1183  }
1184  else if (!gAudioIO->IsBusy() && !scrubber.HasMark()) {
1185  auto &viewInfo = ViewInfo::Get(project);
1186  auto &selection = viewInfo.selectedRegion;
1187  double endTime = TrackList::Get(project).GetEndTime();
1188  double t0 = selection.t0();
1189 
1190  if ((!backwards && t0 >= 0 && t0 < endTime) ||
1191  (backwards && t0 > 0 && t0 <= endTime)) {
1192  initT0 = t0;
1193  initT1 = selection.t1();
1194  scrubber.StartKeyboardScrubbing(t0, backwards);
1195  }
1196  }
1197  }
1198 }
1199 
1201 {
1202  auto evt = context.pEvt;
1203  if (evt)
1204  DoKeyboardScrub(true, evt->GetEventType() == wxEVT_KEY_UP);
1205  else { // called from menu, so simulate keydown and keyup
1206  DoKeyboardScrub(true, false);
1207  DoKeyboardScrub(true, true);
1208  }
1209 }
1210 
1212 {
1213  auto evt = context.pEvt;
1214  if (evt)
1215  DoKeyboardScrub(false, evt->GetEventType() == wxEVT_KEY_UP);
1216  else { // called from menu, so simulate keydown and keyup
1217  DoKeyboardScrub(false, false);
1218  DoKeyboardScrub(false, true);
1219  }
1220 }
1221 
1222 namespace {
1223 
1224 static const auto finder =
1225  [](AudacityProject &project) -> CommandHandlerObject&
1226  { return Scrubber::Get( project ); };
1227 
1228 using namespace MenuTable;
1229 BaseItemSharedPtr ToolbarMenu()
1230 {
1232 
1233  static BaseItemSharedPtr menu { (
1234  FinderScope{ finder },
1235  Menu( wxT("Scrubbing"),
1236  XXO("Scru&bbing"),
1237  []{
1238  BaseItemPtrs ptrs;
1239  for (const auto &item : menuItems()) {
1240  ptrs.push_back( Command( item.name, item.label,
1241  item.memFn,
1242  item.flags,
1243  item.StatusTest
1244  ? // a checkmark item
1245  Options{}.CheckTest( [&item](AudacityProject &project){
1246  return ( Scrubber::Get(project).*(item.StatusTest) )(); } )
1247  : // not a checkmark item
1248  Options{}
1249  ) );
1250  }
1251  return ptrs;
1252  }()
1253  )
1254  ) };
1255 
1256  return menu;
1257 }
1258 
1260  wxT("Transport/Basic"),
1261  Shared( ToolbarMenu() )
1262 };
1263 
1264 BaseItemSharedPtr KeyboardScrubbingItems()
1265 {
1267 
1268  static BaseItemSharedPtr items{
1269  ( FinderScope{ finder },
1270  Items( wxT("KeyboardScrubbing"),
1271  Command(wxT("KeyboardScrubBackwards"), XXO("Scrub Bac&kwards"),
1274  Options{ wxT("U") }.WantKeyUp() ),
1275  Command(wxT("KeyboardScrubForwards"), XXO("Scrub For&wards"),
1278  Options{ wxT("I") }.WantKeyUp() )
1279  ) ) };
1280  return items;
1281 }
1282 
1284  wxT("Optional/Extra/Part1/Transport"),
1285  Shared( KeyboardScrubbingItems() )
1286 };
1287 
1288 }
1289 
1290 void Scrubber::PopulatePopupMenu(wxMenu &menu)
1291 {
1292  int id = CMD_ID;
1293  auto &cm = CommandManager::Get( *mProject );
1294  for (const auto &item : menuItems()) {
1295  if (cm.GetEnabled(item.name)) {
1296  auto test = item.StatusTest;
1297  menu.Append(id, item.label.Translation(), wxString{},
1298  test ? wxITEM_CHECK : wxITEM_NORMAL);
1299  if(test && (this->*test)())
1300  menu.FindItem(id)->Check();
1301  }
1302  ++id;
1303  }
1304 }
1305 
1307 {
1308  auto &cm = CommandManager::Get( *mProject );
1309  for (const auto &item : menuItems()) {
1310  auto test = item.StatusTest;
1311  if (test)
1312  cm.Check(item.name, (this->*test)());
1313  }
1314 }
1315 
1316 #endif
Scrubber::DoKeyboardScrub
void DoKeyboardScrub(bool backwards, bool keyUp)
Scrubber::StartSpeedPlay
bool StartSpeedPlay(double speed, double time0, double time1)
ProjectHistory::ModifyState
void ModifyState(bool bWantsAutoSave)
Definition: ProjectHistory.cpp:124
TranslatableString
Holds a msgid for the translation catalog; may also bind format arguments.
Definition: TranslatableString.h:32
ViewInfo::Get
static ViewInfo & Get(AudacityProject &project)
Definition: ViewInfo.cpp:241
ScrubbingOptions::maxTime
double maxTime
Definition: ScrubState.h:24
field
#define field(n, t)
Definition: ImportAUP.cpp:167
Scrubber::OnScrub
void OnScrub(const CommandContext &)
Scrubber::mDragging
bool mDragging
Definition: Scrubbing.h:171
ProjectSettings::GetPlaySpeed
double GetPlaySpeed() const
Definition: ProjectSettings.h:107
Scrubber::mSeeking
bool mSeeking
Definition: Scrubbing.h:167
AudioIOStartStreamOptions::PolicyFactory
std::function< std::unique_ptr< PlaybackPolicy >() > PolicyFactory
Definition: AudioIOBase.h:73
CanStopAudioStreamFlag
const ReservedCommandFlag & CanStopAudioStreamFlag()
Definition: ProjectAudioManager.cpp:1153
ProjectStatus.h
Scrubber::Get
static Scrubber & Get(AudacityProject &project)
Definition: Scrubbing.cpp:201
WaveTrack
A Track that contains audio waveform data.
Definition: WaveTrack.h:69
Scrubber::IsTransportingPinned
bool IsTransportingPinned() const
ProjectAudioManager::Get
static ProjectAudioManager & Get(AudacityProject &project)
Definition: ProjectAudioManager.cpp:55
MenuTable::FinderScope
Definition: CommandManager.h:485
ScrubbingOptions::MaxAllowedScrubSpeed
static double MaxAllowedScrubSpeed()
Definition: ScrubState.h:43
Scrubber::ShouldScrubPinned
static bool ShouldScrubPinned()
Definition: Scrubbing.cpp:164
registeredStatusWidthFunction
static ProjectStatus::RegisteredStatusWidthFunction registeredStatusWidthFunction
Definition: ControlToolBar.cpp:701
flag
static std::once_flag flag
Definition: WaveformView.cpp:1119
gPrefs
FileConfig * gPrefs
Definition: Prefs.cpp:70
ScrubState::GetLastScrubTime
static double GetLastScrubTime()
return the ending time of the last scrub interval.
Definition: ScrubState.cpp:483
TranslatableStrings
std::vector< TranslatableString > TranslatableStrings
Definition: TranslatableString.h:295
ScrubbingOptions
Definition: ScrubState.h:18
WaveTrack::GetEndTime
double GetEndTime() const override
Get the time at which the last clip in the track ends, plus recorded stuff.
Definition: WaveTrack.cpp:1887
ScrubbingOptions::minTime
double minTime
Definition: ScrubState.h:25
Project.h
Scrubber::Cancel
void Cancel()
Definition: Scrubbing.h:108
CommandContext::pEvt
const wxEvent * pEvt
Definition: CommandContext.h:66
anonymous_namespace{Scrubbing.cpp}::menuItems
const MenuItems & menuItems()
Definition: Scrubbing.cpp:269
StatusBarField
StatusBarField
Definition: ProjectStatus.h:24
Scrubber::DoScrub
void DoScrub(bool seek)
ToolManager::Get
static ToolManager & Get(AudacityProject &project)
Definition: ToolManager.cpp:356
ViewInfo
Definition: ViewInfo.h:202
ReservedCommandFlag
Definition: CommandFlag.h:89
Scrubber::MaybeStartScrubbing
bool MaybeStartScrubbing(wxCoord xx)
stateStatusBarField
@ stateStatusBarField
Definition: ProjectStatus.h:25
ScrubState::StopScrub
static void StopScrub()
Definition: ScrubState.cpp:476
Scrubber::OnToggleScrubRuler
void OnToggleScrubRuler(const CommandContext &)
Registry::Shared
std::unique_ptr< SharedItem > Shared(const BaseItemSharedPtr &ptr)
Definition: Registry.h:93
Scrubber::mProject
AudacityProject * mProject
Definition: Scrubbing.h:179
Scrubber::StartKeyboardScrubbing
bool StartKeyboardScrubbing(double time0, bool backwards)
Scrubber::Scrubs
bool Scrubs() const
Scrubber::MayDragToSeek
bool MayDragToSeek() const
Definition: Scrubbing.h:102
ToolBar::EnableDisableButtons
virtual void EnableDisableButtons()=0
ScrubbingPlaybackPolicyFactory
static AudioIOStartStreamOptions::PolicyFactory ScrubbingPlaybackPolicyFactory(const ScrubbingOptions &options)
Definition: Scrubbing.cpp:349
XO
#define XO(s)
Definition: Internat.h:31
Scrubber::ScrubPollerThread::mScrubber
Scrubber & mScrubber
Definition: Scrubbing.cpp:149
ProjectSettings::Get
static ProjectSettings & Get(AudacityProject &project)
Definition: ProjectSettings.cpp:44
Scrubber::Seeks
bool Seeks() const
ScrubbingOptions::MinAllowedScrubSpeed
static double MinAllowedScrubSpeed()
Definition: ScrubState.h:45
TrackInfo::UpdatePrefs
AUDACITY_DLL_API void UpdatePrefs(wxWindow *pParent)
Scrubber::mOptions
ScrubbingOptions mOptions
Definition: Scrubbing.h:195
DefaultSpeedPlayOptions
AudioIOStartStreamOptions DefaultSpeedPlayOptions(AudacityProject &project)
Definition: ProjectAudioManager.cpp:1180
Scrubber::ScrubPollerThread::Entry
ExitCode Entry() override
Definition: Scrubbing.cpp:152
SCRUBBING_PIXEL_TOLERANCE
@ SCRUBBING_PIXEL_TOLERANCE
Definition: Scrubbing.cpp:49
AlwaysEnabledFlag
constexpr CommandFlag AlwaysEnabledFlag
Definition: CommandFlag.h:35
PlaybackPrefs::GetUnpinnedScrubbingPreference
static bool GetUnpinnedScrubbingPreference()
Definition: PlaybackPrefs.cpp:164
Scrubber::mBackwards
bool mBackwards
Definition: Scrubbing.h:170
MenuTable::AttachedItem
Definition: CommandManager.h:708
CommandFlag
std::bitset< NCommandFlags > CommandFlag
Definition: CommandFlag.h:31
Scrubber::mScrubSpeedDisplayCountdown
int mScrubSpeedDisplayCountdown
Definition: Scrubbing.h:160
ProjectAudioIO::Get
static ProjectAudioIO & Get(AudacityProject &project)
Definition: ProjectAudioIO.cpp:22
Scrubber::StatusMessageForWave
wxString StatusMessageForWave() const
Scrubber::ScrubPoller
Definition: Scrubbing.cpp:171
Scrubber
Definition: Scrubbing.h:45
Scrubber::HasMark
bool HasMark() const
Definition: Scrubbing.h:91
ClientData::Site::RegisteredFactory
Client code makes static instance from a factory of attachments; passes it to Get or Find as a retrie...
Definition: ClientData.h:266
anonymous_namespace{Scrubbing.cpp}::MenuItem::status
TranslatableString status
Definition: Scrubbing.cpp:260
Scrubber::mLastScrubPosition
wxCoord mLastScrubPosition
Definition: Scrubbing.h:162
PlayMode::normalPlay
@ normalPlay
Scrubber::mCancelled
bool mCancelled
Definition: Scrubbing.h:173
ScrubbingOptions::maxSpeed
double maxSpeed
Definition: ScrubState.h:36
Scrubber::ScrubPollerThread::ScrubPollerThread
ScrubPollerThread(Scrubber &scrubber)
Definition: Scrubbing.cpp:142
Scrubber::UpdatePrefs
void UpdatePrefs() override
Scrubber::ContinueScrubbingPoll
void ContinueScrubbingPoll()
Scrubber::WasSpeedPlaying
bool WasSpeedPlaying() const
Definition: Scrubbing.h:77
anonymous_namespace{Scrubbing.cpp}::FindScrubbingSpeed
double FindScrubbingSpeed(const ViewInfo &viewInfo, double maxScrubSpeed, double screen, double timeAtMouse)
Definition: Scrubbing.cpp:62
ScrubbingOptions::minStutterTime
double minStutterTime
Definition: ScrubState.h:41
TrackList::GetEndTime
double GetEndTime() const
Definition: Track.cpp:1038
kOneSecondCountdown
@ kOneSecondCountdown
Definition: Scrubbing.cpp:55
ScrubbingOptions::minSpeed
double minSpeed
Definition: ScrubState.h:35
Scrubber::CheckMenuItems
void CheckMenuItems()
ScrubbingOptions::adjustStart
bool adjustStart
Definition: ScrubState.h:21
Scrubber::CanScrub
bool CanScrub() const
ScrubbingOptions::delay
double delay
Definition: ScrubState.h:31
XXO
#define XXO(s)
Definition: Internat.h:44
ToolManager::GetToolBar
ToolBar * GetToolBar(int type) const
Definition: ToolManager.cpp:1029
ProjectAudioManager::Paused
bool Paused() const
Definition: ProjectAudioManager.h:72
Scrubber::mpThread
ScrubPollerThread * mpThread
Definition: Scrubbing.h:187
ScrubbingOptions::isPlayingAtSpeed
bool isPlayingAtSpeed
Definition: ScrubState.h:28
anonymous_namespace{Scrubbing.cpp}::MenuItem::label
TranslatableString label
Definition: Scrubbing.cpp:259
CommandContext
CommandContext provides additional information to an 'Apply()' command. It provides the project,...
Definition: CommandContext.h:34
Scrubber::Scrubber
Scrubber(AudacityProject *project)
Definition: Scrubbing.cpp:211
TracksPrefs::GetPinnedHeadPositionPreference
static double GetPinnedHeadPositionPreference()
Definition: TracksPrefs.cpp:406
Registry::BaseItemSharedPtr
std::shared_ptr< BaseItem > BaseItemSharedPtr
Definition: Registry.h:72
MinStutter
static const double MinStutter
Definition: Scrubbing.cpp:58
anonymous_namespace{Contrast.cpp}::sAttachment
AttachedItem sAttachment
Definition: Contrast.cpp:697
ScrubbingBarID
@ ScrubbingBarID
Definition: ToolBar.h:80
Scrubber::mScrubStartPosition
wxCoord mScrubStartPosition
Definition: Scrubbing.h:161
Scrubber::OnKeyboardScrubBackwards
void OnKeyboardScrubBackwards(const CommandContext &)
Scrubber::mScrubToken
int mScrubToken
Definition: Scrubbing.h:159
EVT_MENU
EVT_MENU(OnSetPlayRegionToSelectionID, AdornedRulerPanel::OnSetPlayRegionToSelection) EVT_COMMAND(OnTogglePinnedStateID
ScrubbingOptions::isKeyboardScrubbing
bool isKeyboardScrubbing
Definition: ScrubState.h:29
HasWaveDataPred
static const auto HasWaveDataPred
Definition: Scrubbing.cpp:242
Scrubbing.h
AudioIOBase::Get
static AudioIOBase * Get()
Definition: AudioIOBase.cpp:89
anonymous_namespace{Scrubbing.cpp}::MenuItem
Definition: Scrubbing.cpp:257
MenuTable::Items
std::unique_ptr< MenuItems > Items(const Identifier &internalName, Args &&... args)
Definition: CommandManager.h:600
Scrubber::ScrubPoller::Notify
void Notify() override
Definition: Scrubbing.cpp:181
anonymous_namespace{Scrubbing.cpp}::MenuItems
std::vector< MenuItem > MenuItems
Definition: Scrubbing.cpp:268
WaveTrack::GetStartTime
double GetStartTime() const override
Get the time at which the first clip in the track starts.
Definition: WaveTrack.cpp:1867
DefaultPlayOptions
AudioIOStartStreamOptions DefaultPlayOptions(AudacityProject &project, bool looped)
Definition: ProjectAudioManager.cpp:1162
Scrubber::ScrubPoller::ScrubPoller
ScrubPoller(Scrubber &scrubber)
Definition: Scrubbing.cpp:173
ProjectAudioManager::Stop
void Stop(bool stopStream=true)
Definition: ProjectAudioManager.cpp:495
key
static const AudacityProject::AttachedObjects::RegisteredFactory key
Definition: Scrubbing.cpp:196
Scrubber::MarkScrubStart
void MarkScrubStart(wxCoord xx, bool smoothScrolling, bool seek)
Definition: Scrubbing.cpp:313
ViewInfo.h
ScrubState::UpdateScrub
static void UpdateScrub(double endTimeOrSpeed, const ScrubbingOptions &options)
Notify scrubbing engine of desired position or speed. If options.adjustStart is true,...
Definition: ScrubState.cpp:470
Scrubber::ChoseSeeking
bool ChoseSeeking() const
CaptureNotBusyFlag
const ReservedCommandFlag & CaptureNotBusyFlag()
Definition: CommonCommandFlags.cpp:213
Scrubber::TemporarilySeeks
bool TemporarilySeeks() const
ProjectStatus::StatusWidthResult
std::pair< std::vector< TranslatableString >, unsigned > StatusWidthResult
Definition: ProjectStatus.h:59
ProjectAudioManager::PlayPlayRegion
int PlayPlayRegion(const SelectedRegion &selectedRegion, const AudioIOStartStreamOptions &options, PlayMode playMode, bool backwards=false, bool playWhiteSpace=false)
Definition: ProjectAudioManager.cpp:291
anonymous_namespace{Scrubbing.cpp}::MenuItem::GetStatus
const TranslatableString & GetStatus() const
Definition: Scrubbing.cpp:266
Scrubber::mPaused
bool mPaused
Definition: Scrubbing.h:166
ProjectAudioIO::SetAudioIOToken
void SetAudioIOToken(int token)
Definition: ProjectAudioIO.cpp:46
id
int id
Definition: WaveTrackControls.cpp:577
min
int min(int a, int b)
Definition: CompareAudioCommand.cpp:106
TracksPrefs::GetPinnedHeadPreference
static bool GetPinnedHeadPreference()
Definition: TracksPrefs.cpp:387
anonymous_namespace{Scrubbing.cpp}::FindSeekSpeed
double FindSeekSpeed(const ViewInfo &viewInfo, double maxScrubSpeed, double screen, double timeAtMouse)
Definition: Scrubbing.cpp:104
Scrubber::ScrubPollerThread
Definition: Scrubbing.cpp:140
FileConfig::Flush
virtual bool Flush(bool bCurrentOnly=false) wxOVERRIDE
Definition: FileConfig.cpp:143
Registry::BaseItemPtrs
std::vector< BaseItemPtr > BaseItemPtrs
Definition: Registry.h:73
Scrubber::HandleScrollWheel
void HandleScrollWheel(int steps)
Scrubber::PopulatePopupMenu
void PopulatePopupMenu(wxMenu &menu)
TrackList::Get
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:506
ScrubPollInterval_ms
static constexpr unsigned ScrubPollInterval_ms
Definition: ScrubState.h:110
Scrubber::ShouldDrawScrubSpeed
bool ShouldDrawScrubSpeed()
TaggedIdentifier< CommandIdTag, false >
Scrubber::mShowScrubbing
bool mShowScrubbing
Definition: Scrubbing.h:198
_
#define _(s)
Definition: Internat.h:75
Scrubber::mMaxSpeed
double mMaxSpeed
Definition: Scrubbing.h:196
MenuTable::Command
std::unique_ptr< CommandItem > Command(const CommandID &name, const TranslatableString &label_in, void(Handler::*pmf)(const CommandContext &), CommandFlag flags, const CommandManager::Options &options={}, CommandHandlerFinder finder=FinderScope::DefaultFinder())
Definition: CommandManager.h:675
ScrubbingOptions::bySpeed
bool bySpeed
Definition: ScrubState.h:27
Scrubber::StopScrubbing
void StopScrubbing()
Scrubber::FindScrubSpeed
double FindScrubSpeed(bool seeking, double time) const
ProjectStatus::RegisteredStatusWidthFunction
Definition: ProjectStatus.h:67
AudacityProject
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:92
Scrubber::IsScrubbing
bool IsScrubbing() const
anonymous_namespace{Scrubbing.cpp}::MenuItem::name
CommandID name
Definition: Scrubbing.cpp:258
Scrubber::GetKeyboardScrubbingSpeed
double GetKeyboardScrubbingSpeed()
CommandHandlerObject
wxEvtHandler CommandHandlerObject
Definition: CommandFunctors.h:28
Scrubber::ContinueScrubbingUI
void ContinueScrubbingUI()
MenuTable::Menu
std::unique_ptr< MenuItem > Menu(const Identifier &internalName, const TranslatableString &title, Args &&... args)
Definition: CommandManager.h:623
Scrubber::StopPolling
void StopPolling()
HasWaveDataFlag
static const ReservedCommandFlag & HasWaveDataFlag()
Definition: Scrubbing.cpp:252
ScrubbingOptions::initSpeed
double initSpeed
Definition: ScrubState.h:34
Scrubber::mKeyboardScrubbing
bool mKeyboardScrubbing
Definition: Scrubbing.h:169
Scrubber::OnScrubOrSeek
void OnScrubOrSeek(bool seek)
MenuTable
Definition: CommandManager.h:416
TrackList::Any
auto Any() -> TrackIterRange< TrackType >
Definition: Track.h:1371
GetProjectPanel
AUDACITY_DLL_API wxWindow & GetProjectPanel(AudacityProject &project)
Get the main sub-window of the project frame that displays track data.
Definition: ProjectWindows.cpp:49
Scrubber::mSpeedPlaying
bool mSpeedPlaying
Definition: Scrubbing.h:168
Scrubber::mPoller
std::unique_ptr< ScrubPoller > mPoller
Definition: Scrubbing.h:192
CommandManager::Get
static CommandManager & Get(AudacityProject &project)
Definition: CommandManager.cpp:207
Scrubber::ShowsBar
bool ShowsBar() const
AudioIO::Get
static AudioIO * Get()
Definition: AudioIO.cpp:141
safenew
#define safenew
Definition: MemoryX.h:10
Scrubber::OnActivateOrDeactivateApp
void OnActivateOrDeactivateApp(wxActivateEvent &event)
settings
static Settings & settings()
Definition: TrackInfo.cpp:86
Scrubber::StartPolling
void StartPolling()
Scrubber::IsPaused
bool IsPaused() const
Scrubber::ScrubPoller::mScrubber
Scrubber & mScrubber
Definition: Scrubbing.cpp:178
anonymous_namespace{Menus.cpp}::Options
std::vector< CommandFlagOptions > & Options()
Definition: Menus.cpp:527
Scrubber::OnSeek
void OnSeek(const CommandContext &)
END_EVENT_TABLE
END_EVENT_TABLE()
Scrubber::mSmoothScrollingScrub
bool mSmoothScrollingScrub
Definition: Scrubbing.h:164
anonymous_namespace{ClipMenus.cpp}::sAttachment2
AttachedItem sAttachment2
Definition: ClipMenus.cpp:893
ProjectHistory::Get
static ProjectHistory & Get(AudacityProject &project)
Definition: ProjectHistory.cpp:26
Scrubber::Pause
void Pause(bool paused)
Scrubber::OnKeyboardScrubForwards
void OnKeyboardScrubForwards(const CommandContext &)
ZoomInfo::h
double h
Definition: ZoomInfo.h:60
Scrubber::~Scrubber
~Scrubber()
Definition: Scrubbing.cpp:234
anonymous_namespace{Scrubbing.cpp}::MenuItem::seek
bool seek
Definition: Scrubbing.cpp:263
anonymous_namespace{Scrubbing.cpp}::MenuItem::flags
CommandFlag flags
Definition: Scrubbing.cpp:261
Scrubber::mScrubSeekPress
bool mScrubSeekPress
Definition: Scrubbing.h:163
SelectedRegion
Defines a selected portion of a project.
Definition: SelectedRegion.h:35
anonymous_namespace{Scrubbing.cpp}::FindMenuItem
const MenuItem & FindMenuItem(bool seek)
Definition: Scrubbing.cpp:302