/********************************************************************** Audacity: A Digital Audio Editor Scrubbing.h Paul Licameli split from TrackPanel.cpp **********************************************************************/ #ifndef __AUDACITY_SCRUBBING__ #define __AUDACITY_SCRUBBING__ #include "../../MemoryX.h" #include #include #include #include "../../Experimental.h" #include "../../widgets/Overlay.h" class AudacityProject; // Conditionally compile either a separate thead, or else use a timer in the main // thread, to poll the mouse and update scrubbing speed and direction. The advantage of // a thread may be immunity to choppy scrubbing in case redrawing takes too much time. #define USE_SCRUB_THREAD // For putting an increment of work in the scrubbing queue struct ScrubbingOptions { ScrubbingOptions() {} bool adjustStart {}; // usually from TrackList::GetEndTime() long maxSample {}; long minSample {}; bool enqueueBySpeed {}; double delay {}; // Limiting values for the speed of a scrub interval: double minSpeed { 0.0 }; double maxSpeed { 1.0 }; // When maximum speed scrubbing skips to follow the mouse, // this is the minimum amount of playback allowed at the maximum speed: long minStutter {}; // Scrubbing needs the time of start of the mouse movement that began // the scrub: wxLongLong startClockTimeMillis { -1 }; static double MaxAllowedScrubSpeed() { return 32.0; } // Is five octaves enough for your amusement? static double MinAllowedScrubSpeed() { return 0.01; } // Mixer needs a lower bound speed. Scrub no slower than this. }; // Scrub state object class Scrubber : public wxEvtHandler { public: Scrubber(AudacityProject *project); ~Scrubber(); // Assume xx is relative to the left edge of TrackPanel! void MarkScrubStart( wxCoord xx, bool smoothScrolling, bool alwaysSeeking // if false, can switch seeking or scrubbing // by mouse button state ); // Returns true iff the event should be considered consumed by this: // Assume xx is relative to the left edge of TrackPanel! bool MaybeStartScrubbing(wxCoord xx); void ContinueScrubbingUI(); void ContinueScrubbingPoll(); // This is meant to be called only from ControlToolBar void StopScrubbing(); wxCoord GetScrubStartPosition() const { return mScrubStartPosition; } // True iff the user has clicked to start scrub and not yet stopped, // but IsScrubbing() may yet be false bool HasStartedScrubbing() const { return GetScrubStartPosition() >= 0; } bool IsScrubbing() const; bool IsScrollScrubbing() const // If true, implies HasStartedScrubbing() { return mSmoothScrollingScrub; } bool IsAlwaysSeeking() const { return mAlwaysSeeking; } bool ShouldDrawScrubSpeed(); double FindScrubSpeed(bool seeking, double time) const; double GetMaxScrubSpeed() const { return mOptions.maxSpeed; } void HandleScrollWheel(int steps); bool PollIsSeeking(); // This returns the same as the enabled state of the menu items: bool CanScrub() const; // For the toolbar void AddMenuItems(); // For popup void PopulateMenu(wxMenu &menu); void OnScrub(wxCommandEvent&); void OnScrollScrub(wxCommandEvent&); void OnSeek(wxCommandEvent&); void OnScrollSeek(wxCommandEvent&); // A string to put in the leftmost part of the status bar. const wxString &GetUntranslatedStateString() const; // All possible status strings. static std::vector GetAllUntranslatedStatusStrings(); void Pause(bool paused); bool IsPaused() const; private: void ActivateScroller(); void DoScrub(bool scroll, bool seek); void OnActivateOrDeactivateApp(wxActivateEvent & event); void UncheckAllMenuItems(); void CheckMenuItem(); // I need this because I can't push the scrubber as an event handler // in two places at once. struct Forwarder : public wxEvtHandler { Forwarder(Scrubber &scrubber_) : scrubber( scrubber_ ) {} Scrubber &scrubber; void OnMouse(wxMouseEvent &event); DECLARE_EVENT_TABLE() }; Forwarder mForwarder{ *this }; private: int mScrubToken; bool mPaused; int mScrubSpeedDisplayCountdown; wxCoord mScrubStartPosition; wxCoord mLastScrubPosition {}; bool mScrubSeekPress; bool mSmoothScrollingScrub; bool mAlwaysSeeking {}; bool mDragging {}; #ifdef EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL int mLogMaxScrubSpeed; #endif AudacityProject *mProject; DECLARE_EVENT_TABLE() #ifdef USE_SCRUB_THREAD // Course corrections in playback are done in a helper thread, unhindered by // the complications of the main event dispatch loop class ScrubPollerThread; ScrubPollerThread *mpThread {}; #endif // Other periodic update of the UI must be done in the main thread, // by this object which is driven by timer events. class ScrubPoller; std::unique_ptr mPoller; ScrubbingOptions mOptions; double mMaxSpeed { 1.0 }; }; // Specialist in drawing the scrub speed, and listening for certain events class ScrubbingOverlay final : public wxEvtHandler, public Overlay { public: ScrubbingOverlay(AudacityProject *project); virtual ~ScrubbingOverlay(); private: std::pair DoGetRectangle(wxSize size) override; void Draw(OverlayPanel &panel, wxDC &dc) override; void OnTimer(wxCommandEvent &event); const Scrubber &GetScrubber() const; Scrubber &GetScrubber(); AudacityProject *mProject; wxRect mLastScrubRect, mNextScrubRect; wxString mLastScrubSpeedText, mNextScrubSpeedText; }; #endif