1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-05-03 09:09:47 +02:00

TrackPanel.cpp cleanup: Move code for scrubbing and some of drawing out...

... Specifically there are "overlays" for play indicator, cursor, scrubbing
speed text, and quick play indicator, which now use a common abstract class
interface.

Also moved details of handling timer events elsewhere.

Made a class Scrubber with most of the details.  Still there are several places
where TrackPanel must check for scrubbing and call to Scrubber.  Too many
places.
This commit is contained in:
Paul Licameli 2016-04-18 14:32:34 -04:00
commit 408f1ecad2
23 changed files with 1586 additions and 1029 deletions

View File

@ -1208,6 +1208,10 @@
28FC1AFB0A47762C00A188AE /* WrappedType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 28FC1AF90A47762C00A188AE /* WrappedType.cpp */; };
28FE4A080ABF4E960056F5C4 /* mmx_optimized.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 28FE4A060ABF4E960056F5C4 /* mmx_optimized.cpp */; };
28FE4A090ABF4E960056F5C4 /* sse_optimized.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 28FE4A070ABF4E960056F5C4 /* sse_optimized.cpp */; };
5E74D2D81CC4425D00D88B0B /* TrackPanelOverlay.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E74D2D61CC4425D00D88B0B /* TrackPanelOverlay.cpp */; };
5E74D2E31CC4429700D88B0B /* EditCursorOverlay.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E74D2DD1CC4429700D88B0B /* EditCursorOverlay.cpp */; };
5E74D2E41CC4429700D88B0B /* PlayIndicatorOverlay.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E74D2DF1CC4429700D88B0B /* PlayIndicatorOverlay.cpp */; };
5E74D2E51CC4429700D88B0B /* Scrubbing.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5E74D2E11CC4429700D88B0B /* Scrubbing.cpp */; };
8406A93812D0F2510011EA01 /* EQDefaultCurves.xml in Resources */ = {isa = PBXBuildFile; fileRef = 8406A93712D0F2510011EA01 /* EQDefaultCurves.xml */; };
8484F31413086237002DF7F0 /* DeviceManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8484F31213086237002DF7F0 /* DeviceManager.cpp */; };
ED15214D163C22F000451B5F /* lsr.c in Sources */ = {isa = PBXBuildFile; fileRef = ED152123163C220300451B5F /* lsr.c */; };
@ -2968,6 +2972,16 @@
28FE4A070ABF4E960056F5C4 /* sse_optimized.cpp */ = {isa = PBXFileReference; fileEncoding = 5; indentWidth = 3; lastKnownFileType = sourcecode.cpp.cpp; path = sse_optimized.cpp; sourceTree = "<group>"; tabWidth = 3; };
28FEC1B21A12B6FB00FACE48 /* EffectAutomationParameters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EffectAutomationParameters.h; path = ../include/audacity/EffectAutomationParameters.h; sourceTree = SOURCE_ROOT; };
5E61EE0C1CBAA6BB0009FCF1 /* MemoryX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MemoryX.h; sourceTree = "<group>"; };
5E74D2D61CC4425D00D88B0B /* TrackPanelOverlay.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TrackPanelOverlay.cpp; sourceTree = "<group>"; };
5E74D2D71CC4425D00D88B0B /* TrackPanelOverlay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TrackPanelOverlay.h; sourceTree = "<group>"; };
5E74D2D91CC4427B00D88B0B /* TrackPanelCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TrackPanelCell.h; sourceTree = "<group>"; };
5E74D2DA1CC4427B00D88B0B /* TrackPanelCellIterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TrackPanelCellIterator.h; sourceTree = "<group>"; };
5E74D2DD1CC4429700D88B0B /* EditCursorOverlay.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EditCursorOverlay.cpp; sourceTree = "<group>"; };
5E74D2DE1CC4429700D88B0B /* EditCursorOverlay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EditCursorOverlay.h; sourceTree = "<group>"; };
5E74D2DF1CC4429700D88B0B /* PlayIndicatorOverlay.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PlayIndicatorOverlay.cpp; sourceTree = "<group>"; };
5E74D2E01CC4429700D88B0B /* PlayIndicatorOverlay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlayIndicatorOverlay.h; sourceTree = "<group>"; };
5E74D2E11CC4429700D88B0B /* Scrubbing.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Scrubbing.cpp; sourceTree = "<group>"; };
5E74D2E21CC4429700D88B0B /* Scrubbing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Scrubbing.h; sourceTree = "<group>"; };
5ED18DB61CC16B1E00FAFE95 /* Reverb_libSoX.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Reverb_libSoX.h; sourceTree = "<group>"; };
5ED18DB71CC290AB00FAFE95 /* wxFileNameWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wxFileNameWrapper.h; sourceTree = "<group>"; };
82FF184D13CF01A600C1B664 /* dBTable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dBTable.cpp; path = sbsms/src/dBTable.cpp; sourceTree = "<group>"; };
@ -3793,7 +3807,6 @@
1790AFC409883BFD008A330A /* src */ = {
isa = PBXGroup;
children = (
5ED18DB71CC290AB00FAFE95 /* wxFileNameWrapper.h */,
1790AFC709883BFD008A330A /* AboutDialog.cpp */,
1790AFC909883BFD008A330A /* AColor.cpp */,
1790AFCE09883BFD008A330A /* AudacityApp.cpp */,
@ -3870,6 +3883,7 @@
1790B0EA09883BFD008A330A /* TrackArtist.cpp */,
1790B0EC09883BFD008A330A /* TrackPanel.cpp */,
1790B0EE09883BFD008A330A /* TrackPanelAx.cpp */,
5E74D2D61CC4425D00D88B0B /* TrackPanelOverlay.cpp */,
1790B0F209883BFD008A330A /* UndoManager.cpp */,
28C8211C1B5C661E00B53328 /* ViewInfo.cpp */,
1790B0F709883BFD008A330A /* VoiceKey.cpp */,
@ -3964,7 +3978,10 @@
1790B0EB09883BFD008A330A /* TrackArtist.h */,
1790B0ED09883BFD008A330A /* TrackPanel.h */,
1790B0EF09883BFD008A330A /* TrackPanelAx.h */,
5E74D2D91CC4427B00D88B0B /* TrackPanelCell.h */,
5E74D2DA1CC4427B00D88B0B /* TrackPanelCellIterator.h */,
2803C8B619F35AA000278526 /* TrackPanelListener.h */,
5E74D2D71CC4425D00D88B0B /* TrackPanelOverlay.h */,
284416391B82D6BC0000574D /* TranslatableStringArray.h */,
1790B0F309883BFD008A330A /* UndoManager.h */,
1790B0F609883BFD008A330A /* ViewInfo.h */,
@ -3973,6 +3990,7 @@
1790B0FC09883BFD008A330A /* WaveTrack.h */,
2844163A1B82D6BC0000574D /* WaveTrackLocation.h */,
28FC1AFA0A47762C00A188AE /* WrappedType.h */,
5ED18DB71CC290AB00FAFE95 /* wxFileNameWrapper.h */,
1790AFDC09883BFD008A330A /* blockfile */,
174D9025098C78AF00D5909F /* commands */,
1790AFFD09883BFD008A330A /* effects */,
@ -3981,6 +3999,7 @@
1841B4FD0E00AD3D00F386E9 /* ondemand */,
1790B0B509883BFD008A330A /* prefs */,
2897F6DB0AB3DB5A003C20C5 /* toolbars */,
5E74D2DB1CC4429700D88B0B /* tracks */,
1790B0FD09883BFD008A330A /* widgets */,
1790B10D09883BFD008A330A /* xml */,
);
@ -5516,6 +5535,27 @@
path = expat/lib;
sourceTree = "<group>";
};
5E74D2DB1CC4429700D88B0B /* tracks */ = {
isa = PBXGroup;
children = (
5E74D2DC1CC4429700D88B0B /* ui */,
);
path = tracks;
sourceTree = "<group>";
};
5E74D2DC1CC4429700D88B0B /* ui */ = {
isa = PBXGroup;
children = (
5E74D2DD1CC4429700D88B0B /* EditCursorOverlay.cpp */,
5E74D2DE1CC4429700D88B0B /* EditCursorOverlay.h */,
5E74D2DF1CC4429700D88B0B /* PlayIndicatorOverlay.cpp */,
5E74D2E01CC4429700D88B0B /* PlayIndicatorOverlay.h */,
5E74D2E11CC4429700D88B0B /* Scrubbing.cpp */,
5E74D2E21CC4429700D88B0B /* Scrubbing.h */,
);
path = ui;
sourceTree = "<group>";
};
ED05D0FF0E50AD5600CC4BD3 /* libscorealign */ = {
isa = PBXGroup;
children = (
@ -7289,6 +7329,7 @@
1790B14F09883BFD008A330A /* SoundTouchEffect.cpp in Sources */,
1790B15109883BFD008A330A /* StereoToMono.cpp in Sources */,
1790B15209883BFD008A330A /* ToneGen.cpp in Sources */,
5E74D2E31CC4429700D88B0B /* EditCursorOverlay.cpp in Sources */,
1790B15309883BFD008A330A /* TruncSilence.cpp in Sources */,
1790B15409883BFD008A330A /* TwoPassSimpleMono.cpp in Sources */,
1790B15809883BFD008A330A /* Wahwah.cpp in Sources */,
@ -7421,6 +7462,7 @@
1841B50B0E00AD6E00F386E9 /* ODManager.cpp in Sources */,
1841B50C0E00AD6E00F386E9 /* ODTask.cpp in Sources */,
1841B50D0E00AD6E00F386E9 /* ODTaskThread.cpp in Sources */,
5E74D2E51CC4429700D88B0B /* Scrubbing.cpp in Sources */,
1841B50E0E00AD6E00F386E9 /* ODWaveTrackTaskQueue.cpp in Sources */,
1841B5110E00AD8D00F386E9 /* ODPCMAliasBlockFile.cpp in Sources */,
2860BA240E0F0D8600A13878 /* SoundActivatedRecord.cpp in Sources */,
@ -7540,6 +7582,7 @@
ED920CAF15B19F61008CA12C /* ModulePrefs.cpp in Sources */,
EDD2431416934A6100D9DEC2 /* BassTreble.cpp in Sources */,
ED19449A1733F92800F4F5CA /* Reverb.cpp in Sources */,
5E74D2D81CC4425D00D88B0B /* TrackPanelOverlay.cpp in Sources */,
2849A42017F8BEC2005C653F /* KeyView.cpp in Sources */,
284FD04217FC72A50009A025 /* ScienFilter.cpp in Sources */,
284FD04517FC72EE0009A025 /* Biquad.cpp in Sources */,
@ -7559,6 +7602,7 @@
28001B3E1A0F0E5D007DD161 /* NumericTextCtrl.cpp in Sources */,
28001B4B1A0F0EB6007DD161 /* SpectralSelectionBar.cpp in Sources */,
28BB98051A15BE6800D1CC80 /* NoiseReduction.cpp in Sources */,
5E74D2E41CC4429700D88B0B /* PlayIndicatorOverlay.cpp in Sources */,
28D000A51A32920C00367B21 /* DeviceChange.cpp in Sources */,
28D8425C1AD8D69D00551353 /* SelectedRegion.cpp in Sources */,
2888A1631AE25F9A00E06FDC /* Diags.cpp in Sources */,

View File

@ -233,7 +233,11 @@ audacity_SOURCES = \
TrackPanel.h \
TrackPanelAx.cpp \
TrackPanelAx.h \
TrackPanelCell.h \
TrackPanelCellIterator.h \
TrackPanelListener.h \
TrackPanelOverlay.cpp \
TrackPanelOverlay.h \
TranslatableStringArray.h \
UndoManager.cpp \
UndoManager.h \
@ -523,6 +527,12 @@ audacity_SOURCES = \
toolbars/ToolsToolBar.h \
toolbars/TranscriptionToolBar.cpp \
toolbars/TranscriptionToolBar.h \
tracks/ui/EditCursorOverlay.cpp \
tracks/ui/EditCursorOverlay.h \
tracks/ui/PlayIndicatorOverlay.cpp \
tracks/ui/PlayIndicatorOverlay.h \
tracks/ui/Scrubbing.cpp \
tracks/ui/Scrubbing.h \
widgets/AButton.cpp \
widgets/AButton.h \
widgets/ASlider.cpp \

View File

@ -156,6 +156,10 @@ scroll information. It also has some status flags.
#include "toolbars/ToolsToolBar.h"
#include "toolbars/TranscriptionToolBar.h"
#include "tracks/ui/EditCursorOverlay.h"
#include "tracks/ui/PlayIndicatorOverlay.h"
#include "tracks/ui/Scrubbing.h"
#include "commands/ScriptCommandRelay.h"
#include "commands/CommandDirectory.h"
#include "commands/CommandTargets.h"
@ -924,6 +928,33 @@ AudacityProject::AudacityProject(wxWindow * parent, wxWindowID id,
this,
mRuler);
mIndicatorOverlay = std::make_unique<PlayIndicatorOverlay>(this);
mCursorOverlay = std::make_unique<EditCursorOverlay>(this);
#ifdef EXPERIMENTAL_SCRUBBING_BASIC
// This must follow construction of *mIndicatorOverlay, because it must
// attach its timer event handler later (so that its handler is invoked
// earlier)
mScrubOverlay = std::make_unique<ScrubbingOverlay>(this);
mScrubber = std::make_unique<Scrubber>(this);
#endif
// This must follow construction of *mScrubOverlay, because it must
// attach its timer event handler later (so that its handler is invoked
// earlier)
this->Connect(EVT_TRACK_PANEL_TIMER,
wxCommandEventHandler(ViewInfo::OnTimer),
NULL,
&mViewInfo);
// Add the overlays, in the sequence in which they will be painted
mTrackPanel->AddOverlay(mIndicatorOverlay.get());
mTrackPanel->AddOverlay(mCursorOverlay.get());
#ifdef EXPERIMENTAL_SCRUBBING_BASIC
mTrackPanel->AddOverlay(mScrubOverlay.get());
#endif
// LLL: When Audacity starts or becomes active after returning from
// another application, the first window that can accept focus
// will be given the focus even if we try to SetFocus(). By
@ -1051,6 +1082,14 @@ AudacityProject::~AudacityProject()
wxGetApp().GetRecentFiles()->RemoveMenu(mRecentFilesMenu);
}
if(mTrackPanel) {
#ifdef EXPERIMENTAL_SCRUBBING_BASIC
mTrackPanel->RemoveOverlay(mScrubOverlay.get());
#endif
mTrackPanel->RemoveOverlay(mCursorOverlay.get());
mTrackPanel->RemoveOverlay(mIndicatorOverlay.get());
}
wxTheApp->Disconnect(EVT_AUDIOIO_CAPTURE,
wxCommandEventHandler(AudacityProject::OnCapture),
NULL,
@ -2339,6 +2378,11 @@ void AudacityProject::OnCloseWindow(wxCloseEvent & event)
#endif
}
this->Disconnect(EVT_TRACK_PANEL_TIMER,
wxCommandEventHandler(ViewInfo::OnTimer),
NULL,
&mViewInfo);
Destroy();
mIsBeingDeleted = true;

View File

@ -62,6 +62,7 @@ class Tags;
class EffectPlugs;
class TrackPanel;
class TrackPanelOverlay;
class FreqWindow;
class ContrastDialog;
class Meter;
@ -72,6 +73,7 @@ class DeviceToolBar;
class EditToolBar;
class MeterToolBar;
class MixerToolBar;
class Scrubber;
class SelectionBar;
class SpectralSelectionBar;
class Toolbar;
@ -171,9 +173,13 @@ class AUDACITY_DLL_API AudacityProject final : public wxFrame,
double GetRate() const { return mRate; }
bool ZoomInAvailable() const { return mViewInfo.ZoomInAvailable(); }
bool ZoomOutAvailable() const { return mViewInfo.ZoomOutAvailable(); }
double GetSel0() { return mViewInfo.selectedRegion.t0(); }
double GetSel1() { return mViewInfo.selectedRegion.t1(); }
const SelectedRegion &GetSelection() const { return mViewInfo.selectedRegion; }
SelectedRegion &GetSelection() { return mViewInfo.selectedRegion; }
double GetSel0() const { return mViewInfo.selectedRegion.t0(); }
double GetSel1() const { return mViewInfo.selectedRegion.t1(); }
const ZoomInfo &GetZoomInfo() const { return mViewInfo; }
const ViewInfo &GetViewInfo() const { return mViewInfo; }
ViewInfo &GetViewInfo() { return mViewInfo; }
Track *GetFirstVisible();
void UpdateFirstVisible();
@ -702,6 +708,18 @@ public:
// CommandManager needs to use private methods
friend class CommandManager;
// TrackPanelOverlay objects
std::unique_ptr<TrackPanelOverlay>
mIndicatorOverlay, mCursorOverlay;
#ifdef EXPERIMENTAL_SCRUBBING_BASIC
std::unique_ptr<TrackPanelOverlay> mScrubOverlay;
std::unique_ptr<Scrubber> mScrubber;
public:
Scrubber &GetScrubber() { return *mScrubber; }
const Scrubber &GetScrubber() const { return *mScrubber; }
#endif
DECLARE_EVENT_TABLE()
};

