mirror of
https://github.com/cookiengineer/audacity
synced 2025-05-02 08:39:46 +02:00
233 lines
6.4 KiB
C++
233 lines
6.4 KiB
C++
/**********************************************************************
|
|
|
|
Audacity: A Digital Audio Editor
|
|
|
|
Scrubbing.h
|
|
|
|
Paul Licameli split from TrackPanel.cpp
|
|
|
|
**********************************************************************/
|
|
|
|
#ifndef __AUDACITY_SCRUBBING__
|
|
#define __AUDACITY_SCRUBBING__
|
|
|
|
#include "../../MemoryX.h"
|
|
#include <vector>
|
|
#include <wx/event.h>
|
|
#include <wx/longlong.h>
|
|
|
|
#include "../../Experimental.h"
|
|
#include "../../widgets/Overlay.h"
|
|
#include "../../commands/CommandFunctors.h"
|
|
#include "../../commands/CommandContext.h"
|
|
#include "../../../include/audacity/Types.h"
|
|
|
|
class AudacityProject;
|
|
extern AudacityProject *GetActiveProject();
|
|
|
|
// 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.
|
|
#ifdef __WXGTK__
|
|
// Unfortunately some things the thread needs to do are not thread safe
|
|
#else
|
|
#define USE_SCRUB_THREAD
|
|
#endif
|
|
|
|
// For putting an increment of work in the scrubbing queue
|
|
struct ScrubbingOptions {
|
|
ScrubbingOptions() {}
|
|
|
|
bool adjustStart {};
|
|
|
|
// usually from TrackList::GetEndTime()
|
|
double maxTime {};
|
|
double minTime {};
|
|
|
|
bool bySpeed {};
|
|
bool isPlayingAtSpeed{};
|
|
|
|
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:
|
|
double minStutterTime {};
|
|
|
|
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:
|
|
static constexpr unsigned ScrubPollInterval_ms = 50;
|
|
|
|
Scrubber(AudacityProject *project);
|
|
~Scrubber();
|
|
|
|
// Assume xx is relative to the left edge of TrackPanel!
|
|
void MarkScrubStart(wxCoord xx, bool smoothScrolling, bool seek);
|
|
|
|
// 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);
|
|
bool StartSpeedPlay(double speed, double time0, double time1);
|
|
|
|
void ContinueScrubbingUI();
|
|
void ContinueScrubbingPoll();
|
|
|
|
// This is meant to be called only from ControlToolBar
|
|
void StopScrubbing();
|
|
|
|
wxCoord GetScrubStartPosition() const
|
|
{ return mScrubStartPosition; }
|
|
|
|
bool WasSpeedPlaying() const
|
|
{ return mSpeedPlaying;}
|
|
bool IsSpeedPlaying() const
|
|
{ return IsScrubbing() && mSpeedPlaying; }
|
|
// True iff the user has clicked to start scrub and not yet stopped,
|
|
// but IsScrubbing() may yet be false
|
|
bool HasMark() const
|
|
{ return GetScrubStartPosition() >= 0; }
|
|
bool IsScrubbing() const;
|
|
|
|
bool IsScrollScrubbing() const // If true, implies HasMark()
|
|
{ return mSmoothScrollingScrub; }
|
|
void SetScrollScrubbing(bool value)
|
|
{ mSmoothScrollingScrub = value; }
|
|
|
|
bool ChoseSeeking() const;
|
|
bool MayDragToSeek() const;
|
|
bool TemporarilySeeks() const;
|
|
bool Seeks() const;
|
|
bool Scrubs() const;
|
|
bool ShowsBar() const;
|
|
|
|
void Cancel()
|
|
{ mCancelled = true; }
|
|
|
|
bool ShouldDrawScrubSpeed();
|
|
double FindScrubSpeed(bool seeking, double time) const;
|
|
double GetMaxScrubSpeed() const { return mOptions.maxSpeed; }
|
|
|
|
void HandleScrollWheel(int steps);
|
|
|
|
// This returns the same as the enabled state of the menu items:
|
|
bool CanScrub() const;
|
|
|
|
// For the toolbar
|
|
void AddMenuItems();
|
|
// For popup
|
|
void PopulatePopupMenu(wxMenu &menu);
|
|
|
|
void OnScrubOrSeek(bool seek);
|
|
void OnScrub(const CommandContext&);
|
|
void OnSeek(const CommandContext&);
|
|
void OnToggleScrubRuler(const CommandContext&);
|
|
|
|
// Convenience wrapper for the above
|
|
template<void (Scrubber::*pfn)(const CommandContext&)>
|
|
void Thunk(wxCommandEvent &)
|
|
{ (this->*pfn)(*GetActiveProject()); }
|
|
|
|
// A string to put in the leftmost part of the status bar
|
|
// when scrub or seek is in progress, or else empty.
|
|
const wxString &GetUntranslatedStateString() const;
|
|
wxString StatusMessageForWave() const;
|
|
|
|
// All possible status strings.
|
|
static std::vector<wxString> GetAllUntranslatedStatusStrings();
|
|
|
|
void Pause(bool paused);
|
|
bool IsPaused() const;
|
|
void CheckMenuItems();
|
|
|
|
private:
|
|
void StartPolling();
|
|
void StopPolling();
|
|
void DoScrub(bool seek);
|
|
void OnActivateOrDeactivateApp(wxActivateEvent & event);
|
|
|
|
// 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;
|
|
int mScrubSpeedDisplayCountdown;
|
|
wxCoord mScrubStartPosition;
|
|
wxCoord mLastScrubPosition {};
|
|
bool mScrubSeekPress {};
|
|
bool mSmoothScrollingScrub;
|
|
|
|
bool mPaused{};
|
|
bool mSeeking {};
|
|
bool mSpeedPlaying{true};
|
|
bool mDragging {};
|
|
|
|
bool mCancelled {};
|
|
|
|
#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<ScrubPoller> 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);
|
|
|
|
private:
|
|
std::pair<wxRect, bool> 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
|