Audacity 3.2.0
Public Member Functions | Private Member Functions | Private Attributes | List of all members
RingBuffer Class Referencefinal

Holds streamed audio samples. More...

#include <RingBuffer.h>

Inheritance diagram for RingBuffer:
[legend]
Collaboration diagram for RingBuffer:
[legend]

Public Member Functions

 RingBuffer (sampleFormat format, size_t size)
 
 ~RingBuffer ()
 
size_t AvailForPut () const
 
size_t WrittenForGet () const
 Reader may concurrently cause a decrease of what this returns. More...
 
size_t Put (constSamplePtr buffer, sampleFormat format, size_t samples, size_t padding=0)
 Does not apply dithering. More...
 
size_t Unput (size_t size)
 Remove an initial segment of data that has been Put but not Flushed yet. More...
 
size_t Clear (sampleFormat format, size_t samples)
 
std::pair< samplePtr, size_t > GetUnflushed (unsigned iBlock)
 
void Flush ()
 Flush after a sequence of Put (and/or Clear) calls to let consumer see. More...
 
size_t AvailForGet () const
 
size_t Get (samplePtr buffer, sampleFormat format, size_t samples)
 Does not apply dithering. More...
 
size_t Discard (size_t samples)
 

Private Member Functions

size_t Filled (size_t start, size_t end) const
 
size_t Free (size_t start, size_t end) const
 

Private Attributes

size_t mWritten {0}
 
size_t mLastPadding {0}
 
NonInterfering< std::atomic< size_t > > mStart { 0 }
 
NonInterfering< std::atomic< size_t > > mEnd { 0 }
 
const size_t mBufferSize
 
const sampleFormat mFormat
 
const SampleBuffer mBuffer
 

Detailed Description

Holds streamed audio samples.

Assuming that there is only one thread writing, and one thread reading, this class implements a lock-free thread-safe bounded queue of samples with atomic variables that contain the first filled and free positions.

If two threads both need to read, or both need to write, they need to lock this class from outside using their own mutex.

AvailForPut and AvailForGet may underestimate but will never overestimate.

Definition at line 17 of file RingBuffer.h.

Constructor & Destructor Documentation

◆ RingBuffer()

RingBuffer::RingBuffer ( sampleFormat  format,
size_t  size 
)

Definition at line 32 of file RingBuffer.cpp.

33 : mBufferSize{ std::max<size_t>(size, 64) }
34 , mFormat{ format }
36{
37}
const size_t mBufferSize
Definition: RingBuffer.h:64
const sampleFormat mFormat
Definition: RingBuffer.h:66
const SampleBuffer mBuffer
Definition: RingBuffer.h:67

◆ ~RingBuffer()

RingBuffer::~RingBuffer ( )

Definition at line 39 of file RingBuffer.cpp.

40{
41}

Member Function Documentation

◆ AvailForGet()

size_t RingBuffer::AvailForGet ( ) const

Definition at line 226 of file RingBuffer.cpp.

227{
228 auto end = mEnd.load( std::memory_order_relaxed ); // get away with it here
229 auto start = mStart.load( std::memory_order_relaxed );
230 return Filled( start, end );
231
232 // Writer might increase the available samples after return, but will
233 // never decrease them, so reader can safely assume this much at least
234}
NonInterfering< std::atomic< size_t > > mEnd
Definition: RingBuffer.h:62
size_t Filled(size_t start, size_t end) const
Definition: RingBuffer.cpp:46
NonInterfering< std::atomic< size_t > > mStart
Definition: RingBuffer.h:62
const char * end(const char *str) noexcept
Definition: StringUtils.h:106

References details::end(), Filled(), mEnd, and mStart.

Referenced by AudioIO::GetCommonlyAvailCapture(), and AudioIoCallback::GetCommonlyReadyPlayback().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ AvailForPut()

size_t RingBuffer::AvailForPut ( ) const

Definition at line 64 of file RingBuffer.cpp.

65{
66 auto start = mStart.load( std::memory_order_relaxed );
67 return Free( start, mWritten );
68
69 // Reader might increase the available free space after return, but will
70 // never decrease it, so writer can safely assume this much at least
71}
size_t Free(size_t start, size_t end) const
Definition: RingBuffer.cpp:51
size_t mWritten
Definition: RingBuffer.h:58

