1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-16 16:10:06 +02:00

Play head is green during pre-roll, then turns red for real recording

This commit is contained in:
Paul Licameli 2018-06-09 14:19:29 -04:00
parent e40420ba72
commit 8e51391b98
3 changed files with 46 additions and 17 deletions

View File

@ -1934,6 +1934,7 @@ int AudioIO::StartStream(const TransportTracks &tracks,
mT0 = t0 - preRoll; mT0 = t0 - preRoll;
mT1 = t1; mT1 = t1;
mRecordingSchedule = {}; mRecordingSchedule = {};
mRecordingPosition.store(0.0, std::memory_order_relaxed);
mRecordingSchedule.mPreRoll = preRoll; mRecordingSchedule.mPreRoll = preRoll;
mRecordingSchedule.mLatencyCorrection = mRecordingSchedule.mLatencyCorrection =
(gPrefs->ReadDouble(wxT("/AudioIO/LatencyCorrection"), (gPrefs->ReadDouble(wxT("/AudioIO/LatencyCorrection"),
@ -2564,6 +2565,7 @@ void AudioIO::StopStream()
auto cleanup = finally ( [this] { auto cleanup = finally ( [this] {
ClearRecordingException(); ClearRecordingException();
mRecordingSchedule = {}; // free arrays mRecordingSchedule = {}; // free arrays
mRecordingPosition.store(0.0, std::memory_order_relaxed);
} ); } );
if( mPortStreamV19 == NULL if( mPortStreamV19 == NULL
@ -3989,8 +3991,9 @@ void AudioIO::FillBuffers()
GuardedCall( [&] { GuardedCall( [&] {
// start record buffering // start record buffering
const auto avail = GetCommonlyAvailCapture(); // samples const auto avail = GetCommonlyAvailCapture(); // samples
const auto position = mRecordingPosition.load(std::memory_order_relaxed);
const auto remainingTime = const auto remainingTime =
std::max(0.0, mRecordingSchedule.ToConsume()); std::max(0.0, mRecordingSchedule.ToConsume(position));
// This may be a very big double number: // This may be a very big double number:
const auto remainingSamples = remainingTime * mRate; const auto remainingSamples = remainingTime * mRate;
bool latencyCorrected = true; bool latencyCorrected = true;
@ -4028,7 +4031,7 @@ void AudioIO::FillBuffers()
// Leftward shift // Leftward shift
// discard some samples from the ring buffers. // discard some samples from the ring buffers.
size_t size = floor( size_t size = floor(
mRecordingSchedule.ToDiscard() * mRate ); mRecordingSchedule.ToDiscard(position) * mRate );
// The ring buffer might have grown concurrently -- don't discard more // The ring buffer might have grown concurrently -- don't discard more
// than the "avail" value noted above. // than the "avail" value noted above.
@ -4051,7 +4054,8 @@ void AudioIO::FillBuffers()
totalCrossfadeLength = data.size(); totalCrossfadeLength = data.size();
if (totalCrossfadeLength) { if (totalCrossfadeLength) {
crossfadeStart = crossfadeStart =
floor(mRecordingSchedule.Consumed() * mCaptureTracks[i]->GetRate()); floor(mRecordingSchedule.Consumed(position) *
mCaptureTracks[i]->GetRate());
if (crossfadeStart < totalCrossfadeLength) if (crossfadeStart < totalCrossfadeLength)
pCrossfadeSrc = data.data() + crossfadeStart; pCrossfadeSrc = data.data() + crossfadeStart;
} }
@ -4139,8 +4143,13 @@ void AudioIO::FillBuffers()
} }
} // end loop over capture channels } // end loop over capture channels
// Now update the recording shedule position // Now update the recording schedule position
mRecordingSchedule.mPosition += avail / mRate; // Main thread may need to read it for drawing, so update atomically
// Relaxed order here, with the frequent memory fencing done by
// the ring buffers, should give frequent enough update in the
// main thread.
mRecordingPosition.store(
position + avail / mRate, std::memory_order_relaxed);
mRecordingSchedule.mLatencyCorrected = latencyCorrected; mRecordingSchedule.mLatencyCorrected = latencyCorrected;
if (mListener && !blockFileLog.IsEmpty()) if (mListener && !blockFileLog.IsEmpty())
@ -5463,17 +5472,29 @@ int audacityAudioCallback(const void *inputBuffer, void *outputBuffer,
return callbackReturn; return callbackReturn;
} }
double AudioIO::RecordingSchedule::ToConsume() const double AudioIO::RecordingSchedule::ToConsume(double position) const
{ {
return mDuration - Consumed(); return mDuration - Consumed(position);
} }
double AudioIO::RecordingSchedule::Consumed() const double AudioIO::RecordingSchedule::Consumed(double position) const
{ {
return std::max( 0.0, mPosition + TotalCorrection() ); return std::max( 0.0, position + TotalCorrection() );
} }
double AudioIO::RecordingSchedule::ToDiscard() const double AudioIO::RecordingSchedule::ToDiscard(double position) const
{ {
return std::max(0.0, -( mPosition + TotalCorrection() ) ); return std::max(0.0, -( position + TotalCorrection() ) );
}
bool AudioIO::IsCapturing() const
{
// This is for purposes of choosing the color of the play indicator
// Returns false during a pre-roll but true when time before actual recording
// is less than latency
return
GetNumCaptureChannels() > 0 &&
mRecordingSchedule.ToDiscard(
mRecordingPosition.load(std::memory_order_relaxed)) <=
-std::min(0.0, mRecordingSchedule.mLatencyCorrection);
} }

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>
@ -398,6 +399,9 @@ class AUDACITY_DLL_API AudioIO final {
unsigned GetNumPlaybackChannels() const { return mNumPlaybackChannels; } unsigned GetNumPlaybackChannels() const { return mNumPlaybackChannels; }
unsigned GetNumCaptureChannels() const { return mNumCaptureChannels; } unsigned GetNumCaptureChannels() const { return mNumCaptureChannels; }
// Meaning really capturing, not just pre-rolling
bool IsCapturing() const;
/** \brief Array of common audio sample rates /** \brief Array of common audio sample rates
* *
* These are the rates we will always support, regardless of hardware support * These are the rates we will always support, regardless of hardware support
@ -846,16 +850,20 @@ private:
double mDuration{}; double mDuration{};
PRCrossfadeData mCrossfadeData; PRCrossfadeData mCrossfadeData;
// These are initialized by the main thread, then updated // This is initialized by the main thread, then updated
// only by the thread calling FillBuffers: // only by the thread calling FillBuffers:
double mPosition{};
bool mLatencyCorrected{}; bool mLatencyCorrected{};
double TotalCorrection() const { return mLatencyCorrection - mPreRoll; } double TotalCorrection() const { return mLatencyCorrection - mPreRoll; }
double ToConsume() const; double ToConsume(double position) const;
double Consumed() const; double Consumed(double position) const;
double ToDiscard() const; double ToDiscard(double position) const;
} mRecordingSchedule{}; } mRecordingSchedule{};
// This is initialized by the main thread, then updated
// only by the thread calling FillBuffers, but may need to be read by the
// main thread to draw display correctly.
std::atomic<double> mRecordingPosition{};
}; };
#endif #endif

View File

@ -60,7 +60,7 @@ std::pair<wxRect, bool> PlayIndicatorOverlayBase::DoGetRectangle(wxSize size)
void PlayIndicatorOverlayBase::Draw(OverlayPanel &panel, wxDC &dc) void PlayIndicatorOverlayBase::Draw(OverlayPanel &panel, wxDC &dc)
{ {
// Set play/record color // Set play/record color
bool rec = (gAudioIO->GetNumCaptureChannels() > 0); bool rec = gAudioIO->IsCapturing();
AColor::IndicatorColor(&dc, !rec); AColor::IndicatorColor(&dc, !rec);
mLastIndicatorX = mNewIndicatorX; mLastIndicatorX = mNewIndicatorX;
if (!between_incexc(0, mLastIndicatorX, dc.GetSize().GetWidth())) if (!between_incexc(0, mLastIndicatorX, dc.GetSize().GetWidth()))