1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-05-01 16:19:43 +02:00

Merge branch 'master' into scrubbing2

This commit is contained in:
Paul Licameli 2016-05-15 16:22:39 -04:00
commit df0303cdaf
15 changed files with 240 additions and 79 deletions

105
.gitignore vendored
View File

@ -1,3 +1,73 @@
# Autotools generated files
locale/Makefile.in
Makefile
aclocal.m4
autom4te.cache
config.status
bin-stamp
.deps
.dirstamp
lib-stamp
libtool
stamp-h*
stamp-po
*.stamp
# Generated files by build system
lib-src/expat/expat_config.h
lib-src/libflac/doc/Doxyfile
lib-src/libflac/config.h
lib-src/libflac/test/common.sh
lib-src/libid3tag/config.h
lib-src/libid3tag/libid3tag.list
lib-src/libmad/config.h
lib-src/libmad/libmad.list
lib-src/libmad/mad.h
lib-src/libogg/config.h
lib-src/libogg/include/ogg/config_types.h
lib-src/libogg/libogg.spec
lib-src/libsndfile/build-test-tarball.mk
lib-src/libsndfile/doc/libsndfile.css
lib-src/libsndfile/libsndfile.spec
lib-src/libsndfile/man/sndfile-deinterleave.1
lib-src/libsndfile/man/sndfile-metadata-set.1
lib-src/libsndfile/src/config.h
lib-src/libsndfile/src/sndfile.h
lib-src/libsndfile/src/version-metadata.rc
lib-src/libsndfile/tests/pedantic-header-test.sh
lib-src/libsndfile/tests/test_wrapper.sh
lib-src/libsoxr/soxr-config.h
lib-src/libsoxr/src/libsoxr-dev.src
lib-src/libsoxr/src/libsoxr.src
lib-src/libsoxr/tests/ref-*.s32
lib-src/libvorbis/config.h
lib-src/libvorbis/doc/Doxyfile
lib-src/libvorbis/libvorbis.spec
lib-src/sbsms/libsbsms.spec
lib-src/sbsms/src/config.h
lib-src/soundtouch/include/soundtouch_config.h
lib-src/twolame/doc/html/Doxyfile
lib-src/twolame/libtwolame/config.h
locale/POTFILES
src/audacity.desktop
src/configunix.h
src/configwin.h
*.pc
# CMake build output
lib-src/libsoxr/CMakeCache.txt
lib-src/libsoxr/*.cmake
CMakeFiles/
CTestTestfile.cmake
cmake_install.cmake
# Waf build output
lib-src/lv2/.buildvars
lib-src/lv2/include/
lib-src/lv2/*/build/
lib-src/lv2/srcdir.mk
.lock-waf_linux2_build
# Mac build outputs
mac/Audacity.xcodeproj/project.xcworkspace/contents.xcworkspacedata
mac/Audacity.xcodeproj/project.xcworkspace/xcshareddata/Audacity.xccheckout
@ -46,6 +116,9 @@ mac/tests/
*.dylib
*.dll
# Compiled translation files (GNU Machine Object)
*.gmo
# Fortran module files
*.mod
@ -60,6 +133,38 @@ mac/tests/
*.out
*.app
# Executable without extension
/audacity
src/audacity
lib-src/expat/examples/elements
lib-src/expat/examples/outline
lib-src/expat/xmlwf/xmlwf
lib-src/libflac/examples/c/decode/file/example_c_decode_file
lib-src/libflac/examples/c/encode/file/example_c_encode_file
lib-src/libflac/examples/cpp/decode/file/example_cpp_decode_file
lib-src/libflac/examples/cpp/encode/file/example_cpp_encode_file
lib-src/libflac/src/flac/flac
lib-src/libflac/src/metaflac/metaflac
lib-src/libflac/src/test_grabbag/cuesheet/test_cuesheet
lib-src/libflac/src/test_grabbag/picture/test_picture
lib-src/libflac/src/test_libFLAC++/test_libFLAC++
lib-src/libflac/src/test_libFLAC/test_libFLAC
lib-src/libflac/src/test_seeking/test_seeking
lib-src/libflac/src/test_streams/test_streams
lib-src/libogg/src/test_bitwise
lib-src/libogg/src/test_framing
lib-src/libsndfile/src/G72x/g72x_test
lib-src/libsndfile/src/test_main
lib-src/libsoxr/examples/1a-lsr
lib-src/libsoxr/examples/3-options-input-fn
lib-src/libsoxr/tests/vector-cmp
lib-src/libsoxr/tests/vector-gen
lib-src/libvorbis/lib/test_sharedbook
lib-src/portaudio-v19/bin/
lib-src/soundtouch/source/SoundStretch/soundstretch
lib-src/twolame/frontend/twolame
lib-src/twolame/simplefrontend/stwolame
# Mac Specific
.DS_Store
xcuserdata