References Free(), mStart, and mWritten.

Referenced by AudioIO::GetCommonlyFreePlayback().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ Clear()

size_t RingBuffer::Clear ( sampleFormat  format,
size_t  samples 
)

Definition at line 164 of file RingBuffer.cpp.

165{
166 auto start = mStart.load( std::memory_order_acquire );
167 auto end = mWritten;
168 samplesToClear = std::min( samplesToClear, Free( start, end ) );
169 size_t cleared = 0;
170 auto pos = end;
171
172 while(samplesToClear) {
173 auto block = std::min( samplesToClear, mBufferSize - pos );
174
175 ClearSamples(mBuffer.ptr(), format, pos, block);
176
177 pos = (pos + block) % mBufferSize;
178 samplesToClear -= block;
179 cleared += block;
180 }
181
182 mWritten = pos;
183
184 return cleared;
185}
int min(int a, int b)
void ClearSamples(samplePtr dst, sampleFormat format, size_t start, size_t len)
samplePtr ptr() const
Definition: SampleFormat.h:165

References ClearSamples(), details::end(), anonymous_namespace{ExportPCM.cpp}::format, Free(), mBuffer, mBufferSize, min(), mStart, mWritten, and SampleBuffer::ptr().

Here is the call graph for this function:

◆ Discard()

size_t RingBuffer::Discard ( size_t  samples)

Definition at line 267 of file RingBuffer.cpp.

268{
269 auto end = mEnd.load( std::memory_order_relaxed ); // get away with it here
270 auto start = mStart.load( std::memory_order_relaxed );
271 samplesToDiscard = std::min( samplesToDiscard, Filled( start, end ) );
272
273 // Communicate to writer that we have skipped some data, and that's all
274 mStart.store((start + samplesToDiscard) % mBufferSize,
275 std::memory_order_relaxed);
276
277 return samplesToDiscard;
278}

References details::end(), Filled(), mBufferSize, mEnd, min(), and mStart.

Here is the call graph for this function:

◆ Filled()

size_t RingBuffer::Filled ( size_t  start,
size_t  end 
) const
private

Definition at line 46 of file RingBuffer.cpp.

47{
48 return (end + mBufferSize - start) % mBufferSize;
49}

References details::end(), and mBufferSize.

Referenced by AvailForGet(), Discard(), Free(), Get(), GetUnflushed(), Unput(), and WrittenForGet().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ Flush()

void RingBuffer::Flush ( )

Flush after a sequence of Put (and/or Clear) calls to let consumer see.

Definition at line 210 of file RingBuffer.cpp.

211{
212 // Atomically update the end pointer with release, so the nonatomic writes
213 // just done to the buffer don't get reordered after
214 mEnd.store(mWritten, std::memory_order_release);
215 mLastPadding = 0;
216}
size_t mLastPadding
Definition: RingBuffer.h:59

References mEnd, mLastPadding, and mWritten.

◆ Free()

size_t RingBuffer::Free ( size_t  start,
size_t  end 
) const
private

Definition at line 51 of file RingBuffer.cpp.

52{
53 return std::max<size_t>(mBufferSize - Filled( start, end ), 4) - 4;
54}

References details::end(), Filled(), and mBufferSize.

Referenced by AvailForPut(), Clear(), and Put().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ Get()

size_t RingBuffer::Get ( samplePtr  buffer,
sampleFormat  format,
size_t  samples 
)

Does not apply dithering.

Definition at line 236 of file RingBuffer.cpp.

