mirror of
https://github.com/cookiengineer/audacity
synced 2025-06-18 09:00:07 +02:00
Bug 1954: Clicks may occur starting/pausing play-at-speed or Scrub
Problem: On Windows, after 50ms, there is a short period of roughly zero introduced into the output. On Linux, there is also a spike which sounds like a crackle. In AudioIO::FillBuffers(), Mixer::SetTimesAndSpeed() is called, which sets mT0 and mT1 to a small interval. In Mixer::MixVariableRates(), all the samples in the interval are used, which means the Resample::Process() is called with last equal to true. So when Mixer::MixVariableRates() is called again, the resampler is being reused after a call to Process() in which last is true. It is not stated in the soxr documentation if the resampler will produce valid results in this case, and it's only the scrubbing code which does this. I think this is the problem, and so the partial fix below avoids this happening. Partial fix for play-at-speed and keyboard scrubbing: For these, there is no need to reset the values of mT0 and mT1. (There is no need to allow for the sample position being used to potentially jump around.) So for these cases, Mixer::SetSpeed() is called, rather than Mixer::SetTimesAndSpeed().
This commit is contained in:
parent
f11b2b6bab
commit
6b9c8e79cc
@ -2795,6 +2795,7 @@ void AudioIO::FillBuffers()
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_SUPPORT
|
||||
case PlaybackSchedule::PLAY_SCRUB:
|
||||
case PlaybackSchedule::PLAY_AT_SPEED:
|
||||
case PlaybackSchedule::PLAY_KEYBOARD_SCRUB:
|
||||
{
|
||||
mScrubDuration -= frames;
|
||||
wxASSERT(mScrubDuration >= 0);
|
||||
@ -2825,9 +2826,16 @@ void AudioIO::FillBuffers()
|
||||
double(diff) / mScrubDuration.as_double();
|
||||
if (!mSilentScrub)
|
||||
{
|
||||
for (i = 0; i < mPlaybackTracks.size(); i++)
|
||||
mPlaybackMixers[i]->SetTimesAndSpeed(
|
||||
startTime, endTime, fabs( mScrubSpeed ));
|
||||
for (i = 0; i < mPlaybackTracks.size(); i++) {
|
||||
if (mPlaybackSchedule.mPlayMode == PlaybackSchedule::PLAY_AT_SPEED ||
|
||||
mPlaybackSchedule.mPlayMode == PlaybackSchedule::PLAY_KEYBOARD_SCRUB) {
|
||||
mPlaybackMixers[i]->SetSpeed(mScrubSpeed);
|
||||
}
|
||||
else {
|
||||
mPlaybackMixers[i]->SetTimesAndSpeed(
|
||||
startTime, endTime, fabs( mScrubSpeed ));
|
||||
}
|
||||
}
|
||||
}
|
||||
mTimeQueue.mLastTime = startTime;
|
||||
}
|
||||
|
@ -427,10 +427,14 @@ void AudioIOBase::PlaybackSchedule::Init(
|
||||
wxASSERT(false);
|
||||
scrubbing = false;
|
||||
}
|
||||
else
|
||||
mPlayMode = (scrubOptions.isPlayingAtSpeed)
|
||||
? PlaybackSchedule::PLAY_AT_SPEED
|
||||
: PlaybackSchedule::PLAY_SCRUB;
|
||||
else {
|
||||
if (scrubOptions.isPlayingAtSpeed)
|
||||
mPlayMode = PLAY_AT_SPEED;
|
||||
else if (scrubOptions.isKeyboardScrubbing)
|
||||
mPlayMode = PLAY_KEYBOARD_SCRUB;
|
||||
else
|
||||
mPlayMode = PLAY_SCRUB;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -49,6 +49,7 @@ struct ScrubbingOptions {
|
||||
|
||||
bool bySpeed {};
|
||||
bool isPlayingAtSpeed{};
|
||||
bool isKeyboardScrubbing{};
|
||||
|
||||
double delay {};
|
||||
|
||||
@ -358,6 +359,7 @@ protected:
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_SUPPORT
|
||||
PLAY_SCRUB,
|
||||
PLAY_AT_SPEED, // a version of PLAY_SCRUB.
|
||||
PLAY_KEYBOARD_SCRUB,
|
||||
#endif
|
||||
} mPlayMode { PLAY_STRAIGHT };
|
||||
double mCutPreviewGapStart;
|
||||
@ -411,7 +413,7 @@ protected:
|
||||
|
||||
bool PlayingStraight() const { return mPlayMode == PLAY_STRAIGHT; }
|
||||
bool Looping() const { return mPlayMode == PLAY_LOOPED; }
|
||||
bool Scrubbing() const { return mPlayMode == PLAY_SCRUB; }
|
||||
bool Scrubbing() const { return mPlayMode == PLAY_SCRUB || mPlayMode == PLAY_KEYBOARD_SCRUB; }
|
||||
bool PlayingAtSpeed() const { return mPlayMode == PLAY_AT_SPEED; }
|
||||
bool Interactive() const { return Scrubbing() || PlayingAtSpeed(); }
|
||||
|
||||
|
@ -762,6 +762,12 @@ void Mixer::SetTimesAndSpeed(double t0, double t1, double speed)
|
||||
Reposition(t0);
|
||||
}
|
||||
|
||||
void Mixer::SetSpeed(double speed)
|
||||
{
|
||||
wxASSERT(std::isfinite(speed));
|
||||
mSpeed = fabs(speed);
|
||||
}
|
||||
|
||||
MixerSpec::MixerSpec( unsigned numTracks, unsigned maxNumChannels )
|
||||
{
|
||||
mNumTracks = mNumChannels = numTracks;
|
||||
|
@ -134,6 +134,7 @@ class AUDACITY_DLL_API Mixer {
|
||||
|
||||
// Used in scrubbing.
|
||||
void SetTimesAndSpeed(double t0, double t1, double speed);
|
||||
void SetSpeed(double speed);
|
||||
|
||||
/// Current time in seconds (unwarped, i.e. always between startTime and stopTime)
|
||||
/// This value is not accurate, it's useful for progress bars and indicators, but nothing else.
|
||||
|
@ -406,6 +406,7 @@ bool Scrubber::MaybeStartScrubbing(wxCoord xx)
|
||||
options.envelope = nullptr;
|
||||
mOptions.delay = (ScrubPollInterval_ms / 1000.0);
|
||||
mOptions.isPlayingAtSpeed = false;
|
||||
mOptions.isKeyboardScrubbing = false;
|
||||
mOptions.minSpeed = 0.0;
|
||||
#ifdef USE_TRANSCRIPTION_TOOLBAR
|
||||
if (!mAlwaysSeeking) {
|
||||
@ -530,6 +531,7 @@ bool Scrubber::StartSpeedPlay(double speed, double time0, double time1)
|
||||
mOptions.bySpeed = true;
|
||||
mOptions.adjustStart = false;
|
||||
mOptions.isPlayingAtSpeed = true;
|
||||
mOptions.isKeyboardScrubbing = false;
|
||||
|
||||
const bool backwards = time1 < time0;
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL
|
||||
@ -606,6 +608,7 @@ bool Scrubber::StartKeyboardScrubbing(double time0, bool backwards)
|
||||
mOptions.bySpeed = true;
|
||||
mOptions.adjustStart = false;
|
||||
mOptions.isPlayingAtSpeed = false;
|
||||
mOptions.isKeyboardScrubbing = true;
|
||||
|
||||
// Must start the thread and poller first or else PlayPlayRegion
|
||||
// will insert some silence
|
||||
|
Loading…
x
Reference in New Issue
Block a user