View File

@ -34,7 +34,7 @@ dnl Require autoconf >= 2.59
AC_PREREQ([2.59])
dnl Init autoconf
AC_INIT([audacity], [2.0.6])
AC_INIT([audacity], [2.1.2])
dnl Check for existence of Audacity.h
AC_CONFIG_SRCDIR([src/Audacity.h])
AC_CONFIG_AUX_DIR([autotools])

View File

@ -191,7 +191,7 @@ def build(bld):
defines = ['SUIL_SHARED', 'SUIL_INTERNAL'],
install_path = module_dir,
cflags = cflags,
lib = modlib,
lib = modlib + ['X11'],
linkflags = bld.env.NODELETE_FLAGS)
autowaf.use_lib(bld, obj, 'GTK2 GTK2_X11 LV2 LV2_1_4_3')

View File

@ -405,18 +405,28 @@ struct AudioIO::ScrubQueue
double LastTimeInQueue() const
{
// Needed by the main thread sometimes
wxCriticalSectionLocker locker(mUpdating);
wxMutexLocker locker(mUpdating);
const Entry &previous = mEntries[(mLeadingIdx + Size - 1) % Size];
return previous.mS1 / mRate;
}
void PoisonPill()
{
// Main thread is shutting down the scrubbing
wxMutexLocker locker(mUpdating);
mPoisoned = true;
mAvailable.Signal();
}
bool Producer(double end, double maxSpeed, bool bySpeed, bool maySkip)
{
wxASSERT(!mPoisoned);
// Main thread indicates a scrubbing interval
// MAY ADVANCE mLeadingIdx, BUT IT NEVER CATCHES UP TO mTrailingIdx.
wxCriticalSectionLocker locker(mUpdating);
wxMutexLocker locker(mUpdating);
const unsigned next = (mLeadingIdx + 1) % Size;
if (next != mTrailingIdx)
{
@ -429,8 +439,10 @@ struct AudioIO::ScrubQueue
const bool success =
(InitEntry(mEntries[mLeadingIdx], startTime, end, maxSpeed,
bySpeed, &previous, maySkip));
if (success)
if (success) {
mLeadingIdx = next;
mAvailable.Signal();
}
return success;
}
else
@ -450,7 +462,10 @@ struct AudioIO::ScrubQueue
// MAY ADVANCE mMiddleIdx, WHICH MAY EQUAL mLeadingIdx, BUT DOES NOT PASS IT.
wxCriticalSectionLocker locker(mUpdating);
wxMutexLocker locker(mUpdating);
while(!mPoisoned && mMiddleIdx == mLeadingIdx)
mAvailable.Wait();
if (mMiddleIdx != mLeadingIdx)
{
// There is work in the queue
@ -463,7 +478,8 @@ struct AudioIO::ScrubQueue
}
else
{
// next entry is not yet ready
wxASSERT(mPoisoned);
// We got the shut-down signal
startSample = endSample = duration = -1L;
}
}
@ -475,7 +491,7 @@ struct AudioIO::ScrubQueue
// MAY ADVANCE mTrailingIdx, BUT IT NEVER CATCHES UP TO mMiddleIdx.
wxCriticalSectionLocker locker(mUpdating);
wxMutexLocker locker(mUpdating);
// Mark entries as partly or fully "consumed" for
// purposes of mTime update. It should not happen that
@ -678,7 +694,9 @@ private:
const double mRate;
const long mMinStutter;
wxLongLong mLastScrubTimeMillis;
mutable wxCriticalSection mUpdating;
mutable wxMutex mUpdating;
mutable wxCondition mAvailable { mUpdating };
bool mPoisoned { false };
};
#endif
@ -1710,14 +1728,6 @@ int AudioIO::StartStream(const WaveTrackArray &playbackTracks,
sampleCount playbackMixBufferSize =
(sampleCount)mPlaybackSamplesToCopy;
// In the extraordinarily rare case that we can't even afford 100 samples, just give up.
if(playbackBufferSize < 100 || playbackMixBufferSize < 100)
{
StartStreamCleanup();
wxMessageBox(_("Out of memory!"));
return 0;
}
mPlaybackBuffers = new RingBuffer* [mPlaybackTracks->size()];
mPlaybackMixers = new Mixer* [mPlaybackTracks->size()];
@ -1786,7 +1796,19 @@ int AudioIO::StartStream(const WaveTrackArray &playbackTracks,
mCaptureRingBufferSecs *= 0.5;
mMinCaptureSecsToCopy *= 0.5;
bDone = false;
}
// In the extraordinarily rare case that we can't even afford 100 samples, just give up.
sampleCount playbackBufferSize =
(sampleCount)lrint(mRate * mPlaybackRingBufferSecs);
sampleCount playbackMixBufferSize =
(sampleCount)mPlaybackSamplesToCopy;
if(playbackBufferSize < 100 || playbackMixBufferSize < 100)
{
StartStreamCleanup();
wxMessageBox(_("Out of memory!"));
return 0;
}
}
} while(!bDone);
if (mNumPlaybackChannels > 0)
@ -2166,6 +2188,8 @@ void AudioIO::StopStream()
//
mAudioThreadFillBuffersLoopRunning = false;
if (mScrubQueue)
mScrubQueue->PoisonPill();
// Audacity can deadlock if it tries to update meters while
// we're stopping PortAudio (because the meter updating code
@ -2821,7 +2845,16 @@ AudioThread::ExitCode AudioThread::Entry()
}
gAudioIO->mAudioThreadFillBuffersLoopActive = false;
Sleep(10);
if (gAudioIO->mPlayMode == AudioIO::PLAY_SCRUB) {
// Rely on the Wait() in ScrubQueue::Transformer()
// This allows the scrubbing update interval to be made very short without
// playback becoming intermittent.
}
else {
// Perhaps this too could use a condition variable, for available space in the
// ring buffer, instead of a polling loop? But no harm in doing it this way.
Sleep(10);
}
}
return 0;

