From 6dca4f785904fd870d6829dd51aa0c56803012dc Mon Sep 17 00:00:00 2001 From: Paul Licameli Date: Sat, 17 Mar 2018 23:41:27 -0400 Subject: [PATCH] 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 --- src/Experimental.h | 6 +++++ src/RingBuffer.cpp | 63 ++++++++++++++++++++++++++++++++++++++++++++-- src/RingBuffer.h | 6 ++++- 3 files changed, 72 insertions(+), 3 deletions(-) diff --git a/src/Experimental.h b/src/Experimental.h index 2fc7b1557..899168e5f 100644 --- a/src/Experimental.h +++ b/src/Experimental.h @@ -245,4 +245,10 @@ // Easy change of keystroke bindings for menu items #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 diff --git a/src/RingBuffer.cpp b/src/RingBuffer.cpp index de3eb08a5..b9272be5a 100644 --- a/src/RingBuffer.cpp +++ b/src/RingBuffer.cpp @@ -23,12 +23,45 @@ #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) : mFormat{ format } - , mBufferSize{ std::max(size, 64) } + , mBufferSize{ roundUp( std::max(size, 64) ) } , mBuffer{ mBufferSize, mFormat } { +#ifdef REWRITE_RING_BUFFER + PaUtil_InitializeRingBuffer( + &mRingBuffer, SAMPLE_SIZE(format), mBufferSize, mBuffer.ptr() ); +#endif } RingBuffer::~RingBuffer() @@ -37,7 +70,11 @@ RingBuffer::~RingBuffer() size_t RingBuffer::Len() { +#ifdef REWRITE_RING_BUFFER return (mEnd + mBufferSize - mStart) % mBufferSize; +#else + assert(false); +#endif } // @@ -46,12 +83,19 @@ size_t RingBuffer::Len() size_t RingBuffer::AvailForPut() { +#ifdef REWRITE_RING_BUFFER + return PaUtil_GetRingBufferWriteAvailable(&mRingBuffer); +#else return std::max(mBufferSize - Len(), 4) - 4; +#endif } size_t RingBuffer::Put(samplePtr buffer, sampleFormat format, size_t samplesToCopy) { +#ifdef REWRITE_RING_BUFFER + return PaUtil_WriteRingBuffer(&mRingBuffer, buffer, samplesToCopy); +#else samplesToCopy = std::min( samplesToCopy, AvailForPut() ); auto src = buffer; size_t copied = 0; @@ -73,8 +117,11 @@ size_t RingBuffer::Put(samplePtr buffer, sampleFormat format, mEnd = pos; return copied; +#endif } +#if 0 +// not used size_t RingBuffer::Clear(sampleFormat format, size_t samplesToClear) { samplesToClear = std::min( samplesToClear, AvailForPut() ); @@ -95,6 +142,7 @@ size_t RingBuffer::Clear(sampleFormat format, size_t samplesToClear) return cleared; } +#endif // // For the reader only: @@ -102,12 +150,19 @@ size_t RingBuffer::Clear(sampleFormat format, size_t samplesToClear) size_t RingBuffer::AvailForGet() { +#ifdef REWRITE_RING_BUFFER + return PaUtil_GetRingBufferReadAvailable(&mRingBuffer); +#else return Len(); +#endif } size_t RingBuffer::Get(samplePtr buffer, sampleFormat format, size_t samplesToCopy) { +#ifdef REWRITE_RING_BUFFER + return PaUtil_ReadRingBuffer(&mRingBuffer, buffer, samplesToCopy); +#else samplesToCopy = std::min( samplesToCopy, Len() ); auto dest = buffer; size_t copied = 0; @@ -126,13 +181,17 @@ size_t RingBuffer::Get(samplePtr buffer, sampleFormat format, } return copied; +#endif } size_t RingBuffer::Discard(size_t samplesToDiscard) { +#ifdef REWRITE_RING_BUFFER + PaUtil_AdvanceRingBufferReadIndex(&mRingBuffer, samplesToDiscard); +#else samplesToDiscard = std::min( samplesToDiscard, Len() ); mStart = (mStart + samplesToDiscard) % mBufferSize; - +#endif return samplesToDiscard; } diff --git a/src/RingBuffer.h b/src/RingBuffer.h index 6fc19bd42..4d72fde00 100644 --- a/src/RingBuffer.h +++ b/src/RingBuffer.h @@ -13,6 +13,8 @@ #include "SampleFormat.h" +#include "../lib-src/portaudio-v19/src/common/pa_ringbuffer.h" + class RingBuffer { public: RingBuffer(sampleFormat format, size_t size); @@ -24,7 +26,7 @@ class RingBuffer { size_t AvailForPut(); 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: @@ -42,6 +44,8 @@ class RingBuffer { size_t mEnd { 0 }; size_t mBufferSize; SampleBuffer mBuffer; + + PaUtilRingBuffer mRingBuffer; }; #endif /* __AUDACITY_RING_BUFFER__ */