238{
239 // Must match the writer's release with acquire for well defined reads of
240 // the buffer
241 auto end = mEnd.load( std::memory_order_acquire );
242 auto start = mStart.load( std::memory_order_relaxed );
243 samplesToCopy = std::min( samplesToCopy, Filled( start, end ) );
244 auto dest = buffer;
245 size_t copied = 0;
246
247 while(samplesToCopy) {
248 auto block = std::min( samplesToCopy, mBufferSize - start );
249
251 dest, format,
252 block, DitherType::none);
253
254 dest += block * SAMPLE_SIZE(format);
255 start = (start + block) % mBufferSize;
256 samplesToCopy -= block;
257 copied += block;
258 }
259
260 // Communicate to writer that we have consumed some data,
261 // with nonrelaxed ordering
262 mStart.store( start, std::memory_order_release );
263
264 return copied;
265}
@ none
Definition: Dither.h:20
void CopySamples(constSamplePtr src, sampleFormat srcFormat, samplePtr dst, sampleFormat dstFormat, size_t len, DitherType ditherType, unsigned int srcStride, unsigned int dstStride)
Copy samples from any format to any other format; apply dithering only if narrowing the format.
#define SAMPLE_SIZE(SampleFormat)
Definition: SampleFormat.h:52

References CopySamples(), details::end(), Filled(), anonymous_namespace{ExportPCM.cpp}::format, mBuffer, mBufferSize, mEnd, mFormat, min(), mStart, none, SampleBuffer::ptr(), and SAMPLE_SIZE.

Here is the call graph for this function:

◆ GetUnflushed()

std::pair< samplePtr, size_t > RingBuffer::GetUnflushed ( unsigned  iBlock)

Get access to written but unflushed data, which is in at most two blocks Excludes the padding of the most recent Put()

Definition at line 187 of file RingBuffer.cpp.

188{
189 // This function is called by the writer
190
191 // Find total number of samples unflushed:
192 auto end = mEnd.load(std::memory_order_relaxed);
193 const size_t size = Filled(end, mWritten) - mLastPadding;
194
195 // How many in the first part:
196 const size_t size0 = std::min(size, mBufferSize - end);
197 // How many wrap around the ring buffer:
198 const size_t size1 = size - size0;
199
200 if (iBlock == 0)
201 return {
202 size0 ? mBuffer.ptr() + end * SAMPLE_SIZE(mFormat) : nullptr,
203 size0 };
204 else
205 return {
206 size1 ? mBuffer.ptr() : nullptr,
207 size1 };
208}

References details::end(), Filled(), mBuffer, mBufferSize, mEnd, mFormat, min(), mLastPadding, mWritten, SampleBuffer::ptr(), SAMPLE_SIZE, and size.

Here is the call graph for this function:

◆ Put()

size_t RingBuffer::Put ( constSamplePtr  buffer,
sampleFormat  format,
size_t  samples,
size_t  padding = 0 
)

Does not apply dithering.

Definition at line 79 of file RingBuffer.cpp.

81{
82 mLastPadding = padding;
83 auto start = mStart.load( std::memory_order_acquire );
84 auto end = mWritten;
85 const auto free = Free( start, end );
86 samplesToCopy = std::min( samplesToCopy, free );
87 padding = std::min( padding, free - samplesToCopy );
88 auto src = buffer;
89 size_t copied = 0;
90 auto pos = end;
91
92 while ( samplesToCopy ) {
93 auto block = std::min( samplesToCopy, mBufferSize - pos );
94
97 block, DitherType::none);
98
99 src += block * SAMPLE_SIZE(format);
100 pos = (pos + block) % mBufferSize;
101 samplesToCopy -= block;
102 copied += block;
103 }
104
105 while ( padding ) {
106 const auto block = std::min( padding, mBufferSize - pos );
107 ClearSamples( mBuffer.ptr(), mFormat, pos, block );
108 pos = (pos + block) % mBufferSize;
109 padding -= block;
110 copied += block;
111 }
112
113 mWritten = pos;
114 return copied;
115}
void free(void *ptr)
Definition: VectorOps.h:34

References ClearSamples(), CopySamples(), details::end(), anonymous_namespace{ExportPCM.cpp}::format, Free(), staffpad::vo::free(), mBuffer, mBufferSize, mFormat, min(), mLastPadding, mStart, mWritten, none, SampleBuffer::ptr(), and SAMPLE_SIZE.

Here is the call graph for this function:

◆ Unput()

size_t RingBuffer::Unput ( size_t  size)

Remove an initial segment of data that has been Put but not Flushed yet.

Returns
how many were unput

