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:
105
src/AudioIO.cpp
105
src/AudioIO.cpp
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user