mirror of
https://github.com/cookiengineer/audacity
synced 2025-10-21 14:02:57 +02:00
Fewer calls to RingBuffer::Put in case of trailing zeroes...
... so there is only one update per track of the atomics in RingBuffer in each pass of the loop in FillBuffers, which will be needed to synchronize RingBuffer and TimeQueue correctly.
This commit is contained in:
@@ -3987,10 +3987,12 @@ void AudioIO::FillBuffers()
|
||||
// How many samples to produce for each channel.
|
||||
auto frames = available;
|
||||
bool progress = true;
|
||||
auto toProcess = frames;
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_SUPPORT
|
||||
if (mPlaybackSchedule.Interactive())
|
||||
// scrubbing and play-at-speed are not limited by the real time
|
||||
// and length accumulators
|
||||
toProcess =
|
||||
frames = limitSampleBufferSize(frames, mScrubDuration);
|
||||
else
|
||||
#endif
|
||||
@@ -3999,6 +4001,7 @@ void AudioIO::FillBuffers()
|
||||
if (deltat > realTimeRemaining)
|
||||
{
|
||||
frames = realTimeRemaining * mRate;
|
||||
toProcess = frames;
|
||||
// Don't fall into an infinite loop, if loop-playing a selection
|
||||
// that is so short, it has no samples: detect that case
|
||||
progress =
|
||||
@@ -4012,53 +4015,29 @@ void AudioIO::FillBuffers()
|
||||
}
|
||||
|
||||
if (!progress)
|
||||
frames = available;
|
||||
frames = available, toProcess = 0;
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_SUPPORT
|
||||
else if ( mPlaybackSchedule.Interactive() && mSilentScrub)
|
||||
toProcess = 0;
|
||||
#endif
|
||||
|
||||
for (i = 0; i < mPlaybackTracks.size(); i++)
|
||||
{
|
||||
// The mixer here isn't actually mixing: it's just doing
|
||||
// resampling, format conversion, and possibly time track
|
||||
// warping
|
||||
decltype(mPlaybackMixers[i]->Process(frames))
|
||||
processed = 0;
|
||||
samplePtr warpedSamples;
|
||||
//don't do anything if we have no length. In particular, Process() will fail an wxAssert
|
||||
//that causes a crash since this is not the GUI thread and wxASSERT is a GUI call.
|
||||
|
||||
// don't generate either if scrubbing at zero speed.
|
||||
#ifdef EXPERIMENTAL_SCRUBBING_SUPPORT
|
||||
const bool silent =
|
||||
mPlaybackSchedule.Interactive() && mSilentScrub;
|
||||
#else
|
||||
const bool silent = false;
|
||||
#endif
|
||||
|
||||
if (progress && !silent && frames > 0)
|
||||
if (frames > 0)
|
||||
{
|
||||
processed = mPlaybackMixers[i]->Process(frames);
|
||||
wxASSERT(processed <= frames);
|
||||
size_t processed = 0;
|
||||
if ( toProcess )
|
||||
processed = mPlaybackMixers[i]->Process( toProcess );
|
||||
//wxASSERT(processed <= toProcess);
|
||||
warpedSamples = mPlaybackMixers[i]->GetBuffer();
|
||||
const auto put = mPlaybackBuffers[i]->Put
|
||||
(warpedSamples, floatSample, processed);
|
||||
// wxASSERT(put == processed);
|
||||
// but we can't assert in this thread
|
||||
wxUnusedVar(put);
|
||||
}
|
||||
|
||||
//if looping and processed is less than the full chunk/block/buffer that gets pulled from
|
||||
//other longer tracks, then we still need to advance the ring buffers or
|
||||
//we'll trip up on ourselves when we start them back up again.
|
||||
//if not looping we never start them up again, so its okay to not do anything
|
||||
// If scrubbing, we may be producing some silence. Otherwise this should not happen,
|
||||
// but makes sure anyway that we produce equal
|
||||
// numbers of samples for all channels for this pass of the do-loop.
|
||||
if(processed < frames && !mPlaybackSchedule.PlayingStraight())
|
||||
{
|
||||
mSilentBuf.Resize(frames, floatSample);
|
||||
ClearSamples(mSilentBuf.ptr(), floatSample, 0, frames);
|
||||
const auto put = mPlaybackBuffers[i]->Put
|
||||
(mSilentBuf.ptr(), floatSample, frames - processed);
|
||||
// wxASSERT(put == frames - processed);
|
||||
const auto put = mPlaybackBuffers[i]->Put(
|
||||
warpedSamples, floatSample, processed, frames - processed);
|
||||
// wxASSERT(put == frames);
|
||||
// but we can't assert in this thread
|
||||
wxUnusedVar(put);
|
||||
}
|
||||
|
@@ -778,8 +778,6 @@ private:
|
||||
bool mInputMixerWorks;
|
||||
float mMixerOutputVol;
|
||||
|
||||
GrowableSampleBuffer mSilentBuf;
|
||||
|
||||
AudioIOListener* mListener;
|
||||
|
||||
friend class AudioThread;
|
||||
|
@@ -69,16 +69,18 @@ size_t RingBuffer::AvailForPut()
|
||||
}
|
||||
|
||||
size_t RingBuffer::Put(samplePtr buffer, sampleFormat format,
|
||||
size_t samplesToCopy)
|
||||
size_t samplesToCopy, size_t padding)
|
||||
{
|
||||
auto start = mStart.load( std::memory_order_acquire );
|
||||
auto end = mEnd.load( std::memory_order_relaxed );
|
||||
samplesToCopy = std::min( samplesToCopy, Free( start, end ) );
|
||||
const auto free = Free( start, end );
|
||||
samplesToCopy = std::min( samplesToCopy, free );
|
||||
padding = std::min( padding, free - samplesToCopy );
|
||||
auto src = buffer;
|
||||
size_t copied = 0;
|
||||
auto pos = end;
|
||||
|
||||
while(samplesToCopy) {
|
||||
while ( samplesToCopy ) {
|
||||
auto block = std::min( samplesToCopy, mBufferSize - pos );
|
||||
|
||||
CopySamples(src, format,
|
||||
@@ -91,6 +93,14 @@ size_t RingBuffer::Put(samplePtr buffer, sampleFormat format,
|
||||
copied += block;
|
||||
}
|
||||
|
||||
while ( padding ) {
|
||||
const auto block = std::min( padding, mBufferSize - pos );
|
||||
ClearSamples( mBuffer.ptr(), mFormat, pos, block );
|
||||
pos = (pos + block) % mBufferSize;
|
||||
padding -= block;
|
||||
copied += block;
|
||||
}
|
||||
|
||||
// Atomically update the end pointer with release, so the nonatomic writes
|
||||
// just done to the buffer don't get reordered after
|
||||
mEnd.store(pos, std::memory_order_release);
|
||||
|
@@ -24,7 +24,9 @@ class RingBuffer {
|
||||
//
|
||||
|
||||
size_t AvailForPut();
|
||||
size_t Put(samplePtr buffer, sampleFormat format, size_t samples);
|
||||
size_t Put(samplePtr buffer, sampleFormat format, size_t samples,
|
||||
// optional number of trailing zeroes
|
||||
size_t padding = 0);
|
||||
size_t Clear(sampleFormat format, size_t samples);
|
||||
|
||||
//
|
||||
|
Reference in New Issue
Block a user