mirror of
https://github.com/cookiengineer/audacity
synced 2025-06-16 08:09:32 +02:00
Changes to seeking and scrubbing from Paul Licameli.
These are mostly under an EXPERIMENTAL_ #ifdef. Also has a change for the prompt string for preferences so the displayed keybinding is adjusted when in multitool mode.
This commit is contained in:
parent
94c243cb2e
commit
c71397beae
@ -605,6 +605,8 @@ AudioIO::AudioIO()
|
|||||||
mMixerOutputVol = 1.0;
|
mMixerOutputVol = 1.0;
|
||||||
mInputMixerWorks = false;
|
mInputMixerWorks = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
mLastPlaybackTimeMillis = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioIO::~AudioIO()
|
AudioIO::~AudioIO()
|
||||||
@ -1127,7 +1129,8 @@ int AudioIO::StartStream(WaveTrackArray playbackTracks,
|
|||||||
AudioIOListener* listener,
|
AudioIOListener* listener,
|
||||||
bool playLooped /* = false */,
|
bool playLooped /* = false */,
|
||||||
double cutPreviewGapStart /* = 0.0 */,
|
double cutPreviewGapStart /* = 0.0 */,
|
||||||
double cutPreviewGapLen /* = 0.0 */)
|
double cutPreviewGapLen, /* = 0.0 */
|
||||||
|
const double *pStartTime /* = 0 */)
|
||||||
{
|
{
|
||||||
if( IsBusy() )
|
if( IsBusy() )
|
||||||
return 0;
|
return 0;
|
||||||
@ -1369,6 +1372,20 @@ int AudioIO::StartStream(WaveTrackArray playbackTracks,
|
|||||||
AILASetStartTime();
|
AILASetStartTime();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (pStartTime)
|
||||||
|
{
|
||||||
|
// Calculate the new time position
|
||||||
|
mTime = std::max(mT0, std::min(mT1, *pStartTime));
|
||||||
|
// Reset mixer positions for all playback tracks
|
||||||
|
unsigned numMixers = mPlaybackTracks.GetCount();
|
||||||
|
for (unsigned ii = 0; ii < numMixers; ++ii)
|
||||||
|
mPlaybackMixers[ii]->Reposition(mTime);
|
||||||
|
if(mTimeTrack)
|
||||||
|
mWarpedTime = mTimeTrack->ComputeWarpedLength(mT0, mTime);
|
||||||
|
else
|
||||||
|
mWarpedTime = mTime - mT0;
|
||||||
|
}
|
||||||
|
|
||||||
// We signal the audio thread to call FillBuffers, to prime the RingBuffers
|
// We signal the audio thread to call FillBuffers, to prime the RingBuffers
|
||||||
// so that they will have data in them when the stream starts. Having the
|
// so that they will have data in them when the stream starts. Having the
|
||||||
// audio thread call FillBuffers here makes the code more predictable, since
|
// audio thread call FillBuffers here makes the code more predictable, since
|
||||||
@ -3648,6 +3665,8 @@ int audacityAudioCallback(const void *inputBuffer, void *outputBuffer,
|
|||||||
chanCnt = 0;
|
chanCnt = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gAudioIO->mLastPlaybackTimeMillis = ::wxGetLocalTimeMillis();
|
||||||
|
|
||||||
//
|
//
|
||||||
// Clip output to [-1.0,+1.0] range (msmeyer)
|
// Clip output to [-1.0,+1.0] range (msmeyer)
|
||||||
//
|
//
|
||||||
|
@ -53,16 +53,7 @@ wxString DeviceName(const PaDeviceInfo* info);
|
|||||||
wxString HostName(const PaDeviceInfo* info);
|
wxString HostName(const PaDeviceInfo* info);
|
||||||
bool ValidateDeviceNames();
|
bool ValidateDeviceNames();
|
||||||
|
|
||||||
class AUDACITY_DLL_API AudioIOListener {
|
class AudioIOListener;
|
||||||
public:
|
|
||||||
AudioIOListener() {}
|
|
||||||
virtual ~AudioIOListener() {}
|
|
||||||
|
|
||||||
virtual void OnAudioIORate(int rate) = 0;
|
|
||||||
virtual void OnAudioIOStartRecording() = 0;
|
|
||||||
virtual void OnAudioIOStopRecording() = 0;
|
|
||||||
virtual void OnAudioIONewBlockFiles(const wxString& blockFileLog) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define BAD_STREAM_TIME -1000000000.0
|
#define BAD_STREAM_TIME -1000000000.0
|
||||||
|
|
||||||
@ -113,7 +104,10 @@ class AUDACITY_DLL_API AudioIO {
|
|||||||
AudioIOListener* listener,
|
AudioIOListener* listener,
|
||||||
bool playLooped = false,
|
bool playLooped = false,
|
||||||
double cutPreviewGapStart = 0.0,
|
double cutPreviewGapStart = 0.0,
|
||||||
double cutPreviewGapLen = 0.0);
|
double cutPreviewGapLen = 0.0,
|
||||||
|
// May be other than t0,
|
||||||
|
// but will be constrained between t0 and t1
|
||||||
|
const double *pStartTime = 0);
|
||||||
|
|
||||||
/** \brief Stop recording, playback or input monitoring.
|
/** \brief Stop recording, playback or input monitoring.
|
||||||
*
|
*
|
||||||
@ -123,7 +117,7 @@ class AUDACITY_DLL_API AudioIO {
|
|||||||
void StopStream();
|
void StopStream();
|
||||||
/** \brief Move the playback / recording position of the current stream
|
/** \brief Move the playback / recording position of the current stream
|
||||||
* by the specified amount from where it is now */
|
* by the specified amount from where it is now */
|
||||||
void SeekStream(double seconds) { mSeek = seconds; };
|
void SeekStream(double seconds) { mSeek = seconds; }
|
||||||
|
|
||||||
/** \brief Returns true if audio i/o is busy starting, stopping, playing,
|
/** \brief Returns true if audio i/o is busy starting, stopping, playing,
|
||||||
* or recording.
|
* or recording.
|
||||||
@ -140,6 +134,8 @@ class AUDACITY_DLL_API AudioIO {
|
|||||||
bool IsStreamActive();
|
bool IsStreamActive();
|
||||||
bool IsStreamActive(int token);
|
bool IsStreamActive(int token);
|
||||||
|
|
||||||
|
wxLongLong GetLastPlaybackTime() const { return mLastPlaybackTimeMillis; }
|
||||||
|
|
||||||
#ifdef EXPERIMENTAL_MIDI_OUT
|
#ifdef EXPERIMENTAL_MIDI_OUT
|
||||||
/** \brief Compute the current PortMidi timestamp time.
|
/** \brief Compute the current PortMidi timestamp time.
|
||||||
*
|
*
|
||||||
@ -517,6 +513,8 @@ private:
|
|||||||
volatile bool mAudioThreadFillBuffersLoopRunning;
|
volatile bool mAudioThreadFillBuffersLoopRunning;
|
||||||
volatile bool mAudioThreadFillBuffersLoopActive;
|
volatile bool mAudioThreadFillBuffersLoopActive;
|
||||||
|
|
||||||
|
wxLongLong mLastPlaybackTimeMillis;
|
||||||
|
|
||||||
#ifdef EXPERIMENTAL_MIDI_OUT
|
#ifdef EXPERIMENTAL_MIDI_OUT
|
||||||
volatile bool mMidiThreadFillBuffersLoopRunning;
|
volatile bool mMidiThreadFillBuffersLoopRunning;
|
||||||
volatile bool mMidiThreadFillBuffersLoopActive;
|
volatile bool mMidiThreadFillBuffersLoopActive;
|
||||||
|
@ -105,6 +105,12 @@
|
|||||||
// Paul Licameli (PRL) 5 Oct 2014
|
// Paul Licameli (PRL) 5 Oct 2014
|
||||||
#define EXPERIMENTAL_SPECTRAL_EDITING
|
#define EXPERIMENTAL_SPECTRAL_EDITING
|
||||||
|
|
||||||
|
// Paul Licameli (PRL) 29 Nov 2014
|
||||||
|
#define EXPERIMENTAL_SCRUBBING
|
||||||
|
|
||||||
|
// Paul Licameli (PRL) 29 Nov 2014
|
||||||
|
#define EXPERIMENTAL_IMPROVED_SEEKING
|
||||||
|
|
||||||
// Philip Van Baren 01 July 2009
|
// Philip Van Baren 01 July 2009
|
||||||
// Replace RealFFT() and PowerSpectrum function to use (faster) RealFFTf function
|
// Replace RealFFT() and PowerSpectrum function to use (faster) RealFFTf function
|
||||||
#define EXPERIMENTAL_USE_REALFFTF
|
#define EXPERIMENTAL_USE_REALFFTF
|
||||||
|
@ -47,6 +47,7 @@ for drawing different aspects of the label and its text box.
|
|||||||
#include <wx/textfile.h>
|
#include <wx/textfile.h>
|
||||||
#include <wx/utils.h>
|
#include <wx/utils.h>
|
||||||
|
|
||||||
|
#include "AudioIO.h"
|
||||||
#include "LabelTrack.h"
|
#include "LabelTrack.h"
|
||||||
#include "DirManager.h"
|
#include "DirManager.h"
|
||||||
#include "Internat.h"
|
#include "Internat.h"
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include <wx/settings.h> // for wxSystemSettings::GetColour and wxSystemSettings::GetMetric
|
#include <wx/settings.h> // for wxSystemSettings::GetColour and wxSystemSettings::GetMetric
|
||||||
|
|
||||||
#include "AColor.h"
|
#include "AColor.h"
|
||||||
|
#include "AudioIO.h"
|
||||||
#include "MixerBoard.h"
|
#include "MixerBoard.h"
|
||||||
#ifdef EXPERIMENTAL_MIDI_OUT
|
#ifdef EXPERIMENTAL_MIDI_OUT
|
||||||
#include "NoteTrack.h"
|
#include "NoteTrack.h"
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
#include "UndoManager.h"
|
#include "UndoManager.h"
|
||||||
#include "ViewInfo.h"
|
#include "ViewInfo.h"
|
||||||
#include "TrackPanelListener.h"
|
#include "TrackPanelListener.h"
|
||||||
#include "AudioIO.h"
|
#include "AudioIOListener.h"
|
||||||
#include "commands/CommandManager.h"
|
#include "commands/CommandManager.h"
|
||||||
#include "effects/EffectManager.h"
|
#include "effects/EffectManager.h"
|
||||||
#include "xml/XMLTagHandler.h"
|
#include "xml/XMLTagHandler.h"
|
||||||
|
@ -584,6 +584,12 @@ TrackPanel::TrackPanel(wxWindow * parent, wxWindowID id,
|
|||||||
|
|
||||||
mSelStartValid = false;
|
mSelStartValid = false;
|
||||||
mSelStart = 0;
|
mSelStart = 0;
|
||||||
|
|
||||||
|
#ifdef EXPERIMENTAL_SCRUBBING
|
||||||
|
mScrubbing = false;
|
||||||
|
mLastScrubTime = 0;
|
||||||
|
mLastScrubPosition = 0;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
TrackPanel::~TrackPanel()
|
TrackPanel::~TrackPanel()
|
||||||
@ -994,6 +1000,32 @@ void TrackPanel::OnTimer()
|
|||||||
(p->mLastPlayMode == loopedPlay));
|
(p->mLastPlayMode == loopedPlay));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef EXPERIMENTAL_SCRUBBING
|
||||||
|
if (mScrubbing
|
||||||
|
&&
|
||||||
|
gAudioIO->IsStreamActive(GetProject()->GetAudioIOToken()))
|
||||||
|
{
|
||||||
|
if (gAudioIO->GetLastPlaybackTime() < mLastScrubTime) {
|
||||||
|
// Allow some audio catch up
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
wxMouseState state(::wxGetMouseState());
|
||||||
|
wxCoord xx = state.GetX();
|
||||||
|
ScreenToClient(&xx, NULL);
|
||||||
|
double leadPosition = PositionToTime(xx, GetLeftOffset());
|
||||||
|
if (mLastScrubPosition != leadPosition) {
|
||||||
|
wxLongLong clockTime = ::wxGetLocalTimeMillis();
|
||||||
|
double lagPosition = gAudioIO->GetStreamTime();
|
||||||
|
|
||||||
|
gAudioIO->SeekStream(leadPosition - lagPosition);
|
||||||
|
|
||||||
|
mLastScrubPosition = leadPosition;
|
||||||
|
mLastScrubTime = clockTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Check whether we were playing or recording, but the stream has stopped.
|
// Check whether we were playing or recording, but the stream has stopped.
|
||||||
if (p->GetAudioIOToken()>0 &&
|
if (p->GetAudioIOToken()>0 &&
|
||||||
!gAudioIO->IsStreamActive(p->GetAudioIOToken()))
|
!gAudioIO->IsStreamActive(p->GetAudioIOToken()))
|
||||||
@ -1724,8 +1756,12 @@ void TrackPanel::SetCursorAndTipWhenSelectTool( Track * t,
|
|||||||
wxString keyStr
|
wxString keyStr
|
||||||
(GetProject()->GetCommandManager()->GetKeyFromName(wxT("Preferences")));
|
(GetProject()->GetCommandManager()->GetKeyFromName(wxT("Preferences")));
|
||||||
// Must compose a string that survives the function call, hence static.
|
// Must compose a string that survives the function call, hence static.
|
||||||
|
if (keyStr.IsEmpty())
|
||||||
|
// No keyboard preference defined for opening Preferences dialog
|
||||||
|
/* i18n-hint: These are the names of a menu and a command in that menu */
|
||||||
|
keyStr = _("Edit, Preferences...");
|
||||||
static wxString result;
|
static wxString result;
|
||||||
/* i18n-hint: %s is usually replaced by "Ctrl+P" */
|
/* i18n-hint: %s is usually replaced by "Ctrl+P" for Windows/Linux, "Command+," for Mac */
|
||||||
result = wxString::Format(
|
result = wxString::Format(
|
||||||
_("Multi-Tool Mode: %s for Mouse and Keyboard Preferences."),
|
_("Multi-Tool Mode: %s for Mouse and Keyboard Preferences."),
|
||||||
keyStr.c_str());
|
keyStr.c_str());
|
||||||
@ -1977,6 +2013,52 @@ void TrackPanel::HandleSelect(wxMouseEvent & event)
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else if (event.LeftUp() || event.RightUp()) {
|
} else if (event.LeftUp() || event.RightUp()) {
|
||||||
|
#ifdef EXPERIMENTAL_SCRUBBING
|
||||||
|
if(mScrubbing) {
|
||||||
|
if (gAudioIO->IsBusy()) {
|
||||||
|
AudacityProject *p = GetActiveProject();
|
||||||
|
if (p) {
|
||||||
|
ControlToolBar * ctb = p->GetControlToolBar();
|
||||||
|
ctb->StopPlaying();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mAdjustSelectionEdges) {
|
||||||
|
if (event.ShiftDown()) {
|
||||||
|
// Adjust time selection as if shift-left click at end
|
||||||
|
const double selend = PositionToTime(event.m_x, GetLeftOffset());
|
||||||
|
SelectionBoundary boundary = ChooseTimeBoundary(selend, false);
|
||||||
|
switch (boundary)
|
||||||
|
{
|
||||||
|
case SBLeft:
|
||||||
|
mViewInfo->selectedRegion.setT0(selend);
|
||||||
|
break;
|
||||||
|
case SBRight:
|
||||||
|
mViewInfo->selectedRegion.setT1(selend);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
wxASSERT(false);
|
||||||
|
}
|
||||||
|
UpdateSelectionDisplay();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Adjust time selection as if left click
|
||||||
|
StartSelection(event.m_x, r.x);
|
||||||
|
DisplaySelection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mScrubbing = false;
|
||||||
|
}
|
||||||
|
else if (event.CmdDown()) {
|
||||||
|
// A control-click will set just the indicator to the clicked spot,
|
||||||
|
// and turn playback on -- but delayed until button up,
|
||||||
|
// and only if no intervening drag
|
||||||
|
StartOrJumpPlayback(event);
|
||||||
|
}
|
||||||
|
// Don't return yet
|
||||||
|
#endif
|
||||||
|
|
||||||
if (mSnapManager) {
|
if (mSnapManager) {
|
||||||
delete mSnapManager;
|
delete mSnapManager;
|
||||||
mSnapManager = NULL;
|
mSnapManager = NULL;
|
||||||
@ -2055,6 +2137,67 @@ void TrackPanel::HandleSelect(wxMouseEvent & event)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void TrackPanel::StartOrJumpPlayback(wxMouseEvent &event)
|
||||||
|
{
|
||||||
|
AudacityProject *p = GetActiveProject();
|
||||||
|
if (p) {
|
||||||
|
double clicktime = PositionToTime(event.m_x, GetLeftOffset());
|
||||||
|
const double t1 = mViewInfo->selectedRegion.t1();
|
||||||
|
// Play to end of selection, or if that is not right of the pick, end of track
|
||||||
|
double endtime = clicktime < t1 ? t1 : mViewInfo->total;
|
||||||
|
|
||||||
|
//Behavior should differ depending upon whether we are
|
||||||
|
//currently in playback mode or not.
|
||||||
|
|
||||||
|
bool busy = gAudioIO->IsBusy();
|
||||||
|
if (!busy)
|
||||||
|
{
|
||||||
|
//If we aren't currently playing back, start playing back at
|
||||||
|
//the clicked point
|
||||||
|
ControlToolBar * ctb = p->GetControlToolBar();
|
||||||
|
//ctb->SetPlay(true);// Not needed as done in PlayPlayRegion
|
||||||
|
ctb->PlayPlayRegion(clicktime, endtime,false) ;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//If we are playing back, stop and move playback
|
||||||
|
//to the clicked point.
|
||||||
|
//This unpauses paused audio as well. The right thing to do might be to
|
||||||
|
//leave it paused but move the point. This would probably
|
||||||
|
//require a new method in ControlToolBar: SetPause();
|
||||||
|
ControlToolBar * ctb = p->GetControlToolBar();
|
||||||
|
ctb->StopPlaying();
|
||||||
|
ctb->PlayPlayRegion(clicktime,endtime,false) ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef EXPERIMENTAL_SCRUBBING
|
||||||
|
void TrackPanel::StartScrubbing(double position)
|
||||||
|
{
|
||||||
|
AudacityProject *p = GetActiveProject();
|
||||||
|
if (p &&
|
||||||
|
// Should I make a bigger tolerance than zero?
|
||||||
|
mLastScrubPosition != position) {
|
||||||
|
ControlToolBar * ctb = p->GetControlToolBar();
|
||||||
|
bool busy = gAudioIO->IsBusy();
|
||||||
|
double maxTime = p->GetTracks()->GetEndTime();
|
||||||
|
|
||||||
|
if (busy)
|
||||||
|
ctb->StopPlaying();
|
||||||
|
|
||||||
|
ctb->PlayPlayRegion(0, maxTime, false, false,
|
||||||
|
0,
|
||||||
|
&position);
|
||||||
|
mScrubbing = true;
|
||||||
|
mLastScrubPosition = position;
|
||||||
|
mLastScrubTime = ::wxGetLocalTimeMillis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/// This method gets called when we're handling selection
|
/// This method gets called when we're handling selection
|
||||||
/// and the mouse was just clicked.
|
/// and the mouse was just clicked.
|
||||||
void TrackPanel::SelectionHandleClick(wxMouseEvent & event,
|
void TrackPanel::SelectionHandleClick(wxMouseEvent & event,
|
||||||
@ -2087,10 +2230,16 @@ void TrackPanel::SelectionHandleClick(wxMouseEvent & event,
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (event.ShiftDown()
|
if (event.ShiftDown()
|
||||||
#ifdef USE_MIDI
|
|
||||||
&& !stretch
|
#ifdef EXPERIMENTAL_SCRUBBING
|
||||||
|
// Ctrl prevails over Shift with scrubbing enabled
|
||||||
|
&& !event.CmdDown()
|
||||||
#endif
|
#endif
|
||||||
) {
|
|
||||||
|
#ifdef USE_MIDI
|
||||||
|
&& !stretch
|
||||||
|
#endif
|
||||||
|
) {
|
||||||
// If the shift button is down and no track is selected yet,
|
// If the shift button is down and no track is selected yet,
|
||||||
// at least select the track we clicked into.
|
// at least select the track we clicked into.
|
||||||
bool isAtLeastOneTrackSelected = false;
|
bool isAtLeastOneTrackSelected = false;
|
||||||
@ -2168,43 +2317,17 @@ void TrackPanel::SelectionHandleClick(wxMouseEvent & event,
|
|||||||
// and turn playback on.
|
// and turn playback on.
|
||||||
else if(event.CmdDown()
|
else if(event.CmdDown()
|
||||||
#ifdef USE_MIDI
|
#ifdef USE_MIDI
|
||||||
&& !stretch
|
&& !stretch
|
||||||
|
#endif
|
||||||
|
) {
|
||||||
|
#ifdef EXPERIMENTAL_SCRUBBING
|
||||||
|
// With scrubbing enabled, playback happens on button up, not down,
|
||||||
|
// and only if we do not start a scrub in the interim.
|
||||||
|
mScrubbing = false;
|
||||||
|
mLastScrubPosition = PositionToTime(event.m_x, GetLeftOffset());
|
||||||
|
#else
|
||||||
|
StartOrJumpPlayback(event);
|
||||||
#endif
|
#endif
|
||||||
) {
|
|
||||||
AudacityProject *p = GetActiveProject();
|
|
||||||
if (p) {
|
|
||||||
|
|
||||||
double clicktime = PositionToTime(event.m_x, GetLeftOffset());
|
|
||||||
const double t1 = mViewInfo->selectedRegion.t1();
|
|
||||||
double endtime = clicktime < t1 ? t1 : mViewInfo->total;
|
|
||||||
|
|
||||||
//Behavior should differ depending upon whether we are
|
|
||||||
//currently in playback mode or not.
|
|
||||||
|
|
||||||
bool busy = gAudioIO->IsBusy();
|
|
||||||
if(!busy)
|
|
||||||
{
|
|
||||||
//If we aren't currently playing back, start playing back at
|
|
||||||
//the clicked point
|
|
||||||
ControlToolBar * ctb = p->GetControlToolBar();
|
|
||||||
//ctb->SetPlay(true);// Not needed as done in PlayPlayRegion
|
|
||||||
ctb->PlayPlayRegion(clicktime, endtime,false) ;
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//If we are playing back, stop and move playback
|
|
||||||
//to the clicked point.
|
|
||||||
//This unpauses paused audio as well. The right thing to do might be to
|
|
||||||
//leave it paused but move the point. This would probably
|
|
||||||
//require a new method in ControlToolBar: SetPause();
|
|
||||||
ControlToolBar * ctb = p->GetControlToolBar();
|
|
||||||
ctb->StopPlaying();
|
|
||||||
ctb->PlayPlayRegion(clicktime,endtime,false) ;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//Make sure you are within the selected track
|
//Make sure you are within the selected track
|
||||||
@ -2907,9 +3030,22 @@ void TrackPanel::SelectionHandleDrag(wxMouseEvent & event, Track *clickedTrack)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Also fuhggeddaboudit if we're not dragging and not autoscrolling.
|
// Also fuhggeddaboudit if we're not dragging and not autoscrolling.
|
||||||
if ((!event.Dragging() && !mAutoScrolling) || event.CmdDown())
|
if (!event.Dragging() && !mAutoScrolling)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (event.CmdDown()) {
|
||||||
|
#ifdef EXPERIMENTAL_SCRUBBING
|
||||||
|
if (!mScrubbing) {
|
||||||
|
double position = PositionToTime(event.m_x, GetLeftOffset());
|
||||||
|
StartScrubbing(position);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
#else
|
||||||
|
// Ctrl-drag has no meaning, fuhggeddaboudit
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
wxRect r = mCapturedRect;
|
wxRect r = mCapturedRect;
|
||||||
Track *pTrack = mCapturedTrack;
|
Track *pTrack = mCapturedTrack;
|
||||||
|
|
||||||
@ -3091,7 +3227,8 @@ wxInt64 TrackPanel::FrequencyToPosition(double frequency,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void SetIfNotNull( double * pValue, const double Value )
|
template<typename T>
|
||||||
|
inline void SetIfNotNull( T * pValue, const T Value )
|
||||||
{
|
{
|
||||||
if( pValue == NULL )
|
if( pValue == NULL )
|
||||||
return;
|
return;
|
||||||
@ -3099,6 +3236,43 @@ void SetIfNotNull( double * pValue, const double Value )
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TrackPanel::SelectionBoundary TrackPanel::ChooseTimeBoundary
|
||||||
|
(double selend, bool onlyWithinSnapDistance,
|
||||||
|
wxInt64 *pPixelDist, double *pPinValue) const
|
||||||
|
{
|
||||||
|
const double t0 = mViewInfo->selectedRegion.t0();
|
||||||
|
const double t1 = mViewInfo->selectedRegion.t1();
|
||||||
|
wxInt64 pixelDist = mViewInfo->zoom * fabs(selend - t0);
|
||||||
|
bool chooseLeft = true;
|
||||||
|
|
||||||
|
if (mViewInfo->selectedRegion.isPoint())
|
||||||
|
// Special case when selection is a point, and thus left
|
||||||
|
// and right distances are the same
|
||||||
|
chooseLeft = (selend < t0);
|
||||||
|
else {
|
||||||
|
const wxInt64 rightDist = mViewInfo->zoom * fabs(selend - t1);
|
||||||
|
if (rightDist < pixelDist)
|
||||||
|
chooseLeft = false, pixelDist = rightDist;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetIfNotNull(pPixelDist, pixelDist);
|
||||||
|
|
||||||
|
if (onlyWithinSnapDistance &&
|
||||||
|
pixelDist >= SELECTION_RESIZE_REGION) {
|
||||||
|
SetIfNotNull( pPinValue, -1.0);
|
||||||
|
return SBNone;
|
||||||
|
}
|
||||||
|
else if (chooseLeft) {
|
||||||
|
SetIfNotNull( pPinValue, t1);
|
||||||
|
return SBLeft;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
SetIfNotNull( pPinValue, t0);
|
||||||
|
return SBRight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
TrackPanel::SelectionBoundary TrackPanel::ChooseBoundary
|
TrackPanel::SelectionBoundary TrackPanel::ChooseBoundary
|
||||||
(wxMouseEvent & event, const Track *pTrack, const wxRect &rect,
|
(wxMouseEvent & event, const Track *pTrack, const wxRect &rect,
|
||||||
bool mayDragWidth, bool onlyWithinSnapDistance,
|
bool mayDragWidth, bool onlyWithinSnapDistance,
|
||||||
@ -3110,28 +3284,19 @@ bool mayDragWidth, bool onlyWithinSnapDistance,
|
|||||||
// May choose no boundary if onlyWithinSnapDistance is true.
|
// May choose no boundary if onlyWithinSnapDistance is true.
|
||||||
// Otherwise choose the eligible boundary nearest the mouse click.
|
// Otherwise choose the eligible boundary nearest the mouse click.
|
||||||
const double selend = PositionToTime(event.m_x, rect.x);
|
const double selend = PositionToTime(event.m_x, rect.x);
|
||||||
|
wxInt64 pixelDist = 0;
|
||||||
|
SelectionBoundary boundary =
|
||||||
|
ChooseTimeBoundary(selend, onlyWithinSnapDistance,
|
||||||
|
&pixelDist, pPinValue);
|
||||||
|
|
||||||
|
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
|
||||||
const double t0 = mViewInfo->selectedRegion.t0();
|
const double t0 = mViewInfo->selectedRegion.t0();
|
||||||
const double t1 = mViewInfo->selectedRegion.t1();
|
const double t1 = mViewInfo->selectedRegion.t1();
|
||||||
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
|
|
||||||
const double f0 = mViewInfo->selectedRegion.f0();
|
const double f0 = mViewInfo->selectedRegion.f0();
|
||||||
const double f1 = mViewInfo->selectedRegion.f1();
|
const double f1 = mViewInfo->selectedRegion.f1();
|
||||||
const double fc = mViewInfo->selectedRegion.fc();
|
const double fc = mViewInfo->selectedRegion.fc();
|
||||||
double ratio = 0;
|
double ratio = 0;
|
||||||
#endif
|
|
||||||
wxInt64 pixelDist = mViewInfo->zoom * fabs(selend - t0);
|
|
||||||
bool chooseLeft = true;
|
|
||||||
|
|
||||||
if (mViewInfo->selectedRegion.isPoint())
|
|
||||||
// Special case when selection is a point, and thus left
|
|
||||||
// and right distances are the same
|
|
||||||
chooseLeft = (selend < t0);
|
|
||||||
else {
|
|
||||||
const wxInt64 rightDist = mViewInfo->zoom * fabs(selend - t1);
|
|
||||||
if (rightDist < pixelDist)
|
|
||||||
chooseLeft = false, pixelDist = rightDist;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
|
|
||||||
bool chooseTime = true;
|
bool chooseTime = true;
|
||||||
bool chooseBottom = true;
|
bool chooseBottom = true;
|
||||||
bool chooseCenter = false;
|
bool chooseCenter = false;
|
||||||
@ -3146,14 +3311,20 @@ bool mayDragWidth, bool onlyWithinSnapDistance,
|
|||||||
? FrequencyToPosition(f0, rect.y, rect.height,
|
? FrequencyToPosition(f0, rect.y, rect.height,
|
||||||
wt->GetRate(), logF)
|
wt->GetRate(), logF)
|
||||||
: rect.y + rect.height;
|
: rect.y + rect.height;
|
||||||
wxInt64 verticalDist = abs(int(event.m_y - bottomSel));
|
|
||||||
const wxInt64 topSel = (f1 >= 0)
|
const wxInt64 topSel = (f1 >= 0)
|
||||||
? FrequencyToPosition(f1, rect.y, rect.height,
|
? FrequencyToPosition(f1, rect.y, rect.height,
|
||||||
wt->GetRate(), logF)
|
wt->GetRate(), logF)
|
||||||
: rect.y;
|
: rect.y;
|
||||||
const wxInt64 topDist = abs(int(event.m_y - topSel));
|
wxInt64 signedBottomDist = int(event.m_y - bottomSel);
|
||||||
if (topDist < verticalDist)
|
wxInt64 verticalDist = abs(signedBottomDist);
|
||||||
chooseBottom = false, verticalDist = topDist;
|
if (bottomSel == topSel)
|
||||||
|
// Top and bottom are too close to resolve on screen
|
||||||
|
chooseBottom = (signedBottomDist >= 0);
|
||||||
|
else {
|
||||||
|
const wxInt64 topDist = abs(int(event.m_y - topSel));
|
||||||
|
if (topDist < verticalDist)
|
||||||
|
chooseBottom = false, verticalDist = topDist;
|
||||||
|
}
|
||||||
if (fc > 0
|
if (fc > 0
|
||||||
#ifdef SPECTRAL_EDITING_ESC_KEY
|
#ifdef SPECTRAL_EDITING_ESC_KEY
|
||||||
&& mayDragWidth
|
&& mayDragWidth
|
||||||
@ -3202,19 +3373,7 @@ bool mayDragWidth, bool onlyWithinSnapDistance,
|
|||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
if (onlyWithinSnapDistance &&
|
return boundary;
|
||||||
pixelDist >= SELECTION_RESIZE_REGION) {
|
|
||||||
SetIfNotNull( pPinValue, -1.0);
|
|
||||||
return SBNone;
|
|
||||||
}
|
|
||||||
else if (chooseLeft) {
|
|
||||||
SetIfNotNull( pPinValue, t1);
|
|
||||||
return SBLeft;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
SetIfNotNull( pPinValue, t0);
|
|
||||||
return SBRight;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5805,8 +5964,8 @@ void TrackPanel::OnKeyDown(wxKeyEvent & event)
|
|||||||
else {
|
else {
|
||||||
// Handle ESC during frequency drag
|
// Handle ESC during frequency drag
|
||||||
wxMouseState state(::wxGetMouseState());
|
wxMouseState state(::wxGetMouseState());
|
||||||
wxCoord xx = state.GetX(), yy = state.GetY();
|
wxCoord yy = state.GetY();
|
||||||
ScreenToClient(&xx, &yy);
|
ScreenToClient(NULL, &yy);
|
||||||
wxRect r;
|
wxRect r;
|
||||||
if (wt == FindTrack(state.GetX(), yy, false, false, &r)) {
|
if (wt == FindTrack(state.GetX(), yy, false, false, &r)) {
|
||||||
eFreqSelMode saveMode = mFreqSelMode;
|
eFreqSelMode saveMode = mFreqSelMode;
|
||||||
@ -7333,113 +7492,42 @@ void TrackPanel::ScrollIntoView(int x)
|
|||||||
|
|
||||||
void TrackPanel::OnCursorLeft( bool shift, bool ctrl, bool keyup )
|
void TrackPanel::OnCursorLeft( bool shift, bool ctrl, bool keyup )
|
||||||
{
|
{
|
||||||
if( keyup )
|
// PRL: What I found and preserved, strange though it be:
|
||||||
{
|
// During playback: jump depends on preferences and is independent of the zoom
|
||||||
int token = GetProject()->GetAudioIOToken();
|
// and does not vary if the key is held
|
||||||
if( token > 0 && gAudioIO->IsStreamActive( token ) )
|
// Else: jump depends on the zoom and gets bigger if the key is held
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
MakeParentModifyState(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the last adjustment was very recent, we are
|
|
||||||
// holding the key down and should move faster.
|
|
||||||
wxLongLong curtime = ::wxGetLocalTimeMillis();
|
|
||||||
int multiplier = 1;
|
|
||||||
if( curtime - mLastSelectionAdjustment < 50 )
|
|
||||||
{
|
|
||||||
multiplier = 4;
|
|
||||||
}
|
|
||||||
mLastSelectionAdjustment = curtime;
|
|
||||||
|
|
||||||
int snapToTime = GetActiveProject()->GetSnapTo();
|
int snapToTime = GetActiveProject()->GetSnapTo();
|
||||||
|
double quietSeekStepPositive = 1.0 / mViewInfo->zoom;
|
||||||
// Contract selection from the right to the left
|
double audioSeekStepPositive = shift ? mSeekLong : mSeekShort;
|
||||||
if( shift && ctrl )
|
SeekLeftOrRight
|
||||||
{
|
(true, shift, ctrl, keyup, snapToTime, true, false,
|
||||||
// Reduce and constrain (counter-intuitive)
|
quietSeekStepPositive, audioSeekStepPositive);
|
||||||
mViewInfo->selectedRegion.setT1(
|
|
||||||
std::max(mViewInfo->selectedRegion.t0(),
|
|
||||||
snapToTime
|
|
||||||
? GridMove(mViewInfo->selectedRegion.t1(), -multiplier)
|
|
||||||
: mViewInfo->selectedRegion.t1() -
|
|
||||||
multiplier / mViewInfo->zoom));
|
|
||||||
|
|
||||||
// Make sure it's visible.
|
|
||||||
ScrollIntoView( mViewInfo->selectedRegion.t1() );
|
|
||||||
Refresh( false );
|
|
||||||
}
|
|
||||||
// Extend selection toward the left
|
|
||||||
else if( shift )
|
|
||||||
{
|
|
||||||
// If playing, reposition a long amount of time
|
|
||||||
int token = GetProject()->GetAudioIOToken();
|
|
||||||
if( token > 0 && gAudioIO->IsStreamActive( token ) )
|
|
||||||
{
|
|
||||||
gAudioIO->SeekStream(-mSeekLong);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expand and constrain
|
|
||||||
mViewInfo->selectedRegion.setT0(
|
|
||||||
std::max(0.0,
|
|
||||||
snapToTime
|
|
||||||
? GridMove(mViewInfo->selectedRegion.t0(), -multiplier)
|
|
||||||
: mViewInfo->selectedRegion.t0() -
|
|
||||||
multiplier / mViewInfo->zoom));
|
|
||||||
|
|
||||||
// Make sure it's visible.
|
|
||||||
ScrollIntoView( mViewInfo->selectedRegion.t0() );
|
|
||||||
Refresh( false );
|
|
||||||
}
|
|
||||||
// Move the cursor toward the left
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// If playing, reposition a short amount of time
|
|
||||||
int token = GetProject()->GetAudioIOToken();
|
|
||||||
if( token > 0 && gAudioIO->IsStreamActive( token ) )
|
|
||||||
{
|
|
||||||
gAudioIO->SeekStream(-mSeekShort);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Already in cursor mode?
|
|
||||||
if( mViewInfo->selectedRegion.isPoint() )
|
|
||||||
{
|
|
||||||
// Move and constrain
|
|
||||||
mViewInfo->selectedRegion.setT0(
|
|
||||||
std::max(0.0,
|
|
||||||
snapToTime
|
|
||||||
? GridMove(mViewInfo->selectedRegion.t0(), -multiplier)
|
|
||||||
: mViewInfo->selectedRegion.t0() -
|
|
||||||
multiplier / mViewInfo->zoom),
|
|
||||||
false);
|
|
||||||
mViewInfo->selectedRegion.collapseToT0();
|
|
||||||
|
|
||||||
// Move the visual cursor
|
|
||||||
DrawCursor();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Transition to cursor mode.
|
|
||||||
mViewInfo->selectedRegion.collapseToT0();
|
|
||||||
Refresh( false );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure it's visible
|
|
||||||
ScrollIntoView( mViewInfo->selectedRegion.t0() );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TrackPanel::OnCursorRight( bool shift, bool ctrl, bool keyup )
|
void TrackPanel::OnCursorRight(bool shift, bool ctrl, bool keyup)
|
||||||
{
|
{
|
||||||
if( keyup )
|
// PRL: What I found and preserved, strange though it be:
|
||||||
|
// During playback: jump depends on preferences and is independent of the zoom
|
||||||
|
// and does not vary if the key is held
|
||||||
|
// Else: jump depends on the zoom and gets bigger if the key is held
|
||||||
|
int snapToTime = GetActiveProject()->GetSnapTo();
|
||||||
|
double quietSeekStepPositive = 1.0 / mViewInfo->zoom;
|
||||||
|
double audioSeekStepPositive = shift ? mSeekLong : mSeekShort;
|
||||||
|
SeekLeftOrRight
|
||||||
|
(false, shift, ctrl, keyup, snapToTime, true, false,
|
||||||
|
quietSeekStepPositive, audioSeekStepPositive);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle small cursor and play head movements
|
||||||
|
void TrackPanel::SeekLeftOrRight
|
||||||
|
(bool leftward, bool shift, bool ctrl, bool keyup,
|
||||||
|
int snapToTime, bool mayAccelerateQuiet, bool mayAccelerateAudio,
|
||||||
|
double quietSeekStepPositive, double audioSeekStepPositive)
|
||||||
|
{
|
||||||
|
if (keyup)
|
||||||
{
|
{
|
||||||
int token = GetProject()->GetAudioIOToken();
|
int token = GetProject()->GetAudioIOToken();
|
||||||
if( token > 0 && gAudioIO->IsStreamActive( token ) )
|
if (token > 0 && gAudioIO->IsStreamActive(token))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -7450,78 +7538,119 @@ void TrackPanel::OnCursorRight( bool shift, bool ctrl, bool keyup )
|
|||||||
|
|
||||||
// If the last adjustment was very recent, we are
|
// If the last adjustment was very recent, we are
|
||||||
// holding the key down and should move faster.
|
// holding the key down and should move faster.
|
||||||
wxLongLong curtime = ::wxGetLocalTimeMillis();
|
const wxLongLong curtime = ::wxGetLocalTimeMillis();
|
||||||
int multiplier = 1;
|
enum { MIN_INTERVAL = 50 };
|
||||||
if( curtime - mLastSelectionAdjustment < 50 )
|
const bool fast = (curtime - mLastSelectionAdjustment < MIN_INTERVAL);
|
||||||
{
|
|
||||||
multiplier = 4;
|
// How much faster should the cursor move if shift is down?
|
||||||
}
|
enum { LARGER_MULTIPLIER = 4 };
|
||||||
mLastSelectionAdjustment = curtime;
|
int multiplier = (fast && mayAccelerateQuiet) ? LARGER_MULTIPLIER : 1;
|
||||||
|
if (leftward)
|
||||||
|
multiplier = -multiplier;
|
||||||
|
|
||||||
int snapToTime = GetActiveProject()->GetSnapTo();
|
int token = GetProject()->GetAudioIOToken();
|
||||||
|
|
||||||
// Contract selection from the left to the right
|
if (shift && ctrl)
|
||||||
if( shift && ctrl )
|
|
||||||
{
|
{
|
||||||
|
mLastSelectionAdjustment = curtime;
|
||||||
|
|
||||||
|
// Contract selection
|
||||||
// Reduce and constrain (counter-intuitive)
|
// Reduce and constrain (counter-intuitive)
|
||||||
mViewInfo->selectedRegion.setT0(
|
if (leftward) {
|
||||||
std::min(mViewInfo->selectedRegion.t1(),
|
mViewInfo->selectedRegion.setT1(
|
||||||
snapToTime
|
std::max(mViewInfo->selectedRegion.t0(),
|
||||||
|
snapToTime
|
||||||
|
? GridMove(mViewInfo->selectedRegion.t1(), multiplier)
|
||||||
|
: mViewInfo->selectedRegion.t1() +
|
||||||
|
multiplier * quietSeekStepPositive));
|
||||||
|
|
||||||
|
// Make sure it's visible.
|
||||||
|
ScrollIntoView(mViewInfo->selectedRegion.t1());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mViewInfo->selectedRegion.setT0(
|
||||||
|
std::min(mViewInfo->selectedRegion.t1(),
|
||||||
|
snapToTime
|
||||||
? GridMove(mViewInfo->selectedRegion.t0(), multiplier)
|
? GridMove(mViewInfo->selectedRegion.t0(), multiplier)
|
||||||
: mViewInfo->selectedRegion.t0() +
|
: mViewInfo->selectedRegion.t0() +
|
||||||
multiplier / mViewInfo->zoom));
|
multiplier * quietSeekStepPositive));
|
||||||
|
|
||||||
// Make sure new position is in view.
|
// Make sure new position is in view.
|
||||||
ScrollIntoView( mViewInfo->selectedRegion.t0() );
|
ScrollIntoView(mViewInfo->selectedRegion.t0());
|
||||||
Refresh( false );
|
}
|
||||||
|
Refresh(false);
|
||||||
}
|
}
|
||||||
// Extend selection toward the right
|
else if (token > 0 && gAudioIO->IsStreamActive(token)) {
|
||||||
else if( shift )
|
#ifdef EXPERIMENTAL_IMPROVED_SEEKING
|
||||||
{
|
if (gAudioIO->GetLastPlaybackTime() < mLastSelectionAdjustment) {
|
||||||
// If playing, reposition a long amount of time
|
// Allow time for the last seek to output a buffer before
|
||||||
int token = GetProject()->GetAudioIOToken();
|
// discarding samples again
|
||||||
if( token > 0 && gAudioIO->IsStreamActive( token ) )
|
// Do not advance mLastSelectionAdjustment
|
||||||
{
|
|
||||||
gAudioIO->SeekStream(mSeekLong);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
mLastSelectionAdjustment = curtime;
|
||||||
|
|
||||||
// Expand and constrain
|
// Ignore the multiplier for the quiet case
|
||||||
double end = mTracks->GetEndTime();
|
multiplier = (fast && mayAccelerateAudio) ? LARGER_MULTIPLIER : 1;
|
||||||
mViewInfo->selectedRegion.setT1(
|
if (leftward)
|
||||||
std::min(end,
|
multiplier = -multiplier;
|
||||||
snapToTime
|
|
||||||
? GridMove(mViewInfo->selectedRegion.t1(), multiplier)
|
|
||||||
: mViewInfo->selectedRegion.t1() + multiplier/mViewInfo->zoom));
|
|
||||||
|
|
||||||
// Make sure new position is in view.
|
// If playing, reposition
|
||||||
ScrollIntoView( mViewInfo->selectedRegion.t1() );
|
gAudioIO->SeekStream(multiplier * audioSeekStepPositive);
|
||||||
Refresh( false );
|
return;
|
||||||
|
}
|
||||||
|
else if (shift)
|
||||||
|
{
|
||||||
|
mLastSelectionAdjustment = curtime;
|
||||||
|
|
||||||
|
// Extend selection
|
||||||
|
// Expand and constrain
|
||||||
|
if (leftward) {
|
||||||
|
mViewInfo->selectedRegion.setT0(
|
||||||
|
std::max(0.0,
|
||||||
|
snapToTime
|
||||||
|
? GridMove(mViewInfo->selectedRegion.t0(), multiplier)
|
||||||
|
: mViewInfo->selectedRegion.t0() +
|
||||||
|
multiplier * quietSeekStepPositive));
|
||||||
|
|
||||||
|
// Make sure it's visible.
|
||||||
|
ScrollIntoView(mViewInfo->selectedRegion.t0());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
double end = mTracks->GetEndTime();
|
||||||
|
mViewInfo->selectedRegion.setT1(
|
||||||
|
std::min(end,
|
||||||
|
snapToTime
|
||||||
|
? GridMove(mViewInfo->selectedRegion.t1(), multiplier)
|
||||||
|
: mViewInfo->selectedRegion.t1() +
|
||||||
|
multiplier * quietSeekStepPositive));
|
||||||
|
|
||||||
|
// Make sure new position is in view.
|
||||||
|
ScrollIntoView(mViewInfo->selectedRegion.t1());
|
||||||
|
}
|
||||||
|
Refresh(false);
|
||||||
}
|
}
|
||||||
// Move the cursor toward the right
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// If playing, reposition a short amount of time
|
mLastSelectionAdjustment = curtime;
|
||||||
int token = GetProject()->GetAudioIOToken();
|
|
||||||
if( token > 0 && gAudioIO->IsStreamActive( token ) )
|
|
||||||
{
|
|
||||||
gAudioIO->SeekStream(mSeekShort);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Move the cursor
|
||||||
// Already in cursor mode?
|
// Already in cursor mode?
|
||||||
if (mViewInfo->selectedRegion.isPoint())
|
if (mViewInfo->selectedRegion.isPoint())
|
||||||
{
|
{
|
||||||
// Move and constrain
|
// Move and constrain
|
||||||
double end = mTracks->GetEndTime();
|
double end = mTracks->GetEndTime();
|
||||||
mViewInfo->selectedRegion.setT1(
|
mViewInfo->selectedRegion.setT0(
|
||||||
std::min(end,
|
std::max(0.0,
|
||||||
snapToTime
|
std::min(end,
|
||||||
? GridMove(mViewInfo->selectedRegion.t1(), multiplier)
|
snapToTime
|
||||||
: mViewInfo->selectedRegion.t1() +
|
? GridMove(mViewInfo->selectedRegion.t0(), multiplier)
|
||||||
multiplier / mViewInfo->zoom),
|
: mViewInfo->selectedRegion.t0() + multiplier * quietSeekStepPositive
|
||||||
|
)
|
||||||
|
),
|
||||||
false);
|
false);
|
||||||
mViewInfo->selectedRegion.collapseToT1();
|
mViewInfo->selectedRegion.collapseToT0();
|
||||||
|
|
||||||
// Move the visual cursor
|
// Move the visual cursor
|
||||||
DrawCursor();
|
DrawCursor();
|
||||||
@ -7529,12 +7658,15 @@ void TrackPanel::OnCursorRight( bool shift, bool ctrl, bool keyup )
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Transition to cursor mode.
|
// Transition to cursor mode.
|
||||||
mViewInfo->selectedRegion.collapseToT1();
|
if (leftward)
|
||||||
Refresh( false );
|
mViewInfo->selectedRegion.collapseToT0();
|
||||||
|
else
|
||||||
|
mViewInfo->selectedRegion.collapseToT1();
|
||||||
|
Refresh(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure new position is in view
|
// Make sure new position is in view
|
||||||
ScrollIntoView( mViewInfo->selectedRegion.t1() );
|
ScrollIntoView(mViewInfo->selectedRegion.t1());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -7656,74 +7788,25 @@ void TrackPanel::OnBoundaryMove(bool left, bool boundaryContract)
|
|||||||
// longjump=false: Use mSeekShort; longjump=true: Use mSeekLong
|
// longjump=false: Use mSeekShort; longjump=true: Use mSeekLong
|
||||||
void TrackPanel::OnCursorMove(bool forward, bool jump, bool longjump )
|
void TrackPanel::OnCursorMove(bool forward, bool jump, bool longjump )
|
||||||
{
|
{
|
||||||
// If the last adjustment was very recent, we are
|
// PRL: nobody calls this yet with !jump
|
||||||
// holding the key down and should move faster.
|
|
||||||
wxLongLong curtime = ::wxGetLocalTimeMillis();
|
|
||||||
int multiplier = 1;
|
|
||||||
if( curtime - mLastSelectionAdjustment < 50 )
|
|
||||||
{
|
|
||||||
multiplier = 4;
|
|
||||||
}
|
|
||||||
mLastSelectionAdjustment = curtime;
|
|
||||||
|
|
||||||
float direction = -1;
|
double positiveSeekStep;
|
||||||
if (forward) {
|
|
||||||
direction = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
float mSeek;
|
|
||||||
if (jump) {
|
if (jump) {
|
||||||
if (!longjump) {
|
if (!longjump) {
|
||||||
mSeek = mSeekShort;
|
positiveSeekStep = mSeekShort;
|
||||||
} else {
|
} else {
|
||||||
mSeek = mSeekLong;
|
positiveSeekStep = mSeekLong;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
mSeek = multiplier / mViewInfo->zoom;
|
positiveSeekStep = 1.0 / mViewInfo->zoom;
|
||||||
}
|
}
|
||||||
mSeek *= direction;
|
bool mayAccelerate = !jump;
|
||||||
|
SeekLeftOrRight
|
||||||
|
(!forward, false, false, false,
|
||||||
|
0, mayAccelerate, mayAccelerate,
|
||||||
|
positiveSeekStep, positiveSeekStep);
|
||||||
|
|
||||||
// If playing, reposition a short amount of time
|
MakeParentModifyState(false);
|
||||||
int token = GetProject()->GetAudioIOToken();
|
|
||||||
if( token > 0 && gAudioIO->IsStreamActive( token ) )
|
|
||||||
{
|
|
||||||
gAudioIO->SeekStream(mSeek);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Already in cursor mode?
|
|
||||||
if( mViewInfo->selectedRegion.isPoint() )
|
|
||||||
{
|
|
||||||
// Move and constrain
|
|
||||||
|
|
||||||
double t0 = mViewInfo->selectedRegion.t0() + mSeek;
|
|
||||||
if( !forward && t0 < 0.0 )
|
|
||||||
{
|
|
||||||
t0 = 0.0;
|
|
||||||
}
|
|
||||||
double end = mTracks->GetEndTime();
|
|
||||||
if( forward && t0 > end)
|
|
||||||
{
|
|
||||||
t0 = end;
|
|
||||||
}
|
|
||||||
mViewInfo->selectedRegion.setT0(t0, false);
|
|
||||||
mViewInfo->selectedRegion.collapseToT0();
|
|
||||||
|
|
||||||
// Move the visual cursor
|
|
||||||
DrawCursor();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Transition to cursor mode.
|
|
||||||
mViewInfo->selectedRegion.collapseToT0();
|
|
||||||
Refresh( false );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure it's visible
|
|
||||||
ScrollIntoView( mViewInfo->selectedRegion.t0() );
|
|
||||||
|
|
||||||
MakeParentModifyState(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//The following methods operate controls on specified tracks,
|
//The following methods operate controls on specified tracks,
|
||||||
|
@ -310,6 +310,10 @@ class AUDACITY_DLL_API TrackPanel:public wxPanel {
|
|||||||
// AS: Selection handling
|
// AS: Selection handling
|
||||||
virtual void HandleSelect(wxMouseEvent & event);
|
virtual void HandleSelect(wxMouseEvent & event);
|
||||||
virtual void SelectionHandleDrag(wxMouseEvent &event, Track *pTrack);
|
virtual void SelectionHandleDrag(wxMouseEvent &event, Track *pTrack);
|
||||||
|
void StartOrJumpPlayback(wxMouseEvent &event);
|
||||||
|
#ifdef EXPERIMENTAL_SCRUBBING
|
||||||
|
void StartScrubbing(double position);
|
||||||
|
#endif
|
||||||
virtual void SelectionHandleClick(wxMouseEvent &event,
|
virtual void SelectionHandleClick(wxMouseEvent &event,
|
||||||
Track* pTrack, wxRect r);
|
Track* pTrack, wxRect r);
|
||||||
virtual void StartSelection (int mouseXCoordinate, int trackLeftEdge);
|
virtual void StartSelection (int mouseXCoordinate, int trackLeftEdge);
|
||||||
@ -317,6 +321,12 @@ class AUDACITY_DLL_API TrackPanel:public wxPanel {
|
|||||||
Track *pTrack);
|
Track *pTrack);
|
||||||
virtual void UpdateSelectionDisplay();
|
virtual void UpdateSelectionDisplay();
|
||||||
|
|
||||||
|
// Handle small cursor and play head movements
|
||||||
|
void SeekLeftOrRight
|
||||||
|
(bool left, bool shift, bool ctrl, bool keyup,
|
||||||
|
int snapToTime, bool mayAccelerateQuiet, bool mayAccelerateAudio,
|
||||||
|
double quietSeekStepPositive, double audioSeekStepPositive);
|
||||||
|
|
||||||
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
|
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
|
||||||
void StartSnappingFreqSelection (WaveTrack *pTrack);
|
void StartSnappingFreqSelection (WaveTrack *pTrack);
|
||||||
void MoveSnappingFreqSelection (int mouseYCoordinate,
|
void MoveSnappingFreqSelection (int mouseYCoordinate,
|
||||||
@ -688,6 +698,9 @@ protected:
|
|||||||
SBBottom, SBTop, SBCenter, SBWidth,
|
SBBottom, SBTop, SBCenter, SBWidth,
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
SelectionBoundary ChooseTimeBoundary
|
||||||
|
(double selend, bool onlyWithinSnapDistance,
|
||||||
|
wxInt64 *pPixelDist = NULL, double *pPinValue = NULL) const;
|
||||||
SelectionBoundary ChooseBoundary
|
SelectionBoundary ChooseBoundary
|
||||||
(wxMouseEvent & event, const Track *pTrack,
|
(wxMouseEvent & event, const Track *pTrack,
|
||||||
const wxRect &rect,
|
const wxRect &rect,
|
||||||
@ -739,6 +752,12 @@ protected:
|
|||||||
int mMoveUpThreshold;
|
int mMoveUpThreshold;
|
||||||
int mMoveDownThreshold;
|
int mMoveDownThreshold;
|
||||||
|
|
||||||
|
#ifdef EXPERIMENTAL_SCRUBBING
|
||||||
|
bool mScrubbing;
|
||||||
|
wxLongLong mLastScrubTime; // milliseconds
|
||||||
|
double mLastScrubPosition;
|
||||||
|
#endif
|
||||||
|
|
||||||
wxCursor *mArrowCursor;
|
wxCursor *mArrowCursor;
|
||||||
wxCursor *mPencilCursor;
|
wxCursor *mPencilCursor;
|
||||||
wxCursor *mSelectCursor;
|
wxCursor *mSelectCursor;
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
#include "../widgets/treebook.h"
|
#include "../widgets/treebook.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "../AudioIO.h"
|
||||||
#include "../Experimental.h"
|
#include "../Experimental.h"
|
||||||
#include "../Project.h"
|
#include "../Project.h"
|
||||||
#include "../Prefs.h"
|
#include "../Prefs.h"
|
||||||
|
@ -32,6 +32,8 @@
|
|||||||
|
|
||||||
*//*******************************************************************/
|
*//*******************************************************************/
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include "../Audacity.h"
|
#include "../Audacity.h"
|
||||||
#include "../Experimental.h"
|
#include "../Experimental.h"
|
||||||
|
|
||||||
@ -438,7 +440,8 @@ bool ControlToolBar::IsRecordDown()
|
|||||||
void ControlToolBar::PlayPlayRegion(double t0, double t1,
|
void ControlToolBar::PlayPlayRegion(double t0, double t1,
|
||||||
bool looped /* = false */,
|
bool looped /* = false */,
|
||||||
bool cutpreview /* = false */,
|
bool cutpreview /* = false */,
|
||||||
TimeTrack *timetrack /* = NULL */)
|
TimeTrack *timetrack /* = NULL */,
|
||||||
|
const double *pStartTime /* = NULL */)
|
||||||
{
|
{
|
||||||
SetPlay(true, looped, cutpreview);
|
SetPlay(true, looped, cutpreview);
|
||||||
|
|
||||||
@ -564,7 +567,8 @@ void ControlToolBar::PlayPlayRegion(double t0, double t1,
|
|||||||
NoteTrackArray(),
|
NoteTrackArray(),
|
||||||
#endif
|
#endif
|
||||||
timetrack, p->GetRate(), tcp0, tcp1, p, false,
|
timetrack, p->GetRate(), tcp0, tcp1, p, false,
|
||||||
t0, t1-t0);
|
t0, t1-t0,
|
||||||
|
pStartTime);
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
// Cannot create cut preview tracks, clean up and exit
|
// Cannot create cut preview tracks, clean up and exit
|
||||||
@ -583,7 +587,9 @@ void ControlToolBar::PlayPlayRegion(double t0, double t1,
|
|||||||
t->GetNoteTrackArray(false),
|
t->GetNoteTrackArray(false),
|
||||||
#endif
|
#endif
|
||||||
timetrack,
|
timetrack,
|
||||||
p->GetRate(), t0, t1, p, looped);
|
p->GetRate(), t0, t1, p, looped,
|
||||||
|
0, 0,
|
||||||
|
pStartTime);
|
||||||
}
|
}
|
||||||
if (token != 0) {
|
if (token != 0) {
|
||||||
success = true;
|
success = true;
|
||||||
|
@ -67,7 +67,10 @@ class ControlToolBar:public ToolBar {
|
|||||||
void PlayPlayRegion(double t0, double t1,
|
void PlayPlayRegion(double t0, double t1,
|
||||||
bool looped = false,
|
bool looped = false,
|
||||||
bool cutpreview = false,
|
bool cutpreview = false,
|
||||||
TimeTrack *timetrack = NULL);
|
TimeTrack *timetrack = NULL,
|
||||||
|
// May be other than t0,
|
||||||
|
// but will be constrained between t0 and t1
|
||||||
|
const double *pStartTime = NULL);
|
||||||
void PlayDefault();
|
void PlayDefault();
|
||||||
|
|
||||||
// Stop playing
|
// Stop playing
|
||||||
|
@ -60,6 +60,7 @@
|
|||||||
|
|
||||||
#include "../AColor.h"
|
#include "../AColor.h"
|
||||||
#include "../AllThemeResources.h"
|
#include "../AllThemeResources.h"
|
||||||
|
#include "../AudioIO.h"
|
||||||
#include "../ImageManipulation.h"
|
#include "../ImageManipulation.h"
|
||||||
#include "../Prefs.h"
|
#include "../Prefs.h"
|
||||||
#include "../Project.h"
|
#include "../Project.h"
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user