mirror of
https://github.com/cookiengineer/audacity
synced 2025-06-28 22:28:44 +02:00
Add user interface for scrubbing and Experimental.h switches
EXPERIMENTAL_SCRUBBING_BASIC enables middle-click-drag to scrub, or to seek when shift is down. EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL adds middle-double-click-drag to scrub or (with SHIFT down) to seek while keeping the playhead at the midline of the track display. EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL allows control wheel to vary the speed limit, while scrubbing (not seeking). All three are enabled in this commit. Also notice the changes to status bar messages and the Mouse Preferences dialog. Scrubbing works only in Select and Multi tools.
This commit is contained in:
parent
f549e335f6
commit
10e35bb6bb
@ -169,5 +169,13 @@
|
||||
// Paul Licameli (PRL) 16 Apr 2015
|
||||
//Support for scrubbing in the AudioIO engine, without calls to it
|
||||
#define EXPERIMENTAL_SCRUBBING_SUPPORT
|
||||
|
||||
// The following enable parts of the scrubbing user interface.
|
||||
// You must define EXPERIMENTAL_SCRUBBING_SUPPORT if you enable this:
|
||||
#define EXPERIMENTAL_SCRUBBING_BASIC
|
||||
// You must define EXPERIMENTAL_SCRUBBING_BASIC if you enable this:
|
||||
#define EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
|
||||
// You must define EXPERIMENTAL_SCRUBBING_BASIC if you enable this:
|
||||
#define EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL
|
||||
|
||||
#endif
|
||||
|
@ -217,6 +217,7 @@ is time to refresh some aspect of the screen.
|
||||
#include "toolbars/ControlToolBar.h"
|
||||
#include "toolbars/ToolManager.h"
|
||||
#include "toolbars/ToolsToolBar.h"
|
||||
#include "toolbars/TranscriptionToolBar.h"
|
||||
|
||||
#include "widgets/ASlider.h"
|
||||
#include "widgets/Ruler.h"
|
||||
@ -232,9 +233,23 @@ WX_DEFINE_OBJARRAY(TrackClipArray);
|
||||
#include "../images/Cursors.h"
|
||||
#include <iostream>
|
||||
|
||||
#define kLeftInset 4
|
||||
#define kTopInset 4
|
||||
#define kTimerInterval 50 // milliseconds
|
||||
enum {
|
||||
kLeftInset = 4,
|
||||
kTopInset = 4,
|
||||
kTimerInterval = 50, // milliseconds
|
||||
kOneSecondCountdown = 1000 / kTimerInterval,
|
||||
};
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
// Is the distance between A and B less than D?
|
||||
template < class A, class B, class DIST > bool within(A a, B b, DIST d)
|
||||
@ -586,6 +601,22 @@ TrackPanel::TrackPanel(wxWindow * parent, wxWindowID id,
|
||||
mSelStartValid = false;
|
||||
mSelStart = 0;
|
||||
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_BASIC
|
||||
mScrubToken = -1;
|
||||
mScrubStartClockTimeMillis = -1;
|
||||
mScrubStartPosition = 0;
|
||||
mMaxScrubSpeed = 1.0;
|
||||
mScrubSpeedDisplayCountdown = 0;
|
||||
#endif
|
||||
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
|
||||
mSmoothScrollingScrub = false;
|
||||
#endif
|
||||
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL
|
||||
mLogMaxScrubSpeed = 0;
|
||||
#endif
|
||||
|
||||
mInitialTrackSelection = new std::vector<bool>;
|
||||
}
|
||||
|
||||
@ -999,6 +1030,35 @@ void TrackPanel::OnTimer()
|
||||
(p->mLastPlayMode == loopedPlay));
|
||||
}
|
||||
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_BASIC
|
||||
// Call ContinueScrubbing() here 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.
|
||||
if (IsScrubbing())
|
||||
{
|
||||
wxMouseState state = ::wxGetMouseState();
|
||||
wxCoord position = state.GetX();
|
||||
ScreenToClient(&position, NULL);
|
||||
ContinueScrubbing(position, state.ShiftDown());
|
||||
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
|
||||
if (mSmoothScrollingScrub)
|
||||
// Redraw with every timer tick, to keep the indicator centered.
|
||||
Refresh(false);
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if (mScrubSpeedDisplayCountdown > 0) {
|
||||
--mScrubSpeedDisplayCountdown;
|
||||
if (mScrubSpeedDisplayCountdown == kOneSecondCountdown ||
|
||||
mScrubSpeedDisplayCountdown == 0)
|
||||
// Show or hide the maximum speed.
|
||||
Refresh(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Check whether we were playing or recording, but the stream has stopped.
|
||||
if (p->GetAudioIOToken()>0 &&
|
||||
!gAudioIO->IsStreamActive(p->GetAudioIOToken()))
|
||||
@ -1039,8 +1099,14 @@ void TrackPanel::OnTimer()
|
||||
// AS: The "indicator" is the little graphical mark shown in the ruler
|
||||
// that indicates where the current play/record position is. (This also
|
||||
// draws the moving vertical line.)
|
||||
|
||||
// PRL: mIndicatorShowing never becomes true!
|
||||
if (!gAudioIO->IsPaused() &&
|
||||
( mIndicatorShowing || gAudioIO->IsStreamActive(p->GetAudioIOToken())))
|
||||
( mIndicatorShowing || gAudioIO->IsStreamActive(p->GetAudioIOToken()))
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
|
||||
&& !mSmoothScrollingScrub
|
||||
#endif
|
||||
)
|
||||
{
|
||||
DrawIndicator();
|
||||
}
|
||||
@ -1151,7 +1217,8 @@ void TrackPanel::DrawQuickPlayIndicator(wxDC & dc, double pos)
|
||||
}
|
||||
|
||||
/// Second level DrawIndicator()
|
||||
void TrackPanel::DoDrawIndicator(wxDC & dc, bool repairOld /* = false */)
|
||||
void TrackPanel::DoDrawIndicator
|
||||
(wxDC & dc, bool repairOld /* = false */, double indicator /* = -1 */)
|
||||
{
|
||||
bool onScreen;
|
||||
int x;
|
||||
@ -1191,8 +1258,10 @@ void TrackPanel::DoDrawIndicator(wxDC & dc, bool repairOld /* = false */)
|
||||
mLastIndicator = -1;
|
||||
}
|
||||
|
||||
// The stream time can be < 0 if the audio is currently stopped
|
||||
pos = gAudioIO->GetStreamTime();
|
||||
pos = indicator;
|
||||
if (pos < 0.0)
|
||||
// The stream time can be < 0 if the audio is currently stopped
|
||||
pos = gAudioIO->GetStreamTime();
|
||||
|
||||
AudacityProject *p = GetProject();
|
||||
bool audioActive = ( gAudioIO->IsStreamActive( p->GetAudioIOToken() ) != 0 );
|
||||
@ -1394,14 +1463,16 @@ void TrackPanel::OnPaint(wxPaintEvent & /* event */)
|
||||
|
||||
// Construct the paint DC on the heap so that it may be deleted
|
||||
// early
|
||||
wxDC *dc = new wxPaintDC( this );
|
||||
wxDC *dc = new wxPaintDC(this);
|
||||
|
||||
// Retrieve the damage rectangle
|
||||
wxRect box = GetUpdateRegion().GetBox();
|
||||
|
||||
double indicator = -1;
|
||||
|
||||
// Recreate the backing bitmap if we have a full refresh
|
||||
// (See TrackPanel::Refresh())
|
||||
if( mRefreshBacking || ( box == GetRect() ) )
|
||||
if (mRefreshBacking || (box == GetRect()))
|
||||
{
|
||||
// Update visible sliders
|
||||
mTrackInfo.UpdateSliderOffset(mViewInfo->track);
|
||||
@ -1409,6 +1480,20 @@ void TrackPanel::OnPaint(wxPaintEvent & /* event */)
|
||||
// Reset (should a mutex be used???)
|
||||
mRefreshBacking = false;
|
||||
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
|
||||
if (mSmoothScrollingScrub &&
|
||||
gAudioIO->IsStreamActive(GetProject()->GetAudioIOToken())) {
|
||||
// Pan the view, so that we center the play indicator.
|
||||
// By the time DoDrawIndicator() is reached, gAudioIO->GetStreamTime()
|
||||
// may be a little different.
|
||||
// Cause DoDrawIndicator to show the value as it was at this time when we begin
|
||||
// the drawing of the tracks. This prevents flashing of the indicator
|
||||
// at higher magnifications, and keeps the green line still in the middle.
|
||||
indicator = gAudioIO->GetStreamTime();
|
||||
mViewInfo->h = std::max(0.0, indicator - mViewInfo->screen / 2.0);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Redraw the backing bitmap
|
||||
DrawTracks(&mBackingDC);
|
||||
|
||||
@ -1434,9 +1519,10 @@ void TrackPanel::OnPaint(wxPaintEvent & /* event */)
|
||||
if (!gAudioIO->IsPaused() &&
|
||||
(mIndicatorShowing || gAudioIO->IsStreamActive(p->GetAudioIOToken())))
|
||||
{
|
||||
// We just want to repair, not update the old, so set the second param to true.
|
||||
// If not smooth scrolling, then
|
||||
// we just want to repair, not update the old, so set the second param to true.
|
||||
// This is important because this onPaint could be for just some of the tracks.
|
||||
DoDrawIndicator( cdc, true);
|
||||
DoDrawIndicator(cdc, (indicator < 0), indicator);
|
||||
}
|
||||
|
||||
// Draw the cursor
|
||||
@ -2041,6 +2127,27 @@ void TrackPanel::HandleSelect(wxMouseEvent & event)
|
||||
wxRect r;
|
||||
Track *t = FindTrack(event.m_x, event.m_y, false, false, &r);
|
||||
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_BASIC
|
||||
if (
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
|
||||
event.MiddleDClick() ||
|
||||
#endif
|
||||
event.MiddleDown()) {
|
||||
if (IsScrubbing())
|
||||
StopScrubbing();
|
||||
// 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 = event.MiddleDClick();
|
||||
#endif
|
||||
mScrubStartPosition = event.m_x;
|
||||
mScrubStartClockTimeMillis = ::wxGetLocalTimeMillis();
|
||||
mMouseCapture = IsSelecting;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// AS: Ok, did the user just click the mouse, release the mouse,
|
||||
// or drag?
|
||||
if (event.LeftDown()) {
|
||||
@ -2170,6 +2277,161 @@ void TrackPanel::StartOrJumpPlayback(wxMouseEvent &event)
|
||||
}
|
||||
|
||||
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_BASIC
|
||||
bool TrackPanel::IsScrubbing()
|
||||
{
|
||||
if (mScrubToken <= 0)
|
||||
return false;
|
||||
else if (mScrubToken == GetProject()->GetAudioIOToken())
|
||||
return true;
|
||||
else {
|
||||
// Some other command might have stopped scrub play before we
|
||||
// reached StopScrubbing()! But that is okay.
|
||||
mScrubToken = -1;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
|
||||
double TrackPanel::FindScrubSpeed(double timeAtMouse) const
|
||||
{
|
||||
// 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 screen = mViewInfo->screen;
|
||||
const double origin = mViewInfo->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 / mMaxScrubSpeed;
|
||||
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 * mMaxScrubSpeed;
|
||||
if (timeAtMouse < origin)
|
||||
result *= -1.0;
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool TrackPanel::MaybeStartScrubbing(wxMouseEvent &event)
|
||||
{
|
||||
if (IsScrubbing())
|
||||
return false;
|
||||
else
|
||||
{
|
||||
wxCoord position = event.m_x;
|
||||
AudacityProject *p = GetActiveProject();
|
||||
if (p &&
|
||||
abs(mScrubStartPosition - position) >= SCRUBBING_PIXEL_TOLERANCE) {
|
||||
ControlToolBar * ctb = p->GetControlToolBar();
|
||||
bool busy = gAudioIO->IsBusy();
|
||||
double maxTime = p->GetTracks()->GetEndTime();
|
||||
double time0 = std::min(maxTime, PositionToTime(mScrubStartPosition, GetLeftOffset()));
|
||||
double time1 = std::min(maxTime, PositionToTime(position, GetLeftOffset()));
|
||||
if (time1 != time0)
|
||||
{
|
||||
if (busy)
|
||||
ctb->StopPlaying();
|
||||
|
||||
AudioIOStartStreamOptions options(p->GetDefaultPlayOptions());
|
||||
options.timeTrack = NULL;
|
||||
options.scrubDelay = (kTimerInterval / 1000.0);
|
||||
options.scrubStartClockTimeMillis = mScrubStartClockTimeMillis;
|
||||
options.minScrubStutter = 0.2;
|
||||
// Take the starting speed limit from the transcription toolbar,
|
||||
// but it may be varied during the scrub.
|
||||
mMaxScrubSpeed = options.maxScrubSpeed =
|
||||
p->GetTranscriptionToolBar()->GetPlaySpeed();
|
||||
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
|
||||
mScrubStartClockTimeMillis = ::wxGetLocalTimeMillis();
|
||||
if (IsScrubbing()) {
|
||||
mMouseCapture = IsMiddleButtonScrubbing;
|
||||
CaptureMouse();
|
||||
}
|
||||
return IsScrubbing();
|
||||
}
|
||||
}
|
||||
|
||||
bool TrackPanel::ContinueScrubbing(wxCoord position, bool maySkip)
|
||||
{
|
||||
wxCoord leadPosition = position;
|
||||
double newEnd = PositionToTime(leadPosition, GetLeftOffset());
|
||||
|
||||
if (maySkip)
|
||||
// Cause OnTimer() to suppress the speed display
|
||||
mScrubSpeedDisplayCountdown = 1;
|
||||
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
|
||||
if (mSmoothScrollingScrub && !maySkip) {
|
||||
const double speed = FindScrubSpeed(newEnd);
|
||||
return gAudioIO->EnqueueScrubBySignedSpeed(speed, mMaxScrubSpeed, maySkip);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
return gAudioIO->EnqueueScrubByPosition
|
||||
(newEnd, maySkip ? 1.0 : mMaxScrubSpeed, maySkip);
|
||||
}
|
||||
|
||||
bool TrackPanel::StopScrubbing()
|
||||
{
|
||||
if (IsScrubbing())
|
||||
{
|
||||
if (gAudioIO->IsBusy()) {
|
||||
AudacityProject *p = GetActiveProject();
|
||||
if (p) {
|
||||
ControlToolBar * ctb = p->GetControlToolBar();
|
||||
ctb->StopPlaying();
|
||||
}
|
||||
}
|
||||
mScrubToken = -1;
|
||||
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
|
||||
mSmoothScrollingScrub = false;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/// This method gets called when we're handling selection
|
||||
/// and the mouse was just clicked.
|
||||
void TrackPanel::SelectionHandleClick(wxMouseEvent & event,
|
||||
@ -2295,6 +2557,7 @@ void TrackPanel::SelectionHandleClick(wxMouseEvent & event,
|
||||
&& !stretch
|
||||
#endif
|
||||
) {
|
||||
|
||||
StartOrJumpPlayback(event);
|
||||
|
||||
// Not starting a drag
|
||||
@ -3048,6 +3311,20 @@ void TrackPanel::SelectionHandleDrag(wxMouseEvent & event, Track *clickedTrack)
|
||||
if (!event.Dragging() && !mAutoScrolling)
|
||||
return;
|
||||
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_BASIC
|
||||
if (IsScrubbing()) {
|
||||
// May need a screen update, but do nothing else. Don't change selection.
|
||||
if (mAutoScrolling)
|
||||
UpdateSelectionDisplay();
|
||||
return;
|
||||
}
|
||||
else if (event.MiddleIsDown()) {
|
||||
MaybeStartScrubbing(event);
|
||||
// Do nothing more, don't change selection
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (event.CmdDown()) {
|
||||
// Ctrl-drag has no meaning, fuhggeddaboudit
|
||||
return;
|
||||
@ -5884,13 +6161,20 @@ void TrackPanel::HandleWheelRotation(wxMouseEvent & event)
|
||||
double steps = event.m_wheelRotation /
|
||||
(event.m_wheelDelta > 0 ? (double)event.m_wheelDelta : 120.0);
|
||||
|
||||
if (event.ShiftDown())
|
||||
if (event.ShiftDown()
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
|
||||
// Don't pan during smooth scrolling. That would conflict with keeping
|
||||
// the play indicator centered.
|
||||
&& !mSmoothScrollingScrub
|
||||
#endif
|
||||
)
|
||||
{
|
||||
// MM: Scroll left/right when used with Shift key down
|
||||
mListener->TP_ScrollWindow(
|
||||
mViewInfo->h +
|
||||
50.0 * -steps / mViewInfo->zoom);
|
||||
} else if (event.CmdDown())
|
||||
}
|
||||
else if (event.CmdDown())
|
||||
{
|
||||
#if 0
|
||||
// JKC: Alternative scroll wheel zooming code
|
||||
@ -5910,7 +6194,20 @@ void TrackPanel::HandleWheelRotation(wxMouseEvent & event)
|
||||
int trackLeftEdge = GetLeftOffset();
|
||||
|
||||
// Time corresponding to mouse position
|
||||
double center_h = PositionToTime(event.m_x, trackLeftEdge);
|
||||
wxCoord xx;
|
||||
double center_h;
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
|
||||
if (mSmoothScrollingScrub) {
|
||||
// Expand or contract about the center, ignoring mouse position
|
||||
center_h = mViewInfo->h + mViewInfo->screen / 2.0;
|
||||
xx = TimeToPosition(center_h, trackLeftEdge);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
xx = event.m_x;
|
||||
center_h = PositionToTime(xx, trackLeftEdge);
|
||||
}
|
||||
// Time corresponding to last (most far right) audio.
|
||||
double audioEndTime = mTracks->GetEndTime();
|
||||
|
||||
@ -5928,18 +6225,40 @@ void TrackPanel::HandleWheelRotation(wxMouseEvent & event)
|
||||
// Constrain maximum as well as minimum zoom.
|
||||
mViewInfo->zoom = wxMax( gMinZoom, wxMin(mViewInfo->zoom * pow(2.0, steps), gMaxZoom));
|
||||
|
||||
double new_center_h = PositionToTime(event.m_x, trackLeftEdge);
|
||||
double new_center_h = PositionToTime(xx, trackLeftEdge);
|
||||
mViewInfo->h += (center_h - new_center_h);
|
||||
|
||||
MakeParentRedrawScrollbars();
|
||||
Refresh(false);
|
||||
} else
|
||||
}
|
||||
else
|
||||
{
|
||||
// MM: Scroll up/down when used without modifier keys
|
||||
double lines = steps * 4 + mVertScrollRemainder;
|
||||
mVertScrollRemainder = lines - floor(lines);
|
||||
lines = floor(lines);
|
||||
mListener->TP_ScrollUpDown((int)-lines);
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL
|
||||
if (IsScrubbing()) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
// MM: Scroll up/down when used without modifier keys
|
||||
double lines = steps * 4 + mVertScrollRemainder;
|
||||
mVertScrollRemainder = lines - floor(lines);
|
||||
lines = floor(lines);
|
||||
mListener->TP_ScrollUpDown((int)-lines);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6190,6 +6509,18 @@ void TrackPanel::OnMouseEvent(wxMouseEvent & event)
|
||||
case IsAdjustingLabel:
|
||||
HandleLabelTrackMouseEvent((LabelTrack *)mCapturedTrack, mCapturedRect, event);
|
||||
break;
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_BASIC
|
||||
case IsMiddleButtonScrubbing:
|
||||
if (event.MiddleUp()) {
|
||||
if (IsScrubbing()) {
|
||||
StopScrubbing();
|
||||
if (HasCapture())
|
||||
ReleaseMouse();
|
||||
mMouseCapture = IsUncaptured;
|
||||
}
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default: //includes case of IsUncaptured
|
||||
HandleTrackSpecificMouseEvent(event);
|
||||
break;
|
||||
@ -6993,6 +7324,72 @@ void TrackPanel::DrawEverythingElse(wxDC * dc,
|
||||
AColor::Line(*dc, (int)mSnapRight, 0, mSnapRight, 30000);
|
||||
}
|
||||
}
|
||||
|
||||
if (IsScrubbing())
|
||||
DrawScrubSpeed(*dc);
|
||||
}
|
||||
|
||||
void TrackPanel::DrawScrubSpeed(wxDC &dc)
|
||||
{
|
||||
// Don't draw it during stutter play with shift down
|
||||
if (!::wxGetMouseState().ShiftDown() && (
|
||||
|
||||
mScrubSpeedDisplayCountdown > 0
|
||||
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
|
||||
|| mSmoothScrollingScrub
|
||||
#endif
|
||||
|
||||
)) {
|
||||
int panelWidth, panelHeight;
|
||||
GetSize(&panelWidth, &panelHeight);
|
||||
|
||||
// Where's the mouse?
|
||||
int xx, yy;
|
||||
::wxGetMousePosition(&xx, &yy);
|
||||
ScreenToClient(&xx, &yy);
|
||||
|
||||
// Find the text
|
||||
const double speed =
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
|
||||
mSmoothScrollingScrub
|
||||
? FindScrubSpeed(PositionToTime(xx, GetLeftOffset()))
|
||||
:
|
||||
#endif
|
||||
mMaxScrubSpeed;
|
||||
const wxChar *format =
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
|
||||
mSmoothScrollingScrub ? wxT("%+.2f")
|
||||
:
|
||||
#endif
|
||||
wxT("%.2f");
|
||||
wxString text(wxString::Format(format, speed));
|
||||
|
||||
static const wxFont labelFont(24, wxSWISS, wxNORMAL, wxNORMAL);
|
||||
dc.SetFont(labelFont);
|
||||
|
||||
// Find the origin for drawing text
|
||||
wxCoord width, height;
|
||||
dc.GetTextExtent(text, &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));
|
||||
|
||||
// To do, theming?
|
||||
static const wxColour red(255, 0, 0), green(0, 255, 0);
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
|
||||
if (mSmoothScrollingScrub)
|
||||
dc.SetTextForeground(green);
|
||||
else
|
||||
#endif
|
||||
dc.SetTextForeground(red);
|
||||
dc.DrawText(text, xx, yy);
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw zooming indicator that shows the region that will
|
||||
|
@ -272,7 +272,8 @@ class AUDACITY_DLL_API TrackPanel:public wxPanel {
|
||||
virtual void DrawIndicator();
|
||||
/// draws the green line on the tracks to show playback position
|
||||
/// @param repairOld if true the playback position is not updated/erased, and simply redrawn
|
||||
virtual void DoDrawIndicator(wxDC & dc, bool repairOld = false);
|
||||
/// @param indicator if nonnegative, overrides the indicator value obtainable from AudioIO
|
||||
virtual void DoDrawIndicator(wxDC & dc, bool repairOld = false, double indicator = -1);
|
||||
virtual void DrawCursor();
|
||||
virtual void DoDrawCursor(wxDC & dc);
|
||||
|
||||
@ -315,6 +316,17 @@ class AUDACITY_DLL_API TrackPanel:public wxPanel {
|
||||
virtual void HandleSelect(wxMouseEvent & event);
|
||||
virtual void SelectionHandleDrag(wxMouseEvent &event, Track *pTrack);
|
||||
void StartOrJumpPlayback(wxMouseEvent &event);
|
||||
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
|
||||
double FindScrubSpeed(double timeAtMouse) const;
|
||||
#endif
|
||||
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_BASIC
|
||||
bool MaybeStartScrubbing(wxMouseEvent &event);
|
||||
bool ContinueScrubbing(wxCoord position, bool maySkip);
|
||||
bool StopScrubbing();
|
||||
#endif
|
||||
|
||||
virtual void SelectionHandleClick(wxMouseEvent &event,
|
||||
Track* pTrack, wxRect r);
|
||||
virtual void StartSelection (int mouseXCoordinate, int trackLeftEdge);
|
||||
@ -513,6 +525,7 @@ protected:
|
||||
const wxRect panelRect, const wxRect clip);
|
||||
virtual void DrawOutside(Track *t, wxDC *dc, const wxRect rec,
|
||||
const wxRect trackRect);
|
||||
void DrawScrubSpeed(wxDC &dc);
|
||||
virtual void DrawZooming(wxDC* dc, const wxRect clip);
|
||||
|
||||
virtual void HighlightFocusedTrack (wxDC* dc, const wxRect r);
|
||||
@ -750,7 +763,10 @@ protected:
|
||||
#ifdef USE_MIDI
|
||||
IsStretching,
|
||||
#endif
|
||||
IsZooming
|
||||
IsZooming,
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_BASIC
|
||||
IsMiddleButtonScrubbing,
|
||||
#endif
|
||||
};
|
||||
|
||||
enum MouseCaptureEnum mMouseCapture;
|
||||
@ -766,6 +782,23 @@ protected:
|
||||
int mMoveUpThreshold;
|
||||
int mMoveDownThreshold;
|
||||
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_BASIC
|
||||
bool IsScrubbing();
|
||||
int mScrubToken;
|
||||
wxLongLong mScrubStartClockTimeMillis;
|
||||
wxCoord mScrubStartPosition;
|
||||
double mMaxScrubSpeed;
|
||||
int mScrubSpeedDisplayCountdown;
|
||||
#endif
|
||||
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
|
||||
bool mSmoothScrollingScrub;
|
||||
#endif
|
||||
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL
|
||||
int mLogMaxScrubSpeed;
|
||||
#endif
|
||||
|
||||
wxCursor *mArrowCursor;
|
||||
wxCursor *mPencilCursor;
|
||||
wxCursor *mSelectCursor;
|
||||
|
@ -105,6 +105,14 @@ void MousePrefs::CreateList()
|
||||
AddItem(_("Shift-Left-Click"), _("Select"), _("Extend Selection Range"));
|
||||
AddItem(_("Left-Double-Click"), _("Select"), _("Select Clip or Entire Track"));
|
||||
AddItem(_("Ctrl-Left-Click"), _("Select"), _("Set Selection Point and Play"));
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_BASIC
|
||||
AddItem(_("Middle-Drag"), _("Select"), _("Scrub"));
|
||||
AddItem(_("Shift-Middle-Drag"), _("Select"), _("Seek"));
|
||||
#endif
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
|
||||
AddItem(_("Middle-Double-Click-Drag"), _("Select"), _("Smooth Scrolling Scrub"));
|
||||
AddItem(_("Wheel-Rotate"), _("Select"), _("Change maximum scrub speed"));
|
||||
#endif
|
||||
|
||||
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
|
||||
// JKC: Prompt is disabled for now. It's a toggle rather than a drag modifier.
|
||||
@ -136,6 +144,14 @@ void MousePrefs::CreateList()
|
||||
|
||||
AddItem(_("Left-Click"), _("Multi"), _("Set Selection Point"), _("same as select tool"));
|
||||
AddItem(_("Left-Drag"), _("Multi"), _("Set Selection Range"), _("same as select tool"));
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_BASIC
|
||||
AddItem(_("Middle-Drag"), _("Select"), _("Scrub"), _("same as select tool"));
|
||||
AddItem(_("Shift-Middle-Drag"), _("Select"), _("Seek"), _("same as select tool"));
|
||||
#endif
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL
|
||||
AddItem(_("Middle-Double-Click-Drag"), _("Select"), _("Smooth Scrolling Scrub"), _("same as select tool"));
|
||||
AddItem(_("Wheel-Rotate"), _("Select"), _("Change maximum scrub speed"), _("same as select tool"));
|
||||
#endif
|
||||
AddItem(_("Right-Click"), _("Multi"), _("Zoom out one step"), _("same as zoom tool"));
|
||||
AddItem(_("Right-Drag"), _("Multi"), _("Zoom in on a Range"), _("same as zoom tool"));
|
||||
|
||||
|
@ -80,7 +80,12 @@ ToolsToolBar::ToolsToolBar()
|
||||
wxASSERT( drawTool == drawTool - firstTool );
|
||||
wxASSERT( multiTool == multiTool - firstTool );
|
||||
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_BASIC
|
||||
mMessageOfTool[selectTool] =
|
||||
_("Click and drag to select audio, Middle-Click and drag to scrub, Shift-Middle-Click and drag to seek");
|
||||
#else
|
||||
mMessageOfTool[selectTool] = _("Click and drag to select audio");
|
||||
#endif
|
||||
mMessageOfTool[envelopeTool] = _("Click and drag to edit the amplitude envelope");
|
||||
mMessageOfTool[drawTool] = _("Click and drag to edit the samples");
|
||||
#if defined( __WXMAC__ )
|
||||
|
@ -99,6 +99,8 @@ class TranscriptionToolBar:public ToolBar {
|
||||
void SetEnabled(bool enabled);
|
||||
void SetPlaying(bool down, bool looped, bool cutPreview);
|
||||
|
||||
double GetPlaySpeed() const { return mPlaySpeed / 100.0; }
|
||||
|
||||
private:
|
||||
|
||||
void InitializeTranscriptionToolBar();
|
||||
|
Loading…
x
Reference in New Issue
Block a user