File diff suppressed because it is too large Load Diff

View File

@ -35,6 +35,7 @@ class wxRect;
class LabelTrack;
class SpectrumAnalyst;
class TrackPanel;
class TrackPanelOverlay;
class TrackArtist;
class Ruler;
class SnapManager;
@ -73,6 +74,11 @@ enum class UndoPush : unsigned char;
DECLARE_EXPORTED_EVENT_TYPE(AUDACITY_DLL_API, EVT_TRACK_PANEL_TIMER, -1);
enum {
kTimerInterval = 50, // milliseconds
kOneSecondCountdown = 1000 / kTimerInterval,
};
class AUDACITY_DLL_API TrackInfo
{
public:
@ -160,7 +166,6 @@ class AUDACITY_DLL_API TrackPanel final : public wxPanel {
virtual void OnSetFocus(wxFocusEvent & event);
virtual void OnKillFocus(wxFocusEvent & event);
virtual void OnActivateOrDeactivateApp(wxActivateEvent & event);
virtual void OnContextMenu(wxContextMenuEvent & event);
@ -224,8 +229,6 @@ class AUDACITY_DLL_API TrackPanel final : public wxPanel {
virtual void UpdateTrackVRuler(Track *t);
virtual void UpdateVRulerSize();
virtual void DrawQuickPlayIndicator(int x, bool snapped = false);
// Returns the time corresponding to the pixel column one past the track area
// (ignoring any fisheye)
virtual double GetScreenEndTime() const;
@ -250,27 +253,6 @@ class AUDACITY_DLL_API TrackPanel final : public wxPanel {
virtual bool IsOverCutline(WaveTrack * track, wxRect &rect, wxMouseEvent &event);
virtual void HandleTrackSpecificMouseEvent(wxMouseEvent & event);
virtual void TimerUpdateIndicator(double playPos);
// Second member of pair indicates whether the indicator is out of date:
virtual std::pair<wxRect, bool> GetIndicatorRectangle();
virtual void UndrawIndicator(wxDC & dc);
/// draws the green line on the tracks to show playback position
virtual void DoDrawIndicator(wxDC & dc);
// Second member of pair indicates whether the cursor is out of date:
virtual std::pair<wxRect, bool> GetCursorRectangle();
virtual void UndrawCursor(wxDC & dc);
virtual void DoDrawCursor(wxDC & dc);
#ifdef EXPERIMENTAL_SCRUBBING_BASIC
bool ShouldDrawScrubSpeed();
virtual void TimerUpdateScrubbing(double playPos);
// Second member of pair indicates whether the cursor is out of date:
virtual std::pair<wxRect, bool> GetScrubSpeedRectangle();
virtual void UndrawScrubSpeed(wxDC & dc);
virtual void DoDrawScrubSpeed(wxDC & dc);
#endif
virtual void ScrollDuringDrag();
// Working out where to dispatch the event to.
@ -310,31 +292,7 @@ class AUDACITY_DLL_API TrackPanel final : public wxPanel {
virtual void HandleSelect(wxMouseEvent & event);
virtual void SelectionHandleDrag(wxMouseEvent &event, Track *pTrack);
// Made obsolete by scrubbing:
#ifndef EXPERIMENTAL_SCRUBBING_BASIC
void StartOrJumpPlayback(wxMouseEvent &event);
#endif
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
double FindScrubSpeed(double timeAtMouse) const;
double FindSeekSpeed(double timeAtMouse) const;
#endif
#ifdef EXPERIMENTAL_SCRUBBING_BASIC
static bool PollIsSeeking();
bool IsScrubbing();
void MarkScrubStart(
wxCoord xx
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
, bool smoothScrolling
#endif
);
bool MaybeStartScrubbing(wxMouseEvent &event);
bool ContinueScrubbing(wxCoord position, bool hasFocus, bool seek);
public:
bool StopScrubbing();
protected:
#endif
virtual void SelectionHandleClick(wxMouseEvent &event,
Track* pTrack, wxRect rect);
@ -547,6 +505,15 @@ protected:
virtual void DrawOutsideOfTrack (Track *t, wxDC* dc, const wxRect & rect);
public:
// Register and unregister overlay objects.
// The sequence in which they were registered is the sequence in
// which they are painted.
// TrackPanel is not responsible for their memory management.
virtual void AddOverlay(TrackPanelOverlay *pOverlay);
// Returns true if the overlay was found
virtual bool RemoveOverlay(TrackPanelOverlay *pOverlay);
virtual void ClearOverlays();
// Erase and redraw things like the cursor, cheaply and directly to the
// client area, without full refresh.
virtual void DrawOverlays(bool repaint);
@ -597,17 +564,6 @@ protected:
TrackPanel *parent;
} mTimer;
// This stores the parts of the screen that get overwritten by the indicator
// and cursor
int mLastIndicatorX;
int mNewIndicatorX;
int mLastCursorX;
double mCursorTime;
int mNewCursorX;
// Quick-Play indicator postion
int mOldQPIndicatorPos;
int mTimeCount;
wxMemoryDC mBackingDC;
@ -798,27 +754,6 @@ protected:
int mMoveDownThreshold;
int mRearrangeCount;
#ifdef EXPERIMENTAL_SCRUBBING_BASIC
int mScrubToken;
wxLongLong mScrubStartClockTimeMillis;
wxCoord mScrubStartPosition;
double mMaxScrubSpeed;
int mScrubSpeedDisplayCountdown;
bool mScrubHasFocus;
bool mScrubSeekPress;
wxRect mLastScrubRect, mNextScrubRect;
wxString mLastScrubSpeedText, mNextScrubSpeedText;
#endif
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
bool mSmoothScrollingScrub;
#endif
#ifdef EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL
int mLogMaxScrubSpeed;
#endif
std::unique_ptr<wxCursor>
mArrowCursor, mPencilCursor, mSelectCursor,
mResizeCursor, mSlideCursor, mEnvelopeCursor, // doubles as the center frequency cursor
@ -854,6 +789,11 @@ protected:
TrackPanelAx *mAx;
public:
TrackPanelAx &GetAx() { return *mAx; }
protected:
wxString mSoloPref;
// Keeps track of extra fractional vertical scroll steps
@ -867,6 +807,10 @@ protected:
public:
wxSize vrulerSize;
protected:
std::vector<TrackPanelOverlay*> mOverlays;
public:
DECLARE_EVENT_TABLE()
};

19
src/TrackPanelCell.h Normal file
View File

@ -0,0 +1,19 @@
/**********************************************************************
Audacity: A Digital Audio Editor
TrackPanelCell.h
Paul Licameli
**********************************************************************/
#ifndef __AUDACITY_TRACK_PANEL_CELL__
#define __AUDACITY_TRACK_PANEL_CELL__
// Future: TrackPanelCell will be generalized to a new abstract base class of Track
// and of other things.
class Track;
using TrackPanelCell = Track;
#endif

View File

@ -0,0 +1,52 @@
/**********************************************************************
Audacity: A Digital Audio Editor
TrackPanelCellIterator.h
Paul Licameli
**********************************************************************/
#ifndef __AUDACITY_TRACK_PANEL_CELL_ITERATOR__
#define __AUDACITY_TRACK_PANEL_CELL_ITERATOR__
#include "Track.h"
class Track;
typedef Track TrackPanelCell;
class TrackPanel;
// A class that allows iteration over the rectangles of visible cells.
class TrackPanelCellIterator
{
public:
TrackPanelCellIterator(TrackPanel *trackPanel, bool begin);
// implement the STL iterator idiom
TrackPanelCellIterator &operator++ ();
TrackPanelCellIterator operator++ (int);
friend inline bool operator==
(const TrackPanelCellIterator &lhs, const TrackPanelCellIterator &rhs)
{
return lhs.mpCell == rhs.mpCell;
}
using value_type = std::pair<TrackPanelCell*, wxRect>;
value_type operator * () const;
private:
TrackPanel *mPanel;
VisibleTrackIterator mIter;
TrackPanelCell *mpCell;
};
inline bool operator !=
(const TrackPanelCellIterator &lhs, const TrackPanelCellIterator &rhs)
{
return !(lhs == rhs);
}
#endif

40
src/TrackPanelOverlay.cpp Normal file
View File

@ -0,0 +1,40 @@
/**********************************************************************
Audacity: A Digital Audio Editor
TrackPanelOverlay.cpp
Paul Licameli split from TrackPanel.cpp
**********************************************************************/
#include "TrackPanelOverlay.h"
#include <wx/dc.h>
TrackPanelOverlay::~TrackPanelOverlay()
{
}
std::pair<wxRect, bool> TrackPanelOverlay::GetRectangle(wxSize size)
{
auto result = DoGetRectangle(size);
#ifdef __WXMAC__
// On OSX, if a HiDPI resolution is being used, a vertical line will actually take up
// more than 1 pixel (even though it is drawn as 1), so we restore the surrounding
// pixels as well. (This is because the wxClientDC doesn't know about the scaling.
result.first.Inflate(1, 0);
#endif
return result;
}
void TrackPanelOverlay::Erase(wxDC &dc, wxDC &src)
{
wxRect rect(dc.GetSize());
rect.Intersect(src.GetSize());
auto smallRect(GetRectangle(src.GetSize()).first);
rect.Intersect(smallRect);
if (!rect.IsEmpty())
dc.Blit(rect.x, rect.y, rect.width, rect.height,
&src, rect.x, rect.y);
}

43
src/TrackPanelOverlay.h Normal file
View File

@ -0,0 +1,43 @@
/**********************************************************************
Audacity: A Digital Audio Editor
TrackPanelOverlay.h
Paul Licameli split from TrackPanel.cpp
**********************************************************************/
#ifndef __AUDACITY_TRACK_PANEL_OVERLAY__
#define __AUDACITY_TRACK_PANEL_OVERLAY__
class TrackPanelCellIterator;
#include <utility>
class wxDC;
class wxRect;
class wxSize;
class TrackPanelOverlay
{
public:
virtual ~TrackPanelOverlay() = 0;
// nonvirtual wrapper
std::pair<wxRect, bool> GetRectangle(wxSize size);
// size passes the dimensions of the backing dc
// First member of pair is the rectangle that would be erased
// Second member of pair indicates whether the overlay is out of date
virtual std::pair<wxRect, bool> DoGetRectangle(wxSize size) = 0;
// Default implementation blits from backing store over GetRectangle().first
virtual void Erase(wxDC &dc, wxDC &src);
// Draw; dc.GetSize() tells you the total dimensions, and the iterators let you
// find the rectangles of tracks (or other sub-rectangles of the panel)
virtual void Draw
(wxDC &dc, TrackPanelCellIterator begin, TrackPanelCellIterator end) = 0;
};
#endif

View File

@ -13,6 +13,7 @@ Paul Licameli
#include <algorithm>
#include "AudioIO.h"
#include "Internat.h"
#include "prefs/GUISettings.h"
#include "Prefs.h"
@ -123,6 +124,7 @@ ViewInfo::ViewInfo(double start, double screenDuration, double pixelsPerSecond)
, scrollStep(16)
, bUpdateTrackIndicator(true)
, bScrollBeyondZero(false)
, mRecentStreamTime(-1.0)
{
UpdatePrefs();
}
@ -176,3 +178,9 @@ bool ViewInfo::ReadXMLAttribute(const wxChar *attr, const wxChar *value)
return false;
}
void ViewInfo::OnTimer(wxCommandEvent &event)
{
mRecentStreamTime = gAudioIO->GetStreamTime();
event.Skip();
}

View File

@ -12,6 +12,7 @@
#define __AUDACITY_VIEWINFO__
#include <vector>
#include <wx/event.h>
#include "SelectedRegion.h"
@ -131,10 +132,10 @@ public:
// Exclusive:
wxInt64 GetFisheyeRightBoundary(wxInt64 WXUNUSED(origin = 0)) const
{return 0;} // stub
};
class AUDACITY_DLL_API ViewInfo final : public ZoomInfo
class AUDACITY_DLL_API ViewInfo final
: public wxEvtHandler, public ZoomInfo
{
public:
ViewInfo(double start, double screenDuration, double pixelsPerSecond);
@ -180,8 +181,15 @@ public:
bool bScrollBeyondZero;
// During timer update, grab the volatile stream time just once, so that
// various other drawing code can use the exact same value.
double mRecentStreamTime;
void WriteXMLAttributes(XMLWriter &xmlFile);
bool ReadXMLAttribute(const wxChar *attr, const wxChar *value);
// Receive track panel timer notifications
void OnTimer(wxCommandEvent &event);
};
#endif

View File

@ -52,13 +52,15 @@
#include "../AllThemeResources.h"
#include "../ImageManipulation.h"
#include "../Project.h"
#ifdef EXPERIMENTAL_SCRUBBING_BASIC
#include "../TrackPanel.h"
#endif
#include "../Theme.h"
#include "../widgets/AButton.h"
#include "../Experimental.h"
#ifdef EXPERIMENTAL_SCRUBBING_BASIC
#include "../tracks/ui/Scrubbing.h"
#endif
#include "../widgets/AButton.h"
IMPLEMENT_CLASS(ToolsToolBar, ToolBar);
@ -219,13 +221,9 @@ void ToolsToolBar::SetCurrentTool(int tool, bool show)
#ifdef EXPERIMENTAL_SCRUBBING_BASIC
if (tool != selectTool) {
AudacityProject *p = GetActiveProject();
if (p) {
TrackPanel *tp = p->GetTrackPanel();
if (tp) {
tp->StopScrubbing();
}
}
AudacityProject *const p = GetActiveProject();
if (p)
p->GetScrubber().StopScrubbing();
}
#endif
@ -294,13 +292,9 @@ void ToolsToolBar::OnTool(wxCommandEvent & evt)
#ifdef EXPERIMENTAL_SCRUBBING_BASIC
if (0 != mCurrentTool) {
AudacityProject *p = GetActiveProject();
if (p) {
TrackPanel *tp = p->GetTrackPanel();
if (tp) {
tp->StopScrubbing();
}
}
AudacityProject *const p = GetActiveProject();
if (p)
p->GetScrubber().StopScrubbing();
}
#endif

View File

@ -0,0 +1,118 @@
/**********************************************************************
Audacity: A Digital Audio Editor
EditCursorOverlay.cpp
Paul Licameli split from TrackPanel.cpp
**********************************************************************/
#include "../../Audacity.h"
#include "EditCursorOverlay.h"
#include "../../Experimental.h"
#include "../../AColor.h"
#include "../../widgets/Ruler.h"
#include "../../Project.h"
//#include "../../TrackPanel.h"
#include "../../TrackPanelCell.h"
#include "../../TrackPanelCellIterator.h"
#include "../../TrackPanelAx.h"
#include "../../ViewInfo.h"
#include <wx/dc.h>
namespace {
template < class LOW, class MID, class HIGH >
bool between_incexc(LOW l, MID m, HIGH h)
{
return (m >= l && m < h);
}
}
EditCursorOverlay::EditCursorOverlay(AudacityProject *project)
: mProject(project)
, mLastCursorX(-1)
, mCursorTime(-1)
, mNewCursorX(-1)
{
}
EditCursorOverlay::~EditCursorOverlay()
{
}
std::pair<wxRect, bool> EditCursorOverlay::DoGetRectangle(wxSize size)
{
const SelectedRegion &selection = mProject->GetSelection();
if (!selection.isPoint()) {
mCursorTime = -1.0;
mNewCursorX = -1;
}
else {
mCursorTime = selection.t0();
mNewCursorX = mProject->GetZoomInfo().TimeToPosition
(mCursorTime, mProject->GetTrackPanel()->GetLeftOffset());
}
return std::make_pair(
mLastCursorX == -1
? wxRect()
: wxRect(mLastCursorX, 0, 1, size.GetHeight()),
mLastCursorX != mNewCursorX
);
}
void EditCursorOverlay::Draw
(wxDC &dc, TrackPanelCellIterator begin, TrackPanelCellIterator end)
{
mLastCursorX = mNewCursorX;
if (mLastCursorX == -1)
return;
const ZoomInfo &viewInfo = mProject->GetZoomInfo();
const bool
onScreen = between_incexc(viewInfo.h,
mCursorTime,
mProject->GetScreenEndTime());
if (!onScreen)
return;
AColor::CursorColor(&dc);
// Draw cursor in all selected tracks
for (; begin != end; ++begin)
{
TrackPanelCellIterator::value_type data(*begin);
Track *const pTrack = data.first;
if (!pTrack)
continue;
if (pTrack->GetSelected() ||
mProject->GetTrackPanel()->GetAx().IsFocused(pTrack))
{
const wxRect &rect = data.second;
// AColor::Line includes both endpoints so use GetBottom()
AColor::Line(dc, mLastCursorX, rect.GetTop(), mLastCursorX, rect.GetBottom()); // <-- The whole point of this routine.
#ifdef EXPERIMENTAL_OUTPUT_DISPLAY
if (MONO_WAVE_PAN(t)){
y = t->GetY(true) - mViewInfo->vpos + 1;
top = y + kTopInset;
bottom = y + t->GetHeight(true) - kTopInset;
AColor::Line(dc, mLastCursorX, top, mLastCursorX, bottom);
}
#endif
}
}
// AS: Ah, no, this is where we draw the blinky thing in the ruler.
mProject->GetRulerPanel()->DrawCursor(mCursorTime);
// This updates related displays such as numbers on the status bar
mProject->TP_DisplaySelection();
}

View File

@ -0,0 +1,36 @@
/**********************************************************************
Audacity: A Digital Audio Editor
EditCursorOverlay.h
Paul Licameli split from TrackPanel.cpp
**********************************************************************/
#ifndef __AUDACITY_EDIT_CURSOR_OVERLAY__
#define __AUDACITY_EDIT_CURSOR_OVERLAY__
#include "../../TrackPanelOverlay.h"
class AudacityProject;
class EditCursorOverlay final : public TrackPanelOverlay
{
public:
EditCursorOverlay(AudacityProject *project);
virtual ~EditCursorOverlay();
private:
std::pair<wxRect, bool> DoGetRectangle(wxSize size) override;
void Draw
(wxDC &dc, TrackPanelCellIterator begin, TrackPanelCellIterator end) override;
AudacityProject *mProject;
int mLastCursorX;
double mCursorTime;
int mNewCursorX;
};
#endif

View File

@ -0,0 +1,151 @@
/**********************************************************************
Audacity: A Digital Audio Editor
PlayIndicatorOverlay.cpp
Paul Licameli split from TrackPanel.cpp
**********************************************************************/
#include "PlayIndicatorOverlay.h"
#include "../../AColor.h"
#include "../../AudioIO.h"
#include "../../Project.h"
#include "../../TrackPanel.h"
#include "../../TrackPanelCell.h"
#include "../../TrackPanelCellIterator.h"
#include "../../widgets/Ruler.h"
#include <wx/dc.h>
#include <algorithm>
namespace {
template < class LOW, class MID, class HIGH >
bool between_incexc(LOW l, MID m, HIGH h)
{
return (m >= l && m < h);
}
}
PlayIndicatorOverlay::PlayIndicatorOverlay(AudacityProject *project)
: mProject(project)
, mLastIndicatorX(-1)
, mNewIndicatorX(-1)
{
mProject->Connect(EVT_TRACK_PANEL_TIMER,
wxCommandEventHandler(PlayIndicatorOverlay::OnTimer),
NULL,
this);
}
PlayIndicatorOverlay::~PlayIndicatorOverlay()
{
mProject->Disconnect(EVT_TRACK_PANEL_TIMER,
wxCommandEventHandler(PlayIndicatorOverlay::OnTimer),
NULL,
this);
}
std::pair<wxRect, bool> PlayIndicatorOverlay::DoGetRectangle(wxSize size)
{
wxRect rect(mLastIndicatorX, 0, 1, size.GetHeight());
return std::make_pair(
rect,
mLastIndicatorX != mNewIndicatorX
);
}
void PlayIndicatorOverlay::Draw
(wxDC &dc, TrackPanelCellIterator begin, TrackPanelCellIterator end)
{
mLastIndicatorX = mNewIndicatorX;
if (!between_incexc(0, mLastIndicatorX, dc.GetSize().GetWidth()))
return;
const ZoomInfo &viewInfo = mProject->GetZoomInfo();
TrackPanel *const trackPanel = mProject->GetTrackPanel();
double pos = viewInfo.PositionToTime(mLastIndicatorX, trackPanel->GetLeftOffset());
// Set play/record color
bool rec = (gAudioIO->GetNumCaptureChannels() > 0);
AColor::IndicatorColor(&dc, !rec);
mProject->GetRulerPanel()->DrawIndicator(pos, rec);
// Draw indicator in all visible tracks
for (; begin != end; ++begin)
{
TrackPanelCellIterator::value_type data(*begin);
Track *const pTrack = data.first;
if (!pTrack)
continue;
// Don't draw the indicator in label tracks
if (pTrack->GetKind() == Track::Label)
{
continue;
}
// Draw the NEW indicator in its NEW location
// AColor::Line includes both endpoints so use GetBottom()
const wxRect &rect = data.second;
AColor::Line(dc,
mLastIndicatorX,
rect.GetTop(),
mLastIndicatorX,
rect.GetBottom());
}
}
void PlayIndicatorOverlay::Erase(wxDC &dc, wxDC &src)
{
TrackPanelOverlay::Erase(dc, src);
mProject->GetRulerPanel()->ClearIndicator();
}
void PlayIndicatorOverlay::OnTimer(wxCommandEvent &event)
{
// Let other listeners get the notification
event.Skip();
if (!mProject->IsAudioActive())
mNewIndicatorX = -1;
else {
ViewInfo &viewInfo = mProject->GetViewInfo();
// Calculate the horizontal position of the indicator
const double playPos = viewInfo.mRecentStreamTime;
const bool onScreen = playPos >= 0.0 &&
between_incexc(viewInfo.h,
playPos,
mProject->GetScreenEndTime());
// This displays the audio time, too...
mProject->TP_DisplaySelection();
// BG: Scroll screen if option is set
// msmeyer: But only if not playing looped or in one-second mode
if (viewInfo.bUpdateTrackIndicator &&
mProject->mLastPlayMode != loopedPlay &&
mProject->mLastPlayMode != oneSecondPlay &&
playPos >= 0 &&
!onScreen &&
!gAudioIO->IsPaused())
{
mProject->TP_ScrollWindow(playPos);
}
// Always update scrollbars even if not scrolling the window. This is
// important when NEW audio is recorded, because this can change the
// length of the project and therefore the appearance of the scrollbar.
mProject->TP_RedrawScrollbars();
mNewIndicatorX = viewInfo.TimeToPosition(playPos, mProject->GetTrackPanel()->GetLeftOffset());
}
}

View File

@ -0,0 +1,40 @@
/**********************************************************************
Audacity: A Digital Audio Editor
PlayIndicatorOverlay.h
Paul Licameli split from TrackPanel.cpp
**********************************************************************/
#ifndef __AUDACITY_PLAY_INDICATOR_OVERLAY__
#define __AUDACITY_PLAY_INDICATOR_OVERLAY__
#include "../../TrackPanelOverlay.h"
#include <wx/event.h>
class AudacityProject;
class PlayIndicatorOverlay final : public wxEvtHandler, public TrackPanelOverlay
{
public:
PlayIndicatorOverlay(AudacityProject *project);
virtual ~PlayIndicatorOverlay();
private:
std::pair<wxRect, bool> DoGetRectangle(wxSize size) override;
void Draw
(wxDC &dc, TrackPanelCellIterator begin, TrackPanelCellIterator end) override;
void Erase(wxDC &dc, wxDC &src) override;
void OnTimer(wxCommandEvent &event);
AudacityProject *mProject;
int mLastIndicatorX;
int mNewIndicatorX;
};
#endif

534
src/tracks/ui/Scrubbing.cpp Normal file
View File

@ -0,0 +1,534 @@
/**********************************************************************
Audacity: A Digital Audio Editor
Scrubbing.cpp
Paul Licameli split from TrackPanel.cpp
**********************************************************************/
#include "Scrubbing.h"
#include "../../Experimental.h"
#include "../../AudioIO.h"
#include "../../Project.h"
#include "../../TrackPanel.h"
#include "../../TrackPanelCell.h"
#include "../../TrackPanelCellIterator.h"
#include "../../toolbars/ControlToolBar.h"
#include <algorithm>
#include <wx/dc.h>
enum {
// PRL:
// Mouse must move at least this far to distinguish ctrl-drag to scrub
// from ctrl-click for playback.
SCRUBBING_PIXEL_TOLERANCE = 10,
#ifdef EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL
ScrubSpeedStepsPerOctave = 4,
#endif
};
namespace {
bool PollIsSeeking()
{
return ::wxGetMouseState().LeftIsDown();
}
double FindScrubbingSpeed(const ViewInfo &viewInfo, double maxScrubSpeed, double screen, double timeAtMouse)
{
// Map a time (which was mapped from a mouse position)
// to a speed.
// Map times to positive and negative speeds,
// with the time at the midline of the screen mapping to 0,
// and the extremes to the maximum scrub speed.
// Width of visible track area, in time terms:
const double origin = viewInfo.h + screen / 2.0;
// There are various snapping zones that are this fraction of screen:
const double snap = 0.05;
// By shrinking denom a bit, we make margins left and right
// that snap to maximum and negative maximum speeds.
const double factor = 1.0 - (snap * 2);
const double denom = factor * screen / 2.0;
double fraction = std::min(1.0, fabs(timeAtMouse - origin) / denom);
// Snap to 1.0 and -1.0
const double unity = 1.0 / maxScrubSpeed;
const double tolerance = snap / factor;
// Make speeds near 1 available too by remapping fractions outside
// this snap zone
if (fraction <= unity - tolerance)
fraction *= unity / (unity - tolerance);
else if (fraction < unity + tolerance)
fraction = unity;
else
fraction = unity + (fraction - (unity + tolerance)) *
(1.0 - unity) / (1.0 - (unity + tolerance));
double result = fraction * maxScrubSpeed;
if (timeAtMouse < origin)
result *= -1.0;
return result;
}
double FindSeekSpeed(const ViewInfo &viewInfo, double maxScrubSpeed, double screen, double timeAtMouse)
{
// Map a time (which was mapped from a mouse position)
// to a signed skip speed: a multiplier of the stutter duration,
// by which to advance the play position.
// (The stutter will play at unit speed.)
// Times near the midline of the screen map to skip-less play,
// and the extremes to a value proportional to maximum scrub speed.
// If the maximum scrubbing speed defaults to 1.0 when you begin to scroll-scrub,
// the extreme skipping for scroll-seek needs to be larger to be useful.
static const double ARBITRARY_MULTIPLIER = 10.0;
const double extreme = std::max(1.0, maxScrubSpeed * ARBITRARY_MULTIPLIER);
// Width of visible track area, in time terms:
const double halfScreen = screen / 2.0;
const double origin = viewInfo.h + halfScreen;
// The snapping zone is this fraction of screen, on each side of the
// center line:
const double snap = 0.05;
const double fraction =
std::max(snap, std::min(1.0, fabs(timeAtMouse - origin) / halfScreen));
double result = 1.0 + ((fraction - snap) / (1.0 - snap)) * (extreme - 1.0);
if (timeAtMouse < origin)
result *= -1.0;
return result;
}
}
Scrubber::Scrubber(AudacityProject *project)
: mScrubToken(-1)
, mScrubStartClockTimeMillis(-1)
, mScrubHasFocus(false)
, mScrubSpeedDisplayCountdown(0)
, mScrubStartPosition(-1)
, mMaxScrubSpeed(-1.0)
, mScrubSeekPress(false)
#ifdef EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL
, mSmoothScrollingScrub(false)
, mLogMaxScrubSpeed(0)
#endif
, mProject(project)
{
if (wxTheApp)
wxTheApp->Connect
(wxEVT_ACTIVATE_APP,
wxActivateEventHandler(Scrubber::OnActivateOrDeactivateApp), NULL, this);
}
Scrubber::~Scrubber()
{
if (wxTheApp)
wxTheApp->Disconnect
(wxEVT_ACTIVATE_APP,
wxActivateEventHandler(Scrubber::OnActivateOrDeactivateApp), NULL, this);
}
void Scrubber::MarkScrubStart(
wxCoord xx
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
, bool smoothScrolling
#endif
)
{
// Don't actually start scrubbing, but collect some information
// needed for the decision to start scrubbing later when handling
// drag events.
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
mSmoothScrollingScrub = smoothScrolling;
#endif
mScrubStartPosition = xx;
mScrubStartClockTimeMillis = ::wxGetLocalTimeMillis();
}
#ifdef EXPERIMENTAL_SCRUBBING_SUPPORT
bool Scrubber::MaybeStartScrubbing(const wxMouseEvent &event)
{
if (mScrubStartPosition < 0)
return false;
if (IsScrubbing())
return false;
else {
const bool busy = gAudioIO->IsBusy();
if (busy && gAudioIO->GetNumCaptureChannels() > 0) {
// Do not stop recording, and don't try to start scrubbing after
// recording stops
mScrubStartPosition = -1;
return false;
}
wxCoord position = event.m_x;
if (abs(mScrubStartPosition - position) >= SCRUBBING_PIXEL_TOLERANCE) {
const ViewInfo &viewInfo = mProject->GetViewInfo();
TrackPanel *const trackPanel = mProject->GetTrackPanel();
ControlToolBar * const ctb = mProject->GetControlToolBar();
double maxTime = mProject->GetTracks()->GetEndTime();
const int leftOffset = trackPanel->GetLeftOffset();
double time0 = std::min(maxTime,
viewInfo.PositionToTime(mScrubStartPosition, leftOffset)
);
double time1 = std::min(maxTime,
viewInfo.PositionToTime(position, leftOffset)
);
if (time1 != time0)
{
if (busy)
ctb->StopPlaying();
AudioIOStartStreamOptions options(mProject->GetDefaultPlayOptions());
options.timeTrack = NULL;
options.scrubDelay = (kTimerInterval / 1000.0);
options.scrubStartClockTimeMillis = mScrubStartClockTimeMillis;
options.minScrubStutter = 0.2;
#if 0
// Take the starting speed limit from the transcription toolbar,
// but it may be varied during the scrub.
mMaxScrubSpeed = options.maxScrubSpeed =
p->GetTranscriptionToolBar()->GetPlaySpeed();
#else
// That idea seems unpopular... just make it one
mMaxScrubSpeed = options.maxScrubSpeed = 1.0;
#endif
options.maxScrubTime = mProject->GetTracks()->GetEndTime();
const bool cutPreview = false;
const bool backwards = time1 < time0;
#ifdef EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL
static const double maxScrubSpeedBase =
pow(2.0, 1.0 / ScrubSpeedStepsPerOctave);
mLogMaxScrubSpeed = floor(0.5 +
log(mMaxScrubSpeed) / log(maxScrubSpeedBase)
);
#endif
mScrubSpeedDisplayCountdown = 0;
mScrubToken =
ctb->PlayPlayRegion(SelectedRegion(time0, time1), options, cutPreview, backwards);
}
}
else
// Wait to test again
mScrubStartClockTimeMillis = ::wxGetLocalTimeMillis();
if (IsScrubbing())
mScrubHasFocus = true;
// Return true whether we started scrub, or are still waiting to decide.
return true;
}
}
void Scrubber::ContinueScrubbing()
{
// Thus scrubbing relies mostly on periodic polling of mouse and keys,
// not event notifications. But there are a few event handlers that
// leave messages for this routine, in mScrubSeekPress and in mScrubHasFocus.
// Seek only when the pointer is in the panel. Else, scrub.
const wxMouseState state(::wxGetMouseState());
TrackPanel *const trackPanel = mProject->GetTrackPanel();
const wxPoint position = trackPanel->ScreenToClient(state.GetPosition());
const bool inPanel = trackPanel->GetRect().Contains(position);
const bool seek = inPanel && (mScrubSeekPress || PollIsSeeking());
// When we don't have focus, enqueue silent scrubs until we regain focus.
bool result = false;
if (!mScrubHasFocus)
result = gAudioIO->EnqueueScrubBySignedSpeed(0, mMaxScrubSpeed, false);
else {
const double time = mProject->GetViewInfo().PositionToTime(position.x, trackPanel->GetLeftOffset());
if (seek)
// Cause OnTimer() to suppress the speed display
mScrubSpeedDisplayCountdown = 1;
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
if (mSmoothScrollingScrub) {
const double speed = FindScrubSpeed(seek, time);
result = gAudioIO->EnqueueScrubBySignedSpeed(speed, mMaxScrubSpeed, seek);
}
else
#endif
result = gAudioIO->EnqueueScrubByPosition
(time, seek ? 1.0 : mMaxScrubSpeed, seek);
}
if (result)
mScrubSeekPress = false;
// else, if seek requested, try again at a later time when we might
// enqueue a long enough stutter
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
if (mSmoothScrollingScrub)
;
else
#endif
{
if (mScrubSpeedDisplayCountdown > 0)
--mScrubSpeedDisplayCountdown;
}
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
if (mSmoothScrollingScrub) {
// Pan the view, so that we center the play indicator.
ViewInfo &viewInfo = mProject->GetViewInfo();
TrackPanel *const trackPanel = mProject->GetTrackPanel();
const int posX = viewInfo.TimeToPosition(viewInfo.mRecentStreamTime);
int width;
trackPanel->GetTracksUsableArea(&width, NULL);
const int deltaX = posX - width / 2;
viewInfo.h =
viewInfo.OffsetTimeByPixels(viewInfo.h, deltaX, true);
if (!viewInfo.bScrollBeyondZero)
// Can't scroll too far left
viewInfo.h = std::max(0.0, viewInfo.h);
trackPanel->Refresh(false);
}
#endif
}
bool Scrubber::StopScrubbing()
{
if (IsScrubbing())
{
if (gAudioIO->IsBusy()) {
ControlToolBar *const ctb = mProject->GetControlToolBar();
ctb->StopPlaying();
}
return true;
}
else
return false;
}
bool Scrubber::IsScrubbing() const
{
if (mScrubToken <= 0)
return false;
else if (mScrubToken == mProject->GetAudioIOToken())
return true;
else {
const_cast<Scrubber&>(*this).mScrubToken = -1;
const_cast<Scrubber&>(*this).mScrubStartPosition = -1;
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
const_cast<Scrubber&>(*this).mSmoothScrollingScrub = false;
#endif
return false;
}
}
bool Scrubber::ShouldDrawScrubSpeed()
{
return IsScrubbing() &&
mScrubHasFocus && (
// Draw for (non-scroll) scrub, sometimes, but never for seek
(!PollIsSeeking() && mScrubSpeedDisplayCountdown > 0)
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
// Draw always for scroll-scrub and for scroll-seek
|| mSmoothScrollingScrub
#endif
);
}
double Scrubber::FindScrubSpeed(bool seeking, double time) const
{
ViewInfo &viewInfo = mProject->GetViewInfo();
const double screen = mProject->GetScreenEndTime() - viewInfo.h;
return (seeking ? FindSeekSpeed : FindScrubbingSpeed)
(viewInfo, mMaxScrubSpeed, screen, time);
}
void Scrubber::HandleScrollWheel(int steps)
{
const int newLogMaxScrubSpeed = mLogMaxScrubSpeed + steps;
static const double maxScrubSpeedBase =
pow(2.0, 1.0 / ScrubSpeedStepsPerOctave);
double newSpeed = pow(maxScrubSpeedBase, newLogMaxScrubSpeed);
if (newSpeed >= AudioIO::GetMinScrubSpeed() &&
newSpeed <= AudioIO::GetMaxScrubSpeed()) {
mLogMaxScrubSpeed = newLogMaxScrubSpeed;
mMaxScrubSpeed = newSpeed;
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
if (!mSmoothScrollingScrub)
#endif
// Show the speed for one second
mScrubSpeedDisplayCountdown = kOneSecondCountdown + 1;
}
}
void Scrubber::OnActivateOrDeactivateApp(wxActivateEvent &event)
{
if (event.GetActive())
mScrubHasFocus = IsScrubbing();
else
mScrubHasFocus = false;
event.Skip();
}
///////////////////////////////////////////////////////////////////////////////
// class ScrubbingOverlay is responsible for drawing the speed numbers
ScrubbingOverlay::ScrubbingOverlay(AudacityProject *project)
: mProject(project)
, mLastScrubRect()
, mNextScrubRect()
, mLastScrubSpeedText()
, mNextScrubSpeedText()
{
mProject->Connect(EVT_TRACK_PANEL_TIMER,
wxCommandEventHandler(ScrubbingOverlay::OnTimer),
NULL,
this);
}
ScrubbingOverlay::~ScrubbingOverlay()
{
mProject->Disconnect(EVT_TRACK_PANEL_TIMER,
wxCommandEventHandler(ScrubbingOverlay::OnTimer),
NULL,
this);
}
std::pair<wxRect, bool> ScrubbingOverlay::DoGetRectangle(wxSize)
{
wxRect rect(mLastScrubRect);
const bool outdated =
(mLastScrubRect != mNextScrubRect) ||
(!mLastScrubRect.IsEmpty() && !GetScrubber().ShouldDrawScrubSpeed()) ||
(mLastScrubSpeedText != mNextScrubSpeedText);
return std::make_pair(
rect,
outdated
);
}
void ScrubbingOverlay::Draw
(wxDC &dc, TrackPanelCellIterator, TrackPanelCellIterator)
{
mLastScrubRect = mNextScrubRect;
mLastScrubSpeedText = mNextScrubSpeedText;
Scrubber &scrubber = GetScrubber();
if (!scrubber.ShouldDrawScrubSpeed())
return;
static const wxFont labelFont(24, wxSWISS, wxNORMAL, wxNORMAL);
dc.SetFont(labelFont);
// These two colors were previously saturated red and green. However
// we have a rule to try to only use red for reserved purposes of
// (a) Recording
// (b) Error alerts
// So they were changed to 'orange' and 'lime'.
static const wxColour clrNoScroll(215, 162, 0), clrScroll(0, 204, 153);
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
if (scrubber.IsScrollScrubbing())
dc.SetTextForeground(clrScroll);
else
#endif
dc.SetTextForeground(clrNoScroll);
dc.DrawText(mLastScrubSpeedText, mLastScrubRect.GetX(), mLastScrubRect.GetY());
}
void ScrubbingOverlay::OnTimer(wxCommandEvent &event)
{
// Let other listeners get the notification
event.Skip();
Scrubber &scrubber = GetScrubber();
if (!GetScrubber().IsScrubbing()) {
mNextScrubRect = wxRect();
return;
}
// Call ContinueScrubbing() here in the timer handler
// rather than in SelectionHandleDrag()
// so that even without drag events, we can instruct the play head to
// keep approaching the mouse cursor, when its maximum speed is limited.
scrubber.ContinueScrubbing();
if (!scrubber.ShouldDrawScrubSpeed()) {
mNextScrubRect = wxRect();
}
else {
TrackPanel *const trackPanel = mProject->GetTrackPanel();
int panelWidth, panelHeight;
trackPanel->GetSize(&panelWidth, &panelHeight);
// Where's the mouse?
int xx, yy;
::wxGetMousePosition(&xx, &yy);
trackPanel->ScreenToClient(&xx, &yy);
const bool seeking = PollIsSeeking();
// Find the text
const double maxScrubSpeed = GetScrubber().GetMaxScrubSpeed();
const double speed =
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
scrubber.IsScrollScrubbing()
? scrubber.FindScrubSpeed
(seeking, mProject->GetViewInfo().PositionToTime(xx, trackPanel->GetLeftOffset()))
:
#endif
maxScrubSpeed;
const wxChar *format =
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
scrubber.IsScrollScrubbing()
? seeking
? wxT("%+.2fX")
: wxT("%+.2f")
:
#endif
wxT("%.2f");
mNextScrubSpeedText = wxString::Format(format, speed);
// Find the origin for drawing text
wxCoord width, height;
{
wxClientDC dc(trackPanel);
static const wxFont labelFont(24, wxSWISS, wxNORMAL, wxNORMAL);
dc.SetFont(labelFont);
dc.GetTextExtent(mNextScrubSpeedText, &width, &height);
}
xx = std::max(0, std::min(panelWidth - width, xx - width / 2));
// Put the text above the cursor, if it fits.
enum { offset = 20 };
yy -= height + offset;
if (yy < 0)
yy += height + 2 * offset;
yy = std::max(0, std::min(panelHeight - height, yy));
mNextScrubRect = wxRect(xx, yy, width, height);
}
}
const Scrubber &ScrubbingOverlay::GetScrubber() const
{
return mProject->GetScrubber();
}
Scrubber &ScrubbingOverlay::GetScrubber()
{
return mProject->GetScrubber();
}
#endif

95
src/tracks/ui/Scrubbing.h Normal file
View File

@ -0,0 +1,95 @@
/**********************************************************************
Audacity: A Digital Audio Editor
Scrubbing.h
Paul Licameli split from TrackPanel.cpp
**********************************************************************/
#ifndef __AUDACITY_SCRUBBING__
#define __AUDACITY_SCRUBBING__
#include <wx/event.h>
#include <wx/longlong.h>
#include "../../Experimental.h"
#include "../../TrackPanelOverlay.h"
class AudacityProject;
// Scrub state object
class Scrubber : public wxEvtHandler
{
public:
Scrubber(AudacityProject *project);
~Scrubber();
void MarkScrubStart(
wxCoord xx
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
, bool smoothScrolling
#endif
);
// Returns true iff the event should be considered consumed by this:
bool MaybeStartScrubbing(const wxMouseEvent &event);
void ContinueScrubbing();
bool StopScrubbing();
bool IsScrubbing() const;
bool IsScrollScrubbing() const // If true, implies IsScrubbing()
{ return mSmoothScrollingScrub; }
bool ShouldDrawScrubSpeed();
double FindScrubSpeed(bool seeking, double time) const;
double GetMaxScrubSpeed() const { return mMaxScrubSpeed; }
void HandleScrollWheel(int steps);
void SetSeeking() { mScrubSeekPress = true; }
private:
int mScrubToken;
wxLongLong mScrubStartClockTimeMillis;
bool mScrubHasFocus;
int mScrubSpeedDisplayCountdown;
wxCoord mScrubStartPosition;
double mMaxScrubSpeed;
bool mScrubSeekPress;
bool mSmoothScrollingScrub;
#ifdef EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL
int mLogMaxScrubSpeed;
#endif
private:
void OnActivateOrDeactivateApp(wxActivateEvent & event);
AudacityProject *mProject;
};
// Specialist in drawing the scrub speed, and listening for certain events
class ScrubbingOverlay final : public wxEvtHandler, public TrackPanelOverlay
{
public:
ScrubbingOverlay(AudacityProject *project);
virtual ~ScrubbingOverlay();
private:
std::pair<wxRect, bool> DoGetRectangle(wxSize size) override;
void Draw
(wxDC &dc, TrackPanelCellIterator begin, TrackPanelCellIterator end) override;
void OnTimer(wxCommandEvent &event);
const Scrubber &GetScrubber() const;
Scrubber &GetScrubber();
AudacityProject *mProject;
wxRect mLastScrubRect, mNextScrubRect;
wxString mLastScrubSpeedText, mNextScrubSpeedText;
};
#endif

View File

@ -66,6 +66,7 @@ array of Ruler::Label.
#include <wx/menuitem.h>
#include <wx/tooltip.h>
#include "../AColor.h"
#include "../AudioIO.h"
#include "../Internat.h"
#include "../Project.h"
@ -75,6 +76,8 @@ array of Ruler::Label.
#include "../Experimental.h"
#include "../TimeTrack.h"
#include "../TrackPanel.h"
#include "../TrackPanelCellIterator.h"
#include "../TrackPanelOverlay.h"
#include "../Menus.h"
#include "../NumberScale.h"
#include "../Prefs.h"
@ -1643,6 +1646,91 @@ void RulerPanel::DoSetSize(int x, int y,
ruler.SetBounds(0, 0, w-1, h-1);
}
/**********************************************************************
QuickPlayIndicatorOverlay.
Graphical helper for AdornedRulerPanel.
**********************************************************************/
class QuickPlayIndicatorOverlay final : public TrackPanelOverlay
{
public:
QuickPlayIndicatorOverlay(AudacityProject *project)
: mProject(project)
, mOldQPIndicatorPos(-1)
, mNewQPIndicatorPos(-1)
, mOldQPIndicatorSnapped(false)
, mNewQPIndicatorSnapped(false)
{
}
virtual ~QuickPlayIndicatorOverlay()
{
}
void Update(int x, bool snapped = false)
{
mNewQPIndicatorPos = x;
mNewQPIndicatorSnapped = snapped;
// Not strictly needed, but this reduces the lag in updating
// track panel causing momentary mismatch between the triangle
// in the ruler and the white line.
mProject->GetTrackPanel()->DrawOverlays(false);
}
private:
std::pair<wxRect, bool> DoGetRectangle(wxSize size) override;
void Draw
(wxDC &dc, TrackPanelCellIterator begin, TrackPanelCellIterator end) override;
AudacityProject *mProject;
int mOldQPIndicatorPos;
int mNewQPIndicatorPos;
bool mOldQPIndicatorSnapped;
bool mNewQPIndicatorSnapped;
};
std::pair<wxRect, bool> QuickPlayIndicatorOverlay::DoGetRectangle(wxSize size)
{
wxRect rect(mOldQPIndicatorPos, 0, 1, size.GetHeight());
return std::make_pair(
rect,
(mOldQPIndicatorPos != mNewQPIndicatorPos ||
mOldQPIndicatorSnapped != mNewQPIndicatorSnapped)
);
}
void QuickPlayIndicatorOverlay::Draw
(wxDC &dc, TrackPanelCellIterator begin, TrackPanelCellIterator end)
{
mOldQPIndicatorPos = mNewQPIndicatorPos;
mOldQPIndicatorSnapped = mNewQPIndicatorSnapped;
if (mOldQPIndicatorPos >= 0) {
mOldQPIndicatorSnapped ? AColor::SnapGuidePen(&dc) : AColor::Light(&dc, false);
// Draw indicator in all visible tracks
for (; begin != end; ++begin)
{
TrackPanelCellIterator::value_type data(*begin);
Track *const pTrack = dynamic_cast<Track*>(data.first);
if (!pTrack)
continue;
const wxRect &rect = data.second;
// Draw the NEW indicator in its NEW location
AColor::Line(dc,
mOldQPIndicatorPos,
rect.GetTop(),
mOldQPIndicatorPos,
rect.GetBottom());
}
}
}
/**********************************************************************
Implementation of AdornedRulerPanel.
@ -2525,6 +2613,16 @@ void AdornedRulerPanel::DoDrawIndicator(wxDC * dc)
dc->DrawPolygon( 3, tri );
}
QuickPlayIndicatorOverlay *AdornedRulerPanel::GetOverlay()
{
if (!mOverlay) {
TrackPanel *tp = mProject->GetTrackPanel();
mOverlay = std::make_unique<QuickPlayIndicatorOverlay>(mProject);
tp->AddOverlay(mOverlay.get());
}
return mOverlay.get();
}
// Draws the vertical line and green triangle indicating the Quick Play cursor position.
void AdornedRulerPanel::DrawQuickPlayIndicator(wxDC * dc)
{
@ -2532,7 +2630,7 @@ void AdornedRulerPanel::DrawQuickPlayIndicator(wxDC * dc)
double latestEnd = std::max(mTracks->GetEndTime(), mProject->GetSel1());
if (dc == NULL || (mQuickPlayPos >= latestEnd)) {
tp->DrawQuickPlayIndicator(-1);
GetOverlay()->Update(-1);
mLastQuickPlayX = -1;
return;
}
@ -2564,7 +2662,7 @@ void AdornedRulerPanel::DrawQuickPlayIndicator(wxDC * dc)
AColor::IndicatorColor( dc, true);
dc->DrawPolygon( 3, tri, x );
tp->DrawQuickPlayIndicator(x, mIsSnapped);
GetOverlay()->Update(x, mIsSnapped);
}
void AdornedRulerPanel::SetPlayRegion(double playRegionStart,

View File

@ -11,6 +11,7 @@
#ifndef __AUDACITY_RULER__
#define __AUDACITY_RULER__
#include "../MemoryX.h"
#include <wx/bitmap.h>
#include <wx/dc.h>
#include <wx/dcmemory.h>
@ -269,6 +270,8 @@ private:
DECLARE_EVENT_TABLE()
};
class QuickPlayIndicatorOverlay;
// This is an Audacity Specific ruler panel which additionally
// has border, selection markers, play marker.
// Once TrackPanel uses wxSizers, we will derive it from some
@ -320,6 +323,7 @@ private:
void DoDrawCursor(wxDC * dc);
void DoDrawSelection(wxDC * dc);
void DoDrawIndicator(wxDC * dc);
QuickPlayIndicatorOverlay *GetOverlay();
void DrawQuickPlayIndicator(wxDC * dc /*NULL to DELETE old only*/);
void DoDrawPlayRegion(wxDC * dc);
@ -398,6 +402,8 @@ private:
int mLastMouseX; // Pixel position
bool mIsDragging;
std::unique_ptr<QuickPlayIndicatorOverlay> mOverlay;
DECLARE_EVENT_TABLE()
};

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
@ -213,6 +213,10 @@
<ClCompile Include="..\..\..\src\TrackArtist.cpp" />
<ClCompile Include="..\..\..\src\TrackPanel.cpp" />
<ClCompile Include="..\..\..\src\TrackPanelAx.cpp" />
<ClCompile Include="..\..\..\src\TrackPanelOverlay.cpp" />
<ClCompile Include="..\..\..\src\tracks\ui\EditCursorOverlay.cpp" />
<ClCompile Include="..\..\..\src\tracks\ui\PlayIndicatorOverlay.cpp" />
<ClCompile Include="..\..\..\src\tracks\ui\Scrubbing.cpp" />
<ClCompile Include="..\..\..\src\UndoManager.cpp" />
<ClCompile Include="..\..\..\src\ViewInfo.cpp" />
<ClCompile Include="..\..\..\src\VoiceKey.cpp" />
@ -435,7 +439,13 @@
<ClInclude Include="..\..\..\src\SseMathFuncs.h" />
<ClInclude Include="..\..\..\src\toolbars\SpectralSelectionBar.h" />
<ClInclude Include="..\..\..\src\toolbars\SpectralSelectionBarListener.h" />
<ClInclude Include="..\..\..\src\TrackPanelCell.h" />
<ClInclude Include="..\..\..\src\TrackPanelCellIterator.h" />
<ClInclude Include="..\..\..\src\TrackPanelListener.h" />
<ClInclude Include="..\..\..\src\TrackPanelOverlay.h" />
<ClInclude Include="..\..\..\src\tracks\ui\EditCursorOverlay.h" />
<ClInclude Include="..\..\..\src\tracks\ui\PlayIndicatorOverlay.h" />
<ClInclude Include="..\..\..\src\tracks\ui\Scrubbing.h" />
<ClInclude Include="..\..\..\src\TranslatableStringArray.h" />
<ClInclude Include="..\..\..\src\WaveTrackLocation.h" />
<ClInclude Include="..\..\..\src\widgets\HelpSystem.h" />
@ -1097,4 +1107,4 @@
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\ny.targets" />
</ImportGroup>
</Project>
</Project>

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="src">
@ -69,6 +69,12 @@
<Filter Include="src\effects\VST">
<UniqueIdentifier>{99325e00-3930-4e03-a599-999f275db78e}</UniqueIdentifier>
</Filter>
<Filter Include="src\tracks">
<UniqueIdentifier>{7b403d1f-de11-43d5-becc-d9d118d06f18}</UniqueIdentifier>
</Filter>
<Filter Include="src\tracks\ui">
<UniqueIdentifier>{aa9627ea-e614-4704-bf68-4a347023569f}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\..\src\AboutDialog.cpp">
@ -275,9 +281,6 @@
<ClCompile Include="..\..\..\src\TrackArtist.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\TrackPanel.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\TrackPanelAx.cpp">
<Filter>src</Filter>
</ClCompile>
@ -857,6 +860,21 @@
<ClCompile Include="..\..\..\src\RealFFTf48x.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\TrackPanelOverlay.cpp">
<Filter>src</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\tracks\ui\EditCursorOverlay.cpp">
<Filter>src\tracks\ui</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\tracks\ui\PlayIndicatorOverlay.cpp">
<Filter>src\tracks\ui</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\tracks\ui\Scrubbing.cpp">
<Filter>src\tracks\ui</Filter>
</ClCompile>
<ClCompile Include="..\..\..\src\TrackPanel.cpp">
<Filter>src</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\src\AboutDialog.h">
@ -1714,6 +1732,9 @@
<ClInclude Include="..\..\..\src\effects\VST\VSTControlMSW.h">
<Filter>src\effects\VST</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\TrackPanelOverlay.h">
<Filter>src</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\TranslatableStringArray.h">
<Filter>src</Filter>
</ClInclude>
@ -1732,6 +1753,21 @@
<ClInclude Include="..\..\..\src\RealFFTf48x.h">
<Filter>src</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\TrackPanelCell.h">
<Filter>src</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\tracks\ui\EditCursorOverlay.h">
<Filter>src\tracks\ui</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\tracks\ui\PlayIndicatorOverlay.h">
<Filter>src\tracks\ui</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\tracks\ui\Scrubbing.h">
<Filter>src\tracks\ui</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\TrackPanelCellIterator.h">
<Filter>src</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Image Include="..\..\audacity.ico">
@ -1952,4 +1988,4 @@
<Filter>plug-ins</Filter>
</copy>
</ItemGroup>
</Project>
</Project>