1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-05-03 00:59:43 +02:00

Possibly correct some cases of clicky playback or corrupt recording...

... By reusing portaudio's ring buffer class, which has more safe
inter-thread synchronization with memory fences
This commit is contained in:
Paul Licameli 2018-03-17 23:41:27 -04:00
parent 7b658d7925
commit 6dca4f7859
3 changed files with 72 additions and 3 deletions

View File

@ -245,4 +245,10 @@
// Easy change of keystroke bindings for menu items // Easy change of keystroke bindings for menu items
#define EXPERIMENTAL_EASY_CHANGE_KEY_BINDINGS #define EXPERIMENTAL_EASY_CHANGE_KEY_BINDINGS
// PRL 17 Mar 2018
// Hoping to commit to use of this branch before 2.3.0 is out.
// Don't use our own RingBuffer class, but reuse PortAudio's which includes
// proper memory fences.
#undef EXPERIMENTAL_REWRITE_RING_BUFFER
#endif #endif

View File

@ -23,12 +23,45 @@
#include "RingBuffer.h" #include "RingBuffer.h"
#include "Experimental.h"
#undef REWRITE_RING_BUFFER
// For now enabling the rewrite only for Mac
#ifdef EXPERIMENTAL_REWRITE_RING_BUFFER
#define REWRITE_RING_BUFFER
#elif defined (__APPLE__)
#define REWRITE_RING_BUFFER
#endif
namespace {
size_t roundUp(size_t n)
{
#ifdef REWRITE_RING_BUFFER
// PaUtilRingBuffer requires a power of 2
if (n == 0)
return 1;
size_t prev = n;
for (size_t temp = n; temp != 0; prev = temp, temp = ((temp - 1) & temp))
;
return prev == n ? prev : prev << 1;
#else
return n;
#endif
}
}
RingBuffer::RingBuffer(sampleFormat format, size_t size) RingBuffer::RingBuffer(sampleFormat format, size_t size)
: mFormat{ format } : mFormat{ format }
, mBufferSize{ std::max<size_t>(size, 64) } , mBufferSize{ roundUp( std::max<size_t>(size, 64) ) }
, mBuffer{ mBufferSize, mFormat } , mBuffer{ mBufferSize, mFormat }
{ {
#ifdef REWRITE_RING_BUFFER
PaUtil_InitializeRingBuffer(
&mRingBuffer, SAMPLE_SIZE(format), mBufferSize, mBuffer.ptr() );
#endif
} }
RingBuffer::~RingBuffer() RingBuffer::~RingBuffer()
@ -37,7 +70,11 @@ RingBuffer::~RingBuffer()
size_t RingBuffer::Len() size_t RingBuffer::Len()
{ {
#ifdef REWRITE_RING_BUFFER
return (mEnd + mBufferSize - mStart) % mBufferSize; return (mEnd + mBufferSize - mStart) % mBufferSize;
#else
assert(false);
#endif
} }
// //
@ -46,12 +83,19 @@ size_t RingBuffer::Len()
size_t RingBuffer::AvailForPut() size_t RingBuffer::AvailForPut()
{ {
#ifdef REWRITE_RING_BUFFER
return PaUtil_GetRingBufferWriteAvailable(&mRingBuffer);
#else
return std::max<size_t>(mBufferSize - Len(), 4) - 4; return std::max<size_t>(mBufferSize - Len(), 4) - 4;
#endif
} }
size_t RingBuffer::Put(samplePtr buffer, sampleFormat format, size_t RingBuffer::Put(samplePtr buffer, sampleFormat format,
size_t samplesToCopy) size_t samplesToCopy)
{ {
#ifdef REWRITE_RING_BUFFER
return PaUtil_WriteRingBuffer(&mRingBuffer, buffer, samplesToCopy);
#else
samplesToCopy = std::min( samplesToCopy, AvailForPut() ); samplesToCopy = std::min( samplesToCopy, AvailForPut() );
auto src = buffer; auto src = buffer;
size_t copied = 0; size_t copied = 0;
@ -73,8 +117,11 @@ size_t RingBuffer::Put(samplePtr buffer, sampleFormat format,
mEnd = pos; mEnd = pos;
return copied; return copied;
#endif
} }
#if 0
// not used
size_t RingBuffer::Clear(sampleFormat format, size_t samplesToClear) size_t RingBuffer::Clear(sampleFormat format, size_t samplesToClear)
{ {
samplesToClear = std::min( samplesToClear, AvailForPut() ); samplesToClear = std::min( samplesToClear, AvailForPut() );
@ -95,6 +142,7 @@ size_t RingBuffer::Clear(sampleFormat format, size_t samplesToClear)
return cleared; return cleared;
} }
#endif
// //
// For the reader only: // For the reader only:
@ -102,12 +150,19 @@ size_t RingBuffer::Clear(sampleFormat format, size_t samplesToClear)
size_t RingBuffer::AvailForGet() size_t RingBuffer::AvailForGet()
{ {
#ifdef REWRITE_RING_BUFFER
return PaUtil_GetRingBufferReadAvailable(&mRingBuffer);
#else
return Len(); return Len();
#endif
} }
size_t RingBuffer::Get(samplePtr buffer, sampleFormat format, size_t RingBuffer::Get(samplePtr buffer, sampleFormat format,
size_t samplesToCopy) size_t samplesToCopy)
{ {
#ifdef REWRITE_RING_BUFFER
return PaUtil_ReadRingBuffer(&mRingBuffer, buffer, samplesToCopy);
#else
samplesToCopy = std::min( samplesToCopy, Len() ); samplesToCopy = std::min( samplesToCopy, Len() );
auto dest = buffer; auto dest = buffer;
size_t copied = 0; size_t copied = 0;
@ -126,13 +181,17 @@ size_t RingBuffer::Get(samplePtr buffer, sampleFormat format,
} }
return copied; return copied;
#endif
} }
size_t RingBuffer::Discard(size_t samplesToDiscard) size_t RingBuffer::Discard(size_t samplesToDiscard)
{ {
#ifdef REWRITE_RING_BUFFER
PaUtil_AdvanceRingBufferReadIndex(&mRingBuffer, samplesToDiscard);
#else
samplesToDiscard = std::min( samplesToDiscard, Len() ); samplesToDiscard = std::min( samplesToDiscard, Len() );
mStart = (mStart + samplesToDiscard) % mBufferSize; mStart = (mStart + samplesToDiscard) % mBufferSize;
#endif
return samplesToDiscard; return samplesToDiscard;
} }

View File

@ -13,6 +13,8 @@
#include "SampleFormat.h" #include "SampleFormat.h"
#include "../lib-src/portaudio-v19/src/common/pa_ringbuffer.h"
class RingBuffer { class RingBuffer {
public: public:
RingBuffer(sampleFormat format, size_t size); RingBuffer(sampleFormat format, size_t size);
@ -24,7 +26,7 @@ class RingBuffer {
size_t AvailForPut(); size_t AvailForPut();
size_t Put(samplePtr buffer, sampleFormat format, size_t samples); size_t Put(samplePtr buffer, sampleFormat format, size_t samples);
size_t Clear(sampleFormat format, size_t samples); // size_t Clear(sampleFormat format, size_t samples);
// //
// For the reader only: // For the reader only:
@ -42,6 +44,8 @@ class RingBuffer {
size_t mEnd { 0 }; size_t mEnd { 0 };
size_t mBufferSize; size_t mBufferSize;
SampleBuffer mBuffer; SampleBuffer mBuffer;
PaUtilRingBuffer mRingBuffer;
}; };
#endif /* __AUDACITY_RING_BUFFER__ */ #endif /* __AUDACITY_RING_BUFFER__ */