Definition at line 117 of file RingBuffer.cpp.

118{
119 const auto sampleSize = SAMPLE_SIZE(mFormat);
120 const auto buffer = mBuffer.ptr();
121
122 // un-put some of the un-flushed data which is from mEnd to mWritten
123 // bound the result
124 auto end = mEnd.load(std::memory_order_relaxed);
126 const auto result = size;
127
128 // First memmove
129 auto limit = end < mWritten ? mWritten : mBufferSize;
130 // Source offset for move
131 auto source = std::min(end + size, limit);
132 // How many to move
133 auto count = limit - source;
134 auto pDst = buffer + end * sampleSize;
135 auto pSrc = buffer + source * sampleSize;
136 memmove(pDst, pSrc, count * sampleSize);
137 // Discount how many really discarded
138 size -= (source - end);
139
140 if (end >= mWritten) {
141 // The unflushed data were wrapped around, not contiguous
142 end += count;
143 auto pDst = buffer + end * sampleSize;
144 // Rotate some samples from start of buffer, but discarding
145 // any remaining number that must be un-put
146 // Then shift samples near the start of buffer
147 pSrc = buffer + size * sampleSize;
148 auto toMove = mWritten - size;
149 auto toMove1 = std::min(toMove, mBufferSize - end);
150 auto toMove2 = toMove - toMove1;
151 memmove(pDst, pSrc, toMove1 * sampleSize);
152 memmove(buffer, pSrc + toMove1 * sampleSize, toMove2 * sampleSize);
153 }
154
155 // Move mWritten backwards by result
156 mWritten = (mWritten + (mBufferSize - result)) % mBufferSize;
157
158 // Adjust mLastPadding
160
161 return result;
162}

References details::end(), Filled(), mBuffer, mBufferSize, mEnd, mFormat, min(), mLastPadding, mWritten, SampleBuffer::ptr(), SAMPLE_SIZE, and size.

Here is the call graph for this function:

◆ WrittenForGet()

size_t RingBuffer::WrittenForGet ( ) const

Reader may concurrently cause a decrease of what this returns.

Definition at line 73 of file RingBuffer.cpp.

74{
75 auto start = mStart.load( std::memory_order_relaxed );
76 return Filled( start, mWritten );
77}

References Filled(), mStart, and mWritten.

Referenced by AudioIoCallback::GetCommonlyWrittenForPlayback().

Here is the call graph for this function:
Here is the caller graph for this function:

Member Data Documentation

◆ mBuffer

const SampleBuffer RingBuffer::mBuffer
private

Definition at line 67 of file RingBuffer.h.

Referenced by Clear(), Get(), GetUnflushed(), Put(), and Unput().

◆ mBufferSize

const size_t RingBuffer::mBufferSize
private

Definition at line 64 of file RingBuffer.h.

Referenced by Clear(), Discard(), Filled(), Free(), Get(), GetUnflushed(), Put(), and Unput().

◆ mEnd

NonInterfering< std::atomic<size_t> > RingBuffer::mEnd { 0 }
private

Definition at line 62 of file RingBuffer.h.

Referenced by AvailForGet(), Discard(), Flush(), Get(), GetUnflushed(), and Unput().

◆ mFormat

const sampleFormat RingBuffer::mFormat
private

Definition at line 66 of file RingBuffer.h.

Referenced by Get(), GetUnflushed(), Put(), and Unput().

◆ mLastPadding

size_t RingBuffer::mLastPadding {0}
private

Definition at line 59 of file RingBuffer.h.

Referenced by Flush(), GetUnflushed(), Put(), and Unput().

◆ mStart

NonInterfering< std::atomic<size_t> > RingBuffer::mStart { 0 }
private

Definition at line 62 of file RingBuffer.h.

Referenced by AvailForGet(), AvailForPut(), Clear(), Discard(), Get(), Put(), and WrittenForGet().

◆ mWritten

size_t RingBuffer::mWritten {0}
private

Definition at line 58 of file RingBuffer.h.

Referenced by AvailForPut(), Clear(), Flush(), GetUnflushed(), Put(), Unput(), and WrittenForGet().


The documentation for this class was generated from the following files: