18#include "../../CommonCommandFlags.h"
21#include "../../ProjectAudioManager.h"
23#include "../../ProjectWindows.h"
25#include "../../ScrubState.h"
29#include "../../prefs/PlaybackPrefs.h"
30#include "../../prefs/TracksPrefs.h"
32#undef USE_TRANSCRIPTION_TOOLBAR
70 const double origin = viewInfo.
hpos + partScreen;
71 if (timeAtMouse >= origin)
72 partScreen = screen - partScreen;
75 const double snap = 0.05;
79 const double factor = 1.0 - (snap * 2);
80 const double denom = factor * partScreen;
81 double fraction = (denom <= 0.0) ? 0.0 :
82 std::min(1.0, fabs(timeAtMouse - origin) / denom);
85 const double unity = 1.0 / maxScrubSpeed;
86 const double tolerance = snap / factor;
89 if (fraction <= unity - tolerance)
90 fraction *= unity / (unity - tolerance);
91 else if (fraction < unity + tolerance)
94 fraction = unity + (fraction - (unity + tolerance)) *
95 (1.0 - unity) / (1.0 - (unity + tolerance));
97 double result = fraction * maxScrubSpeed;
98 if (timeAtMouse < origin)
115 static const double ARBITRARY_MULTIPLIER = 10.0;
116 const double extreme = std::max(1.0, maxScrubSpeed * ARBITRARY_MULTIPLIER);
120 const double origin = viewInfo.
hpos + partScreen;
121 if (timeAtMouse >= origin)
122 partScreen = screen - partScreen;
126 const double snap = 0.05;
127 const double fraction = (partScreen <= 0.0) ? 0.0 :
128 std::max(snap,
std::min(1.0, fabs(timeAtMouse - origin) / partScreen));
130 double result = 1.0 + ((fraction - snap) / (1.0 - snap)) * (extreme - 1.0);
131 if (timeAtMouse < origin)
137#ifdef USE_SCRUB_THREAD
141 while (!mFinishThread.load(std::memory_order_acquire)) {
173#ifndef USE_SCRUB_THREAD
183 return std::make_shared< Scrubber >( &parent ); }
219#ifdef USE_SCRUB_THREAD
220 if (mThread.joinable()) {
221 mFinishThread.store(
true, std::memory_order_release);
238 return !range.empty();
266 {
wxT(
"Scrub"),
XXO(
"&Scrub"),
XO(
"Scrubbing"),
275 {
wxT(
"Seek"),
XXO(
"See&k"),
XO(
"Seeking"),
284 {
wxT(
"ToggleScrubRuler"),
XXO(
"Scrub &Ruler"), {},
296 return seek == item.
seek;
305 wxCoord xx,
bool smoothScrolling,
bool seek
318 projectAudioManager.Stop();
341 return [options](
auto&) -> std::unique_ptr<PlaybackPolicy>
343 return std::make_unique<ScrubbingPlaybackPolicy>(options);
355#ifdef USE_SCRUB_THREAD
356 if (mThread.joinable())
360 const auto state = ::wxGetMouseState();
364 const bool busy = gAudioIO->IsBusy();
365 if (busy && gAudioIO->GetNumCaptureChannels() > 0) {
372 wxCoord position = xx;
377 const int leftOffset = viewInfo.GetLeftOffset();
382 viewInfo.PositionToTime(position, leftOffset)
384 if (time1 != time0) {
387 projectAudioManager.Stop();
393 auto delta = time0 - time1;
394 time0 = std::max(0.0,
std::min(maxTime,
396 (viewInfo.GetScreenEndTime() - viewInfo.h)
399 time1 = time0 + delta;
407#ifndef USE_SCRUB_THREAD
411 options.playbackStreamPrimer = [
this](){
416 options.playNonWaveTracks =
false;
417 options.envelope =
nullptr;
422#ifdef USE_TRANSCRIPTION_TOOLBAR
423 if (!mAlwaysSeeking) {
448 const bool backwards = time1 < time0;
449 static const double maxScrubSpeedBase =
459 auto cleanup =
finally([
this]{
466 projectAudioManager.PlayPlayRegion(
496#ifdef USE_SCRUB_THREAD
497 if (mThread.joinable())
510#ifndef USE_SCRUB_THREAD
514 options.playbackStreamPrimer = [
this]() {
520 options.playNonWaveTracks =
false;
521 options.envelope =
nullptr;
542 auto cleanup =
finally([
this] {
561 const double speedAtDefaultZoom = 0.5;
562 const double maxSpeed = 3.0;
563 const double minSpeed = 0.0625;
566 double speed = speedAtDefaultZoom*viewInfo.GetDefaultZoom() / viewInfo.GetZoom();
568 speed = std::max(speed, minSpeed);
598 speed = projectAudioIO.GetPlaySpeed();
615 const wxMouseState state(::wxGetMouseState());
617 const wxPoint position = trackPanel.ScreenToClient(state.GetPosition());
623 const double time = viewInfo.OffsetTimeByPixels(lastTime, delta);
628 gAudioIO->UpdateScrub(time,
mOptions);
634 const auto origin = viewInfo.GetLeftOffset();
635 auto xx = position.x;
641 auto width = viewInfo.GetTracksUsableWidth();
642 auto delta = xx - origin;
645 else if (delta >= width)
649 const double time = viewInfo.PositionToTime(xx, origin);
674 const wxMouseState state(::wxGetMouseState());
679 bool bShift = state.ShiftDown();
681 projectAudioManager.DoPlayStopSelect(
true, bShift );
682 projectAudioManager.Stop();
720#ifdef USE_SCRUB_THREAD
721 assert(!mThread.joinable());
722 mFinishThread.store(
false, std::memory_order_relaxed);
723 mThread = std::thread{
735#ifdef USE_SCRUB_THREAD
749 const wxMouseState state(::wxGetMouseState());
751 bool bShift = state.ShiftDown();
753 projectAudioManager.DoPlayStopSelect(
true, bShift);
773 if (
mScrubToken == projectAudioIO.GetAudioIOToken() &&
774 projectAudioIO.IsAudioActive())
787#if !defined(DRAG_SCRUB)
826 const double screen =
827 viewInfo.GetScreenEndTime() - viewInfo.hpos;
838 static const double maxScrubSpeedBase =
840 double newSpeed = pow(maxScrubSpeedBase, newLogMaxScrubSpeed);
883 else if (!event.GetActive())
900 wxCoord xx = tp.ScreenToClient(::wxGetMouseState().GetPosition()).x;
903 auto width = viewInfo.GetTracksUsableWidth();
904 const auto offset = viewInfo.GetLeftOffset();
905 xx = (std::max(offset,
std::min(offset + width - 1, xx)));
914 projectAudioManager.Stop();
972#define THUNK(Name) Scrubber::Thunk<&Scrubber::Name>
974BEGIN_EVENT_TABLE(
Scrubber, wxEvtHandler)
991 if (IsSpeedPlaying()) {
994 else if (IsKeyboardScrubbing()) {
997 else if (HasMark()) {
998 auto &item =
FindMenuItem(Seeks() || TemporarilySeeks());
1010 result =
_(
"Move mouse pointer to Seek");
1012 result =
_(
"Move mouse pointer to Scrub");
1027 strings.push_back( item.GetStatus() );
1032 return { std::move( strings ), 30 };
1042 return !( gAudioIO->IsBusy() && gAudioIO->GetNumCaptureChannels() > 0 ) &&
1050 static double initT0 = 0;
1051 static double initT1 = 0;
1055 if (scrubber.IsKeyboardScrubbing() && scrubber.IsBackwards() == backwards) {
1057 auto time = gAudioIO->GetStreamTime();
1059 auto &selection = viewInfo.selectedRegion;
1063 if (selection.t0() == initT0 && selection.t1() == initT1) {
1066 time = std::max(time, 0.0);
1067 selection.setTimes(time, time);
1078 if (scrubber.IsKeyboardScrubbing() && scrubber.IsBackwards() != backwards) {
1080 scrubber.SetBackwards(backwards);
1082 else if (!gAudioIO->IsBusy() && !scrubber.HasMark()) {
1084 auto &selection = viewInfo.selectedRegion;
1086 double t0 = selection.t0();
1088 if ((!backwards && t0 >= 0 && t0 < endTime) ||
1089 (backwards && t0 > 0 && t0 <= endTime)) {
1091 initT1 = selection.t1();
1092 scrubber.StartKeyboardScrubbing(t0, backwards);
1100 auto evt = context.
pEvt;
1111 auto evt = context.
pEvt;
1129 static auto menu = []{
1131 auto menu = std::shared_ptr{
Menu(
"Scrubbing",
XXO(
"Scru&bbing")) };
1133 menu->push_back(
Command(item.name, item.label,
1139 return (Scrubber::Get(project).*(item.StatusTest))(); } )
1153 static auto items = std::shared_ptr{
1156 Command(
wxT(
"KeyboardScrubBackwards"),
XXO(
"Scrub Bac&kwards"),
1160 Command(
wxT(
"KeyboardScrubForwards"),
XXO(
"Scrub For&wards"),
1169 wxT(
"Optional/Extra/Part1/Transport")
1179 if (cm.GetEnabled(item.name)) {
1180 auto test = item.StatusTest;
1181 menu.Append(
id, item.label.Translation(), wxString{},
1182 test ? wxITEM_CHECK : wxITEM_NORMAL);
1183 if(test && (this->*test)())
1184 menu.FindItem(
id)->Check();
1194 auto test = item.StatusTest;
1196 cm.Check(item.name, (this->*test)());
EVT_MENU(OnSetPlayRegionToSelectionID, AdornedRulerPanel::OnSetPlayRegionToSelection) EVT_COMMAND(OnTogglePinnedStateID
AttachedItem sAttachment2
constexpr CommandFlag AlwaysEnabledFlag
std::bitset< NCommandFlags > CommandFlag
wxEvtHandler CommandHandlerObject
const ReservedCommandFlag & CaptureNotBusyFlag()
XXO("&Cut/Copy/Paste Toolbar")
audacity::BasicSettings * gPrefs
AudioIOStartStreamOptions DefaultSpeedPlayOptions(AudacityProject &project)
const ReservedCommandFlag & CanStopAudioStreamFlag()
StatusBarField StateStatusBarField()
ID of the first field in the status bar. This filed is used to display playback state.
AUDACITY_DLL_API wxWindow & GetProjectPanel(AudacityProject &project)
Get the main sub-window of the project frame that displays track data.
static constexpr auto ScrubPollInterval
@ SCRUBBING_PIXEL_TOLERANCE
@ ScrubSpeedStepsPerOctave
static const auto HasWaveDataPred
static const AudacityProject::AttachedObjects::RegisteredFactory key
static auto sPlayAtSpeedStatus
static ProjectStatus::RegisteredStatusWidthFunction registeredStatusWidthFunction
static constexpr PlaybackPolicy::Duration MinStutter
static const ReservedCommandFlag & HasWaveDataFlag()
static AudioIOStartStreamOptions::PolicyFactory ScrubbingPlaybackPolicyFactory(const ScrubbingOptions &options)
static auto sKeyboardScrubbingStatus
declares abstract base class Track, TrackList, and iterators over TrackList
std::vector< TranslatableString > TranslatableStrings
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
static AudioIOBase * Get()
Client code makes static instance from a factory of attachments; passes it to Get or Find as a retrie...
CommandContext provides additional information to an 'Apply()' command. It provides the project,...
static CommandManager & Get(AudacityProject &project)
An explicitly nonlocalized string, not meant for the user to see.
std::chrono::duration< double > Duration
static bool GetUnpinnedScrubbingPreference()
void SetAudioIOToken(int token)
static AudioIOStartStreamOptions GetDefaultOptions(AudacityProject &project, bool newDefaults=false)
Invoke the global hook, supplying a default argument.
static ProjectAudioIO & Get(AudacityProject &project)
void Stop(bool stopStream=true)
static ProjectAudioManager & Get(AudacityProject &project)
int PlayPlayRegion(const SelectedRegion &selectedRegion, const AudioIOStartStreamOptions &options, PlayMode playMode, bool backwards=false)
void ModifyState(bool bWantsAutoSave)
static ProjectHistory & Get(AudacityProject &project)
static ProjectSettings & Get(AudacityProject &project)
std::pair< std::vector< TranslatableString >, unsigned > StatusWidthResult
Generates classes whose instances register items at construction.
ScrubPoller(Scrubber &scrubber)
void ContinueScrubbingUI()
void MarkScrubStart(wxCoord xx, bool smoothScrolling, bool seek)
bool ShouldDrawScrubSpeed()
void DoKeyboardScrub(bool backwards, bool keyUp)
double GetKeyboardScrubbingSpeed()
void HandleScrollWheel(int steps)
bool MaybeStartScrubbing(wxCoord xx)
wxCoord mLastScrubPosition
wxCoord mScrubStartPosition
bool mSmoothScrollingScrub
void OnToggleScrubRuler(const CommandContext &)
bool ChoseSeeking() const
bool TemporarilySeeks() const
std::unique_ptr< ScrubPoller > mPoller
static bool ShouldScrubPinned()
void OnActivateOrDeactivateApp(wxActivateEvent &event)
int mScrubSpeedDisplayCountdown
static Scrubber & Get(AudacityProject &project)
AudacityProject * mProject
void UpdatePrefs() override
ScrubbingOptions mOptions
void OnSeek(const CommandContext &)
bool StartKeyboardScrubbing(double time0, bool backwards)
void OnScrub(const CommandContext &)
void OnKeyboardScrubForwards(const CommandContext &)
bool MayDragToSeek() const
bool WasSpeedPlaying() const
void ContinueScrubbingPoll()
void OnKeyboardScrubBackwards(const CommandContext &)
bool IsTransportingPinned() const
void OnScrubOrSeek(bool seek)
void PopulatePopupMenu(wxMenu &menu)
wxString StatusMessageForWave() const
double FindScrubSpeed(bool seeking, double time) const
Scrubber(AudacityProject *project)
Defines a selected portion of a project.
double GetEndTime() const
Return the greatest end time of the tracks, or 0 when no tracks.
auto Any() -> TrackIterRange< TrackType >
static TrackList & Get(AudacityProject &project)
static bool GetPinnedHeadPreference()
static double GetPinnedHeadPositionPreference()
Holds a msgid for the translation catalog; may also bind format arguments.
static ViewInfo & Get(AudacityProject &project)
A Track that contains audio waveform data.
double GetStartTime() const override
Implement WideSampleSequence.
double GetEndTime() const override
Implement WideSampleSequence.
double hpos
Leftmost visible timeline position in seconds.
virtual bool Flush() noexcept=0
virtual bool Write(const wxString &key, bool value)=0
virtual bool Read(const wxString &key, bool *value) const =0
std::unique_ptr< detail::IndirectItem< Item > > Indirect(const std::shared_ptr< Item > &ptr)
A convenience function.
void WriteScrubEnabledPref(bool value)
bool ReadScrubEnabledPref()
auto KeyboardScrubbingItems()
static const wxChar * scrubEnabledPrefName
const MenuItems & menuItems()
double FindSeekSpeed(const ViewInfo &viewInfo, double maxScrubSpeed, double screen, double timeAtMouse)
const MenuItem & FindMenuItem(bool seek)
double FindScrubbingSpeed(const ViewInfo &viewInfo, double maxScrubSpeed, double screen, double timeAtMouse)
static CommandContext::TargetFactory::SubstituteInUnique< InteractiveOutputTargets > scope
const char * end(const char *str) noexcept
const char * begin(const char *str) noexcept
std::function< std::unique_ptr< PlaybackPolicy >(const AudioIOStartStreamOptions &) > PolicyFactory
static double GetLastScrubTime()
return the ending time of the last scrub interval.
static void UpdateScrub(double endTimeOrSpeed, const ScrubbingOptions &options)
Notify scrubbing engine of desired position or speed. If options.adjustStart is true,...
PlaybackPolicy::Duration minStutterTime
PlaybackPolicy::Duration delay
static double MinAllowedScrubSpeed()
static double MaxAllowedScrubSpeed()