1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-05-05 14:18:53 +02:00

Bug 133 - Enh: Play-at-Speed slider: Change of playback speed is no longer automatic

This adds a new preference to Playback prefs, VariSpeedPlay, on by default, which makes it possible to vary the playback speed slider whilst audio is playing using play at speed.  The code uses the Scrubbing playback engine.

This code is not final, but is already useful.  Known issues:
  1- Playback does not stop and pop-up the Play at Speed button at the end of playing the selection.
  2- The scrub widget shows uselessly on the scrub ruler when in Play at Speed mode and the status bar proclaims 'Scrubbing'.
  3- At low speeds the playback is clicky, with VariSpeedPlay, even if no changes to speed are being made.

If we can't fix both 1 and 2 by 2.3.0 release time, VariSpeedPlay will be made false by default.
This commit is contained in:
James Crook 2018-07-20 21:43:07 +01:00
parent 848b66f4fa
commit 17ca54ad4b
4 changed files with 148 additions and 21 deletions

View File

@ -115,6 +115,18 @@ void PlaybackPrefs::PopulateOrExchange(ShuttleGui & S)
S.EndThreeColumn();
}
S.EndStatic();
S.StartStatic(_("Options"));
{
S.StartTwoColumn();
{
S.TieCheckBox("&Vari-Speed Play", "/AudioIO/VariSpeedPlay", true);
}
S.EndTwoColumn();
}
S.EndStatic();
S.EndScroller();
}

View File

@ -40,6 +40,8 @@
#include "../WaveTrack.h"
#include "../widgets/AButton.h"
#include "../widgets/ASlider.h"
#include "../tracks/ui/Scrubbing.h"
#include "../Prefs.h"
#ifdef EXPERIMENTAL_VOICE_DETECTION
#include "../VoiceKey.h"
@ -447,12 +449,22 @@ void TranscriptionToolBar::PlayAtSpeed(bool looped, bool cutPreview)
return;
}
// Create a TimeTrack if we haven't done so already
if (!mTimeTrack) {
mTimeTrack = p->GetTrackFactory()->NewTimeTrack();
// Fixed speed play is the old method, that uses a time track.
// VariSpeed play reuses Scrubbing.
bool bFixedSpeedPlay = !gPrefs->ReadBool(wxT("/AudioIO/VariSpeedPlay"), true);
if (bFixedSpeedPlay)
{
// Create a TimeTrack if we haven't done so already
if (!mTimeTrack) {
return;
mTimeTrack = p->GetTrackFactory()->NewTimeTrack();
if (!mTimeTrack) {
return;
}
}
// Set the speed range
//mTimeTrack->SetRangeUpper((double)mPlaySpeed / 100.0);
//mTimeTrack->SetRangeLower((double)mPlaySpeed / 100.0);
mTimeTrack->GetEnvelope()->Flatten((double)mPlaySpeed / 100.0);
}
// Pop up the button
@ -463,18 +475,15 @@ void TranscriptionToolBar::PlayAtSpeed(bool looped, bool cutPreview)
p->GetControlToolBar()->StopPlaying();
}
// Set the speed range
//mTimeTrack->SetRangeUpper((double)mPlaySpeed / 100.0);
//mTimeTrack->SetRangeLower((double)mPlaySpeed / 100.0);
mTimeTrack->GetEnvelope()->Flatten((double)mPlaySpeed / 100.0);
// Get the current play region
double playRegionStart, playRegionEnd;
p->GetPlayRegion(&playRegionStart, &playRegionEnd);
// Start playing
if (playRegionStart >= 0) {
// playRegionEnd = playRegionStart + (playRegionEnd-playRegionStart)* 100.0/mPlaySpeed;
if (playRegionStart < 0)
return;
if (bFixedSpeedPlay)
{
AudioIOStartStreamOptions options(p->GetDefaultPlayOptions());
options.playLooped = looped;
options.timeTrack = mTimeTrack.get();
@ -484,9 +493,14 @@ void TranscriptionToolBar::PlayAtSpeed(bool looped, bool cutPreview)
: ControlToolBar::PlayAppearance::Straight;
p->GetControlToolBar()->PlayPlayRegion
(SelectedRegion(playRegionStart, playRegionEnd),
options,
PlayMode::normalPlay,
appearance);
options,
PlayMode::normalPlay,
appearance);
}
else
{
Scrubber &Scrubber = p->GetScrubber();
Scrubber.StartSpeedPlay(GetPlaySpeed(), playRegionStart, playRegionEnd);
}
}

View File