View File

@ -650,7 +650,7 @@ private:
bool mInputMixerWorks;
float mMixerOutputVol;
enum {
volatile enum {
PLAY_STRAIGHT,
PLAY_LOOPED,
#ifdef EXPERIMENTAL_SCRUBBING_SUPPORT

View File

@ -928,7 +928,7 @@ void AudacityProject::CreateMenusAndCommands()
//////////////////////////////////////////////////////////////////////////
c->AddSeparator();
c->AddCheck(wxT("ScrollLeftOfZero"), _("Scroll left of zero"),
c->AddCheck(wxT("ScrollLeftOfZero"), _("Scroll le&ft of zero"),
FN(OnToggleScrollLeftOfZero),
gPrefs->ReadBool(
TracksPrefs::ScrollingPreferenceKey(),
@ -7072,6 +7072,9 @@ void AudacityProject::SeekLeftOrRight
// Move the visual cursor, avoiding an unnecessary complete redraw
GetTrackPanel()->DrawOverlays(false);
GetRulerPanel()->DrawOverlays(false);
// This updates the selection shown on the selection bar, and the play region
TP_DisplaySelection();
}
else
{

View File

@ -22,7 +22,6 @@
#include "Audacity.h"
#include "Mix.h"
#include <math.h>
@ -251,13 +250,13 @@ Mixer::Mixer(const WaveTrackConstArray &inputTracks,
const auto numInputTracks = inputTracks.size();
mHighQuality = highQuality;
mNumInputTracks = numInputTracks;
mInputTrack = new const WaveTrack*[mNumInputTracks];
mInputTrack = new WaveTrackCache[mNumInputTracks];
// mSamplePos holds for each track the next sample position not
// yet processed.
mSamplePos = new sampleCount[mNumInputTracks];
for(i=0; i<mNumInputTracks; i++) {
mInputTrack[i] = inputTracks[i];
mInputTrack[i].SetTrack(inputTracks[i]);
mSamplePos[i] = inputTracks[i]->TimeToLongSamples(startTime);
}
mTimeTrack = warpOptions.timeTrack;
@ -313,7 +312,7 @@ Mixer::Mixer(const WaveTrackConstArray &inputTracks,
mSampleQueue = new float *[mNumInputTracks];
mResample = new Resample*[mNumInputTracks];
for(i=0; i<mNumInputTracks; i++) {
double factor = (mRate / mInputTrack[i]->GetRate());
double factor = (mRate / mInputTrack[i].GetTrack()->GetRate());
double minFactor, maxFactor;
if (mTimeTrack) {
// variable rate resampling
@ -408,11 +407,12 @@ void MixBuffers(int numChannels, int *channelFlags, float *gains,
}
}
sampleCount Mixer::MixVariableRates(int *channelFlags, const WaveTrack *track,
sampleCount Mixer::MixVariableRates(int *channelFlags, WaveTrackCache &cache,
sampleCount *pos, float *queue,
int *queueStart, int *queueLen,
Resample * pResample)
{
const WaveTrack *const track = cache.GetTrack();
const double trackRate = track->GetRate();
const double initialWarp = mRate / mSpeed / trackRate;
const double tstep = 1.0 / trackRate;
@ -455,10 +455,8 @@ sampleCount Mixer::MixVariableRates(int *channelFlags, const WaveTrack *track,
// Nothing to do if past end of play interval
if (getLen > 0) {
if (backwards) {
track->Get((samplePtr)&queue[*queueLen],
floatSample,
*pos - (getLen - 1),
getLen);
auto results = cache.Get(floatSample, *pos - (getLen - 1), getLen);
memcpy(&queue[*queueLen], results, sizeof(float) * getLen);
track->GetEnvelopeValues(mEnvValues,
getLen,
@ -468,10 +466,8 @@ sampleCount Mixer::MixVariableRates(int *channelFlags, const WaveTrack *track,
*pos -= getLen;
}
else {
track->Get((samplePtr)&queue[*queueLen],
floatSample,
*pos,
getLen);
auto results = cache.Get(floatSample, *pos, getLen);
memcpy(&queue[*queueLen], results, sizeof(float) * getLen);
track->GetEnvelopeValues(mEnvValues,
getLen,
@ -558,9 +554,10 @@ sampleCount Mixer::MixVariableRates(int *channelFlags, const WaveTrack *track,
return out;
}
sampleCount Mixer::MixSameRate(int *channelFlags, const WaveTrack *track,
sampleCount Mixer::MixSameRate(int *channelFlags, WaveTrackCache &cache,
sampleCount *pos)
{
const WaveTrack *const track = cache.GetTrack();
int slen = mMaxOut;
int c;
const double t = *pos / track->GetRate();
@ -588,7 +585,8 @@ sampleCount Mixer::MixSameRate(int *channelFlags, const WaveTrack *track,
slen = mMaxOut;
if (backwards) {
track->Get((samplePtr)mFloatBuffer, floatSample, *pos - (slen - 1), slen);
auto results = cache.Get(floatSample, *pos - (slen - 1), slen);
memcpy(mFloatBuffer, results, sizeof(float) * slen);
track->GetEnvelopeValues(mEnvValues, slen, t - (slen - 1) / mRate, 1.0 / mRate);
for(int i=0; i<slen; i++)
mFloatBuffer[i] *= mEnvValues[i]; // Track gain control will go here?
@ -597,7 +595,8 @@ sampleCount Mixer::MixSameRate(int *channelFlags, const WaveTrack *track,
*pos -= slen;
}
else {
track->Get((samplePtr)mFloatBuffer, floatSample, *pos, slen);
auto results = cache.Get(floatSample, *pos, slen);
memcpy(mFloatBuffer, results, sizeof(float) * slen);
track->GetEnvelopeValues(mEnvValues, slen, t, 1.0 / mRate);
for(int i=0; i<slen; i++)
mFloatBuffer[i] *= mEnvValues[i]; // Track gain control will go here?
@ -632,7 +631,7 @@ sampleCount Mixer::Process(sampleCount maxToProcess)
Clear();
for(i=0; i<mNumInputTracks; i++) {
const WaveTrack *track = mInputTrack[i];
const WaveTrack *const track = mInputTrack[i].GetTrack();
for(j=0; j<mNumChannels; j++)
channelFlags[j] = 0;
@ -661,12 +660,12 @@ sampleCount Mixer::Process(sampleCount maxToProcess)
}
if (mbVariableRates || track->GetRate() != mRate)
maxOut = std::max(maxOut,
MixVariableRates(channelFlags, track,
&mSamplePos[i], mSampleQueue[i],
&mQueueStart[i], &mQueueLen[i], mResample[i]));
MixVariableRates(channelFlags, mInputTrack[i],
&mSamplePos[i], mSampleQueue[i],
&mQueueStart[i], &mQueueLen[i], mResample[i]));
else
maxOut = std::max(maxOut,
MixSameRate(channelFlags, track, &mSamplePos[i]));
MixSameRate(channelFlags, mInputTrack[i], &mSamplePos[i]));
double t = (double)mSamplePos[i] / (double)track->GetRate();
if (mT0 > mT1)
@ -728,7 +727,7 @@ void Mixer::Restart()
mTime = mT0;
for(i=0; i<mNumInputTracks; i++)
mSamplePos[i] = mInputTrack[i]->TimeToLongSamples(mT0);
mSamplePos[i] = mInputTrack[i].GetTrack()->TimeToLongSamples(mT0);
for(i=0; i<mNumInputTracks; i++) {
mQueueStart[i] = 0;
@ -748,7 +747,7 @@ void Mixer::Reposition(double t)
mTime = std::max(mT0, (std::min(mT1, mTime)));
for(i=0; i<mNumInputTracks; i++) {
mSamplePos[i] = mInputTrack[i]->TimeToLongSamples(mTime);
mSamplePos[i] = mInputTrack[i].GetTrack()->TimeToLongSamples(mTime);
mQueueStart[i] = 0;
mQueueLen[i] = 0;
}

View File

@ -14,16 +14,16 @@
#include "MemoryX.h"
#include <wx/string.h>
#include "SampleFormat.h"
#include "Resample.h"
class Resample;
class DirManager;
class TimeTrack;
class TrackFactory;
class TrackList;
class WaveTrack;
class WaveTrackConstArray;
class WaveTrackCache;
/** @brief Mixes together all input tracks, applying any envelopes, amplitude
* gain, panning, and real-time effects in the process.
@ -141,10 +141,10 @@ class AUDACITY_DLL_API Mixer {
private:
void Clear();
sampleCount MixSameRate(int *channelFlags, const WaveTrack *src,
sampleCount MixSameRate(int *channelFlags, WaveTrackCache &cache,
sampleCount *pos);
sampleCount MixVariableRates(int *channelFlags, const WaveTrack *track,
sampleCount MixVariableRates(int *channelFlags, WaveTrackCache &cache,
sampleCount *pos, float *queue,
int *queueStart, int *queueLen,
Resample * pResample);
@ -152,7 +152,8 @@ class AUDACITY_DLL_API Mixer {
private:
// Input
int mNumInputTracks;
const WaveTrack **mInputTrack;
WaveTrackCache *mInputTrack;
bool mbVariableRates;
const TimeTrack *mTimeTrack;
sampleCount *mSamplePos;

View File

@ -4705,6 +4705,15 @@ void AudacityProject::TP_DisplaySelection()
{
double audioTime;
if (mRuler) {
if (!gAudioIO->IsBusy() && !mLockPlayRegion)
mRuler->SetPlayRegion(mViewInfo.selectedRegion.t0(),
mViewInfo.selectedRegion.t1());
else
// Cause ruler redraw anyway, because we may be zooming or scrolling
mRuler->Refresh();
}
if (gAudioIO->IsBusy())
audioTime = gAudioIO->GetStreamTime();
else {
@ -4719,14 +4728,6 @@ void AudacityProject::TP_DisplaySelection()
(mViewInfo.selectedRegion.f0(), mViewInfo.selectedRegion.f1());
#endif
if (mRuler) {
if (!gAudioIO->IsBusy() && !mLockPlayRegion)
mRuler->SetPlayRegion(mViewInfo.selectedRegion.t0(),
mViewInfo.selectedRegion.t1());
else
// Cause ruler redraw anyway, because we may be zooming or scrolling
mRuler->Refresh();
}
}

View File

@ -25,7 +25,7 @@
#include "../widgets/NumericTextCtrl.h"
#include "FileDialog.h"
#include <math.h>
#include <cmath>
#include <limits>
#if defined(__WXMSW__) && !defined(__CYGWIN__)
@ -58,6 +58,8 @@
#define DB_MAX_LIMIT 0.0 // TODO: We should probably fail WCAG2 if audio is massively distorted.
#define WCAG2_PASS 20.0 // dB difference required to pass WCAG2 test.
using std::isinf;
bool ContrastDialog::GetDB(float &dB)
{
float rms = float(0.0);

View File

@ -13,17 +13,12 @@
#if defined(USE_LV2)
#if defined(__WXMSW__)
#include <float.h>
#define isfinite _finite
#define isnan _isnan
#endif
#include <wx/button.h>
#include <wx/choice.h>
#include <wx/dcbuffer.h>
#include <wx/dialog.h>
#include <wx/dynarray.h>
#include <wx/math.h>
#include <wx/msgdlg.h>
#include <wx/sizer.h>
#include <wx/statbox.h>
@ -562,13 +557,13 @@ bool LV2Effect::SetHost(EffectHostInterface *host)
lilv_scale_points_free(points);
// Collect the value and range info
ctrl.mHasLo = !isnan(minimumVals[i]);
ctrl.mHasHi = !isnan(maximumVals[i]);
ctrl.mHasLo = !wxIsNaN(minimumVals[i]);
ctrl.mHasHi = !wxIsNaN(maximumVals[i]);
ctrl.mMin = ctrl.mHasLo ? minimumVals[i] : 0.0;
ctrl.mMax = ctrl.mHasHi ? maximumVals[i] : 1.0;
ctrl.mLo = ctrl.mMin;
ctrl.mHi = ctrl.mMax;
ctrl.mDef = !isnan(defaultValues[i]) ?
ctrl.mDef = !wxIsNaN(defaultValues[i]) ?
defaultValues[i] :
ctrl.mHasLo ?
ctrl.mLo :

View File

@ -27,8 +27,8 @@ effects from this one class.
#include "../../Audacity.h"
#include <algorithm>
#include <cmath>
#include <math.h>
#include <locale.h>
#include <wx/checkbox.h>
@ -683,7 +683,7 @@ bool NyquistEffect::Process()
// with very small values, bandwidth calculation may be inf.
// (Observed on Linux)
double bw = log(mF1 / mF0) / log(2.0);
if (!isinf(bw)) {
if (!std::isinf(bw)) {
bandwidth.Printf(wxT("(float %s)"), Internat::ToString(bw).c_str());
}
}

View File

@ -127,9 +127,6 @@ void EditCursorOverlay::Draw(OverlayPanel &panel, wxDC &dc)
}
}
// This updates related displays such as numbers on the status bar
mProject->TP_DisplaySelection();
}
else if (auto ruler = dynamic_cast<AdornedRulerPanel*>(&panel)) {
wxASSERT(!mIsMaster);

View File

@ -38,6 +38,8 @@ enum {
#ifdef EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL
ScrubSpeedStepsPerOctave = 4,
#endif
ScrubPollInterval_ms = 50,
};
namespace {
@ -112,6 +114,26 @@ namespace {
}
}
class Scrubber::ScrubPoller : public wxTimer
{
public:
ScrubPoller(Scrubber &scrubber) : mScrubber{ scrubber } {}
private:
void Notify() override;
Scrubber &mScrubber;
};
void Scrubber::ScrubPoller::Notify()
{
// Call ContinueScrubbing() here in a 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.
mScrubber.ContinueScrubbing();
}
Scrubber::Scrubber(AudacityProject *project)
: mScrubToken(-1)
, mScrubStartClockTimeMillis(-1)
@ -126,6 +148,7 @@ Scrubber::Scrubber(AudacityProject *project)
#endif
, mProject(project)
, mPoller { std::make_unique<ScrubPoller>(*this) }
{
if (wxTheApp)
wxTheApp->Connect
@ -269,7 +292,7 @@ bool Scrubber::MaybeStartScrubbing(wxCoord xx)
AudioIOStartStreamOptions options(mProject->GetDefaultPlayOptions());
options.timeTrack = NULL;
options.scrubDelay = (kTimerInterval / 1000.0);
options.scrubDelay = (ScrubPollInterval_ms / 1000.0);
options.scrubStartClockTimeMillis = mScrubStartClockTimeMillis;
options.minScrubStutter = 0.2;
#if 0
@ -311,6 +334,8 @@ bool Scrubber::MaybeStartScrubbing(wxCoord xx)
mProject->GetPlaybackScroller().Activate(mSmoothScrollingScrub);
mScrubHasFocus = true;
mLastScrubPosition = xx;
mPoller->Start(ScrubPollInterval_ms);
}
// Return true whether we started scrub, or are still waiting to decide.
@ -393,6 +418,8 @@ void Scrubber::ContinueScrubbing()
void Scrubber::StopScrubbing()
{
mPoller->Stop();
UncheckAllMenuItems();
mScrubStartPosition = -1;
@ -597,12 +624,6 @@ void ScrubbingOverlay::OnTimer(wxCommandEvent &event)
ruler->ShowQuickPlayIndicator();
}
// 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();
}

View File

@ -11,6 +11,7 @@ Paul Licameli split from TrackPanel.cpp
#ifndef __AUDACITY_SCRUBBING__
#define __AUDACITY_SCRUBBING__
#include "../../MemoryX.h"
#include <vector>
#include <wx/event.h>
#include <wx/longlong.h>
@ -125,6 +126,9 @@ private:
AudacityProject *mProject;
DECLARE_EVENT_TABLE()
class ScrubPoller;
std::unique_ptr<ScrubPoller> mPoller;
};
// Specialist in drawing the scrub speed, and listening for certain events