1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-10-26 07:13:49 +01:00

Make PlaybackSchedule::mTime atomic and rename some functions...

... The variable ought to be atomic because it is read and written by different
threads.

Use local variables to avoid repeated reads of the atomic.
This commit is contained in:
Paul Licameli
2018-08-10 13:18:13 -04:00
parent 8a78ae280c
commit f036700b09
2 changed files with 98 additions and 45 deletions

View File

@@ -2206,14 +2206,16 @@ int AudioIO::StartStream(const TransportTracks &tracks,
if (options.pStartTime) if (options.pStartTime)
{ {
// Calculate the NEW time position // Calculate the NEW time position
mPlaybackSchedule.mTime = const auto time = mPlaybackSchedule.ClampTrackTime( *options.pStartTime );
std::max(mPlaybackSchedule.mT0,
std::min(mPlaybackSchedule.mT1, *options.pStartTime)); // Main thread's initialization of mTime
mPlaybackSchedule.SetTrackTime( time );
// Reset mixer positions for all playback tracks // Reset mixer positions for all playback tracks
unsigned numMixers = mPlaybackTracks.size(); unsigned numMixers = mPlaybackTracks.size();
for (unsigned ii = 0; ii < numMixers; ++ii) for (unsigned ii = 0; ii < numMixers; ++ii)
mPlaybackMixers[ii]->Reposition(mPlaybackSchedule.mTime); mPlaybackMixers[ii]->Reposition( time );
mPlaybackSchedule.RealTimeInit(); mPlaybackSchedule.RealTimeInit( time );
} }
#ifdef EXPERIMENTAL_SCRUBBING_SUPPORT #ifdef EXPERIMENTAL_SCRUBBING_SUPPORT
@@ -2875,7 +2877,8 @@ void AudioIO::PlaybackSchedule::Init(
// desired length of recording // desired length of recording
mT1 -= pRecordingSchedule->mLatencyCorrection; mT1 -= pRecordingSchedule->mLatencyCorrection;
mTime = mT0; // Main thread's initialization of mTime
SetTrackTime( mT0 );
mPlayMode = options.playLooped mPlayMode = options.playLooped
? PlaybackSchedule::PLAY_LOOPED ? PlaybackSchedule::PLAY_LOOPED
@@ -2913,17 +2916,25 @@ void AudioIO::PlaybackSchedule::Init(
mWarpedLength = RealDuration(mT1); mWarpedLength = RealDuration(mT1);
} }
double AudioIO::PlaybackSchedule::LimitStreamTime() const double AudioIO::PlaybackSchedule::LimitTrackTime() const
{ {
// Track time readout for the main thread
// Allows for forward or backward play // Allows for forward or backward play
if (ReversedTime()) return ClampTrackTime( GetTrackTime() );
return std::max(mT1, std::min(mT0, mTime));
else
return std::max(mT0, std::min(mT1, mTime));
} }
double AudioIO::PlaybackSchedule::NormalizeStreamTime() const double AudioIO::PlaybackSchedule::ClampTrackTime( double trackTime ) const
{ {
if (ReversedTime())
return std::max(mT1, std::min(mT0, trackTime));
else
return std::max(mT0, std::min(mT1, trackTime));
}
double AudioIO::PlaybackSchedule::NormalizeTrackTime() const
{
// Track time readout for the main thread
// dmazzoni: This function is needed for two reasons: // dmazzoni: This function is needed for two reasons:
// One is for looped-play mode - this function makes sure that the // One is for looped-play mode - this function makes sure that the
// position indicator keeps wrapping around. The other reason is // position indicator keeps wrapping around. The other reason is
@@ -2936,14 +2947,16 @@ double AudioIO::PlaybackSchedule::NormalizeStreamTime() const
// mode. In this case, we should jump over a defined "gap" in the // mode. In this case, we should jump over a defined "gap" in the
// audio. // audio.
auto absoluteTime = mTime; double absoluteTime;
#ifdef EXPERIMENTAL_SCRUBBING_SUPPORT #ifdef EXPERIMENTAL_SCRUBBING_SUPPORT
// Limit the time between t0 and t1 if not scrubbing. // Limit the time between t0 and t1 if not scrubbing.
// Should the limiting be necessary in any play mode if there are no bugs? // Should the limiting be necessary in any play mode if there are no bugs?
if (!Interactive()) if (Interactive())
absoluteTime = GetTrackTime();
else
#endif #endif
absoluteTime = LimitStreamTime(); absoluteTime = LimitTrackTime();
if (mCutPreviewGapLen > 0) if (mCutPreviewGapLen > 0)
{ {
@@ -2958,10 +2971,12 @@ double AudioIO::PlaybackSchedule::NormalizeStreamTime() const
double AudioIO::GetStreamTime() double AudioIO::GetStreamTime()
{ {
// Track time readout for the main thread
if( !IsStreamActive() ) if( !IsStreamActive() )
return BAD_STREAM_TIME; return BAD_STREAM_TIME;
return mPlaybackSchedule.NormalizeStreamTime(); return mPlaybackSchedule.NormalizeTrackTime();
} }
@@ -4589,7 +4604,7 @@ void AudioIO::AILAProcess(double maxPeak) {
mAILAMax = max(mAILAMax, maxPeak); mAILAMax = max(mAILAMax, maxPeak);
if ((mAILATotalAnalysis == 0 || mAILAAnalysisCounter < mAILATotalAnalysis) && mPlaybackSchedule.mTime - mAILALastStartTime >= mAILAAnalysisTime) { if ((mAILATotalAnalysis == 0 || mAILAAnalysisCounter < mAILATotalAnalysis) && mPlaybackSchedule.GetTrackTime() - mAILALastStartTime >= mAILAAnalysisTime) {
auto ToLinearIfDB = [](double value, int dbRange) { auto ToLinearIfDB = [](double value, int dbRange) {
if (dbRange >= 0) if (dbRange >= 0)
value = pow(10.0, (-(1.0-value) * dbRange)/20.0); value = pow(10.0, (-(1.0-value) * dbRange)/20.0);
@@ -4670,7 +4685,7 @@ void AudioIO::AILAProcess(double maxPeak) {
mAILAMax = 0; mAILAMax = 0;
wxPrintf("\tA decision was made @ %f\n", mAILAAnalysisEndTime); wxPrintf("\tA decision was made @ %f\n", mAILAAnalysisEndTime);
mAILAClipped = false; mAILAClipped = false;
mAILALastStartTime = mPlaybackSchedule.mTime; mAILALastStartTime = mPlaybackSchedule.GetTrackTime();
if (changetype == 0) if (changetype == 0)
mAILAChangeFactor *= 0.8; //time factor mAILAChangeFactor *= 0.8; //time factor
@@ -5138,7 +5153,7 @@ int AudioIO::AudioCallback(const void *inputBuffer, void *outputBuffer,
// "Consume" only as much as the ring buffers produced, which may // "Consume" only as much as the ring buffers produced, which may
// be less than framesPerBuffer (during "stutter") // be less than framesPerBuffer (during "stutter")
if (mPlaybackSchedule.Interactive()) if (mPlaybackSchedule.Interactive())
mPlaybackSchedule.mTime = mScrubQueue->Consumer( maxLen ); mPlaybackSchedule.SetTrackTime( mScrubQueue->Consumer( maxLen ) );
#endif #endif
em.RealtimeProcessEnd(); em.RealtimeProcessEnd();
@@ -5212,7 +5227,7 @@ int AudioIO::AudioCallback(const void *inputBuffer, void *outputBuffer,
len < framesPerBuffer) ) { len < framesPerBuffer) ) {
// Assume that any good partial buffer should be written leftmost // Assume that any good partial buffer should be written leftmost
// and zeroes will be padded after; label the zeroes. // and zeroes will be padded after; label the zeroes.
auto start = mPlaybackSchedule.mTime + auto start = mPlaybackSchedule.GetTrackTime() +
len / mRate + mRecordingSchedule.mLatencyCorrection; len / mRate + mRecordingSchedule.mLatencyCorrection;
auto duration = (framesPerBuffer - len) / mRate; auto duration = (framesPerBuffer - len) / mRate;
auto interval = std::make_pair( start, duration ); auto interval = std::make_pair( start, duration );
@@ -5384,17 +5399,18 @@ PaStreamCallbackResult AudioIO::CallbackDoSeek()
wxMilliSleep( 50 ); wxMilliSleep( 50 );
} }
// Calculate the NEW time position // Calculate the NEW time position, in the PortAudio callback
mPlaybackSchedule.mTime += mSeek; const auto time = mPlaybackSchedule.ClampTrackTime(
mPlaybackSchedule.mTime = mPlaybackSchedule.LimitStreamTime(); mPlaybackSchedule.GetTrackTime() + mSeek );
mPlaybackSchedule.SetTrackTime( time );
mSeek = 0.0; mSeek = 0.0;
mPlaybackSchedule.RealTimeInit(); mPlaybackSchedule.RealTimeInit( time );
// Reset mixer positions and flush buffers for all tracks // Reset mixer positions and flush buffers for all tracks
for (size_t i = 0; i < numPlaybackTracks; i++) for (size_t i = 0; i < numPlaybackTracks; i++)
{ {
mPlaybackMixers[i]->Reposition( mPlaybackSchedule.mTime ); mPlaybackMixers[i]->Reposition( time );
const auto toDiscard = const auto toDiscard =
mPlaybackBuffers[i]->AvailForGet(); mPlaybackBuffers[i]->AvailForGet();
const auto discarded = const auto discarded =
@@ -5439,32 +5455,41 @@ void AudioIO::CallbackCheckCompletion(
bool AudioIO::PlaybackSchedule::PassIsComplete() const bool AudioIO::PlaybackSchedule::PassIsComplete() const
{ {
// Test mTime within the PortAudio callback
if (Scrubbing()) if (Scrubbing())
return false; // but may be true if playing at speed return false; // but may be true if playing at speed
return (ReversedTime() ? mTime <= mT1 : mTime >= mT1); return Overruns( GetTrackTime() );
}
bool AudioIO::PlaybackSchedule::Overruns( double trackTime ) const
{
return (ReversedTime() ? trackTime <= mT1 : trackTime >= mT1);
} }
void AudioIO::PlaybackSchedule::TrackTimeUpdate(double realElapsed) void AudioIO::PlaybackSchedule::TrackTimeUpdate(double realElapsed)
{ {
// Update mTime within the PortAudio callback
if (Interactive()) if (Interactive())
return; return;
if (ReversedTime()) if (ReversedTime())
realElapsed *= -1.0; realElapsed *= -1.0;
auto time = GetTrackTime();
if (mTimeTrack) { if (mTimeTrack) {
// Defence against a case that might cause the do-loop not to terminate // Defense against a case that might cause the do-loop not to terminate
if ( fabs(mT0 - mT1) < 1e-9 ) { if ( fabs(mT0 - mT1) < 1e-9 ) {
mTime = mT0; SetTrackTime( mT0 );
return; return;
} }
double total; double total;
bool foundTotal = false; bool foundTotal = false;
do { do {
auto oldTime = mTime; auto oldTime = time;
mTime = mTimeTrack->SolveWarpedLength(mTime, realElapsed); time = mTimeTrack->SolveWarpedLength(time, realElapsed);
if (Looping() && PassIsComplete()) { if (Looping() && Overruns( time )) {
// Bug1922: The part of the time track outside the loop should not // Bug1922: The part of the time track outside the loop should not
// influence the result // influence the result
double delta; double delta;
@@ -5477,25 +5502,26 @@ void AudioIO::PlaybackSchedule::TrackTimeUpdate(double realElapsed)
foundTotal = true, total = delta; foundTotal = true, total = delta;
} }
realElapsed -= delta; realElapsed -= delta;
mTime = mT0; time = mT0;
} }
else else
break; break;
} while ( true ); } while ( true );
} }
else { else {
mTime += realElapsed; time += realElapsed;
// Wrap to start if looping // Wrap to start if looping
if (Looping()) { if (Looping()) {
while (PassIsComplete()) { while ( Overruns( time ) ) {
// LL: This is not exactly right, but I'm at my wits end trying to // LL: This is not exactly right, but I'm at my wits end trying to
// figure it out. Feel free to fix it. :-) // figure it out. Feel free to fix it. :-)
// MB: it's much easier than you think, mTime isn't warped at all! // MB: it's much easier than you think, mTime isn't warped at all!
mTime -= mT1 - mT0; time -= mT1 - mT0;
} }
} }
} }
SetTrackTime( time );
} }
double AudioIO::PlaybackSchedule::TrackDuration(double realElapsed) const double AudioIO::PlaybackSchedule::TrackDuration(double realElapsed) const
@@ -5526,12 +5552,12 @@ void AudioIO::PlaybackSchedule::RealTimeAdvance( double increment )
mWarpedTime += increment; mWarpedTime += increment;
} }
void AudioIO::PlaybackSchedule::RealTimeInit() void AudioIO::PlaybackSchedule::RealTimeInit( double trackTime )
{ {
if (Scrubbing()) if (Scrubbing())
mWarpedTime = 0.0; mWarpedTime = 0.0;
else else
mWarpedTime = RealDuration(mTime); mWarpedTime = RealDuration( trackTime );
} }
void AudioIO::PlaybackSchedule::RealTimeRestart() void AudioIO::PlaybackSchedule::RealTimeRestart()
@@ -5556,7 +5582,8 @@ double AudioIO::RecordingSchedule::ToDiscard() const
bool AudioIO::IsCapturing() const bool AudioIO::IsCapturing() const
{ {
// Includes a test of mTime, used in the main thread
return GetNumCaptureChannels() > 0 && return GetNumCaptureChannels() > 0 &&
mPlaybackSchedule.mTime >= mPlaybackSchedule.GetTrackTime() >=
mPlaybackSchedule.mT0 + mRecordingSchedule.mPreRoll; mPlaybackSchedule.mT0 + mRecordingSchedule.mPreRoll;
} }

View File

@@ -18,6 +18,7 @@
#include "Experimental.h" #include "Experimental.h"
#include "MemoryX.h" #include "MemoryX.h"
#include <atomic>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <wx/atomic.h> #include <wx/atomic.h>
@@ -832,8 +833,11 @@ private:
double mT0; double mT0;
/// Playback ends at offset of mT1, which is measured in seconds. Note that mT1 may be less than mT0 during scrubbing. /// Playback ends at offset of mT1, which is measured in seconds. Note that mT1 may be less than mT0 during scrubbing.
double mT1; double mT1;
/// Current time position during playback, in seconds. Between mT0 and mT1. /// Current track time position during playback, in seconds.
double mTime; /// Initialized by the main thread but updated by worker threads during
/// playback or recording, and periodically reread by the main thread for
/// purposes such as display update.
std::atomic<double> mTime;
/// Accumulated real time (not track position), starting at zero (unlike /// Accumulated real time (not track position), starting at zero (unlike
/// mTime), and wrapping back to zero each time around looping play. /// mTime), and wrapping back to zero each time around looping play.
@@ -877,19 +881,38 @@ private:
return mT1 < mT0; return mT1 < mT0;
} }
/** \brief Get current track time value, unadjusted
*
* Returns a time in seconds.
*/
double GetTrackTime() const
{ return mTime.load(std::memory_order_relaxed); }
/** \brief Set current track time value, unadjusted
*/
void SetTrackTime( double time )
{ mTime.store(time, std::memory_order_relaxed); }
/** \brief Clamps argument to be between mT0 and mT1
*
* Returns the bound if the value is out of bounds; does not wrap.
* Returns a time in seconds.
*/
double ClampTrackTime( double trackTime ) const;
/** \brief Clamps mTime to be between mT0 and mT1 /** \brief Clamps mTime to be between mT0 and mT1
* *
* Returns the bound if the value is out of bounds; does not wrap. * Returns the bound if the value is out of bounds; does not wrap.
* Returns a time in seconds. * Returns a time in seconds.
*/ */
double LimitStreamTime() const; double LimitTrackTime() const;
/** \brief Normalizes mTime, clamping it and handling gaps from cut preview. /** \brief Normalizes mTime, clamping it and handling gaps from cut preview.
* *
* Clamps the time (unless scrubbing), and skips over the cut section. * Clamps the time (unless scrubbing), and skips over the cut section.
* Returns a time in seconds. * Returns a time in seconds.
*/ */
double NormalizeStreamTime() const; double NormalizeTrackTime() const;
void ResetMode() { mPlayMode = PLAY_STRAIGHT; } void ResetMode() { mPlayMode = PLAY_STRAIGHT; }
@@ -903,6 +926,9 @@ private:
// is completed at the current value of mTime // is completed at the current value of mTime
bool PassIsComplete() const; bool PassIsComplete() const;
// Returns true if time equals t1 or is on opposite side of t1, to t0
bool Overruns( double trackTime ) const;
void TrackTimeUpdate(double realElapsed); void TrackTimeUpdate(double realElapsed);
// Convert a nonnegative real duration to an increment of track time // Convert a nonnegative real duration to an increment of track time
@@ -921,7 +947,7 @@ private:
// Determine starting duration within the first pass -- sometimes not // Determine starting duration within the first pass -- sometimes not
// zero // zero
void RealTimeInit(); void RealTimeInit( double trackTime );
void RealTimeRestart(); void RealTimeRestart();