@ -21,6 +21,7 @@ Paul Licameli split from TrackPanel.cpp
#include "../../toolbars/ControlToolBar.h"
#include "../../toolbars/ScrubbingToolBar.h"
#include "../../toolbars/ToolManager.h"
#include "../../toolbars/TranscriptionToolBar.h"
#undef USE_TRANSCRIPTION_TOOLBAR
#ifdef USE_TRANSCRIPTION_TOOLBAR
@ -348,7 +349,7 @@ bool Scrubber::MaybeStartScrubbing(wxCoord xx)
time1 = time0 + delta;
}
#endif
mSpeedPlaying = false;
AudioIOStartStreamOptions options(mProject->GetDefaultPlayOptions());
options.pScrubbingOptions = &mOptions;
options.timeTrack = NULL;
@ -432,6 +433,80 @@ bool Scrubber::MaybeStartScrubbing(wxCoord xx)
}
}
bool Scrubber::StartSpeedPlay(double speed, double time0, double time1)
{
if (IsScrubbing())
return false;
const bool busy = gAudioIO->IsBusy();
if (busy && gAudioIO->GetNumCaptureChannels() > 0) {
// Do not stop recording, and don't try to start scrubbing after
// recording stops
mScrubStartPosition = -1;
return false;
}
ControlToolBar * const ctb = mProject->GetControlToolBar();
if (busy) {
ctb->StopPlaying();
}
mScrubStartPosition = 0;
mSpeedPlaying = true;
mMaxSpeed = speed;
mDragging = false;
AudioIOStartStreamOptions options(mProject->GetDefaultPlayOptions());
options.pScrubbingOptions = &mOptions;
options.timeTrack = NULL;
mOptions.startClockTimeMillis = ::wxGetLocalTimeMillis();
mOptions.delay = (ScrubPollInterval_ms * 0.9 / 1000.0);
mOptions.minSpeed = speed;
mOptions.maxSpeed = speed;
if (time1 == time0)
time1 = std::max(0.0, mProject->GetTracks()->GetEndTime());
mOptions.minSample = 0;
mOptions.maxSample = lrint(time1 * options.rate);
mOptions.minStutter = lrint(std::max(0.0, MinStutter) * options.rate);
mOptions.enqueueBySpeed = true;
mOptions.adjustStart = false;
ControlToolBar::PlayAppearance appearance = ControlToolBar::PlayAppearance::Straight;
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(mMaxSpeed) / log(maxScrubSpeedBase)
);
#endif
mScrubSpeedDisplayCountdown = 0;
mScrubToken =
ctb->PlayPlayRegion(SelectedRegion(time0, time1), options,
PlayMode::normalPlay, appearance, backwards);
if (mScrubToken >= 0) {
mPaused = false;
mLastScrubPosition = 0;
#ifdef USE_SCRUB_THREAD
// Detached thread is self-deleting, after it receives the Delete() message
mpThread = safenew ScrubPollerThread{ *this };
mpThread->Create(4096);
mpThread->Run();
#endif
mPoller->Start(ScrubPollInterval_ms);
}
return true;
}
void Scrubber::ContinueScrubbingPoll()
{
// Thus scrubbing relies mostly on periodic polling of mouse and keys,
@ -452,7 +527,20 @@ void Scrubber::ContinueScrubbingPoll()
mOptions.enqueueBySpeed = true;
result = gAudioIO->EnqueueScrub(0, mOptions);
}
else {
else if (mSpeedPlaying) {
// default speed of 1.3 set, so that we can hear there is a problem
// when playAtSpeedTB not found.
double speed = 1.3;
TranscriptionToolBar *const playAtSpeedTB = mProject->GetTranscriptionToolBar();
if (playAtSpeedTB) {
speed = playAtSpeedTB->GetPlaySpeed();
}
mOptions.minSpeed = speed;
mOptions.maxSpeed = speed;
mOptions.adjustStart = false;
mOptions.enqueueBySpeed = true;
result = gAudioIO->EnqueueScrub(speed, mOptions);
} else {
const wxMouseState state(::wxGetMouseState());
const auto trackPanel = mProject->GetTrackPanel();
const wxPoint position = trackPanel->ScreenToClient(state.GetPosition());
@ -687,10 +775,22 @@ bool Scrubber::IsPaused() const
void Scrubber::OnActivateOrDeactivateApp(wxActivateEvent &event)
{
if (event.GetActive())
Pause(!IsScrubbing() || mProject->GetControlToolBar()->IsPauseDown());
// First match priority logic...
// Pause if Pause down, or not scrubbing.
if (mProject->GetControlToolBar()->IsPauseDown())
Pause( true );
else if (!IsScrubbing())
Pause( true );
// Speed playing does not pause if losing focus.
else if (mSpeedPlaying)
Pause( false );
// But scrub and seek do.
else if (!event.GetActive())
Pause( true );
else
Pause(true);
Pause(false);
event.Skip();
}

View File

@ -80,6 +80,7 @@ public:
// Returns true iff the event should be considered consumed by this:
// Assume xx is relative to the left edge of TrackPanel!
bool MaybeStartScrubbing(wxCoord xx);
bool StartSpeedPlay(double speed, double time0, double time1);
void ContinueScrubbingUI();
void ContinueScrubbingPoll();
@ -168,15 +169,15 @@ private:
private:
int mScrubToken;
bool mPaused;
int mScrubSpeedDisplayCountdown;
wxCoord mScrubStartPosition;
wxCoord mLastScrubPosition {};
bool mScrubSeekPress {};
bool mSmoothScrollingScrub;
bool mPaused{};
bool mSeeking {};
bool mSpeedPlaying{true};
bool mDragging {};
bool mCancelled {};