Audacity 3.2.0
RingBuffer.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 RingBuffer.cpp
6
7 Dominic Mazzoni
8 Paul Licameli
9
10*******************************************************************//*******************************************************************/
26
27
28#include "RingBuffer.h"
29#include "Dither.h"
30
32 : mBufferSize{ std::max<size_t>(size, 64) }
33 , mFormat{ format }
34 , mBuffer{ mBufferSize, mFormat }
35{
36}
37
39{
40}
41
42// Calculations of free and filled space, given snapshots taken of the start
43// and end values
44
45size_t RingBuffer::Filled( size_t start, size_t end )
46{
47 return (end + mBufferSize - start) % mBufferSize;
48}
49
50size_t RingBuffer::Free( size_t start, size_t end )
51{
52 return std::max<size_t>(mBufferSize - Filled( start, end ), 4) - 4;
53}
54
55//
56// For the writer only:
57// Only writer reads or writes mWritten
58// Only writer writes the end, so it can read it again relaxed
59// And it reads the start written by reader, with acquire order,
60// so that any reading done in Get() happens-before any reuse of the space.
61//
62
64{
65 auto start = mStart.load( std::memory_order_relaxed );
66 return Free( start, mWritten );
67
68 // Reader might increase the available free space after return, but will
69 // never decrease it, so writer can safely assume this much at least
70}
71
73 size_t samplesToCopy, size_t padding)
74{
75 auto start = mStart.load( std::memory_order_acquire );
76 auto end = mWritten;
77 const auto free = Free( start, end );
78 samplesToCopy = std::min( samplesToCopy, free );
79 padding = std::min( padding, free - samplesToCopy );
80 auto src = buffer;
81 size_t copied = 0;
82 auto pos = end;
83
84 while ( samplesToCopy ) {
85 auto block = std::min( samplesToCopy, mBufferSize - pos );
86
89 block, DitherType::none);
90
91 src += block * SAMPLE_SIZE(format);
92 pos = (pos + block) % mBufferSize;
93 samplesToCopy -= block;
94 copied += block;
95 }
96
97 while ( padding ) {
98 const auto block = std::min( padding, mBufferSize - pos );
99 ClearSamples( mBuffer.ptr(), mFormat, pos, block );
100 pos = (pos + block) % mBufferSize;
101 padding -= block;
102 copied += block;
103 }
104
105 mWritten = pos;
106
107 return copied;
108}
109
110size_t RingBuffer::Clear(sampleFormat format, size_t samplesToClear)
111{
112 auto start = mStart.load( std::memory_order_acquire );
113 auto end = mWritten;
114 samplesToClear = std::min( samplesToClear, Free( start, end ) );
115 size_t cleared = 0;
116 auto pos = end;
117
118 while(samplesToClear) {
119 auto block = std::min( samplesToClear, mBufferSize - pos );
120
121 ClearSamples(mBuffer.ptr(), format, pos, block);
122
123 pos = (pos + block) % mBufferSize;
124 samplesToClear -= block;
125 cleared += block;
126 }
127
128 mWritten = pos;
129
130 return cleared;
131}
132
133std::pair<samplePtr, size_t> RingBuffer::GetUnflushed(unsigned iBlock)
134{
135 // This function is called by the writer
136
137 // Find total number of samples unflushed:
138 auto end = mEnd.load(std::memory_order_relaxed);
139 const size_t size = Filled(end, mWritten);
140
141 // How many in the first part:
142 const size_t size0 = std::min(size, mBufferSize - end);
143 // How many wrap around the ring buffer:
144 const size_t size1 = size - size0;
145
146 if (iBlock == 0)
147 return {
148 size0 ? mBuffer.ptr() + end * SAMPLE_SIZE(mFormat) : nullptr,
149 size0 };
150 else
151 return {
152 size1 ? mBuffer.ptr() : nullptr,
153 size1 };
154}
155
157{
158 // Atomically update the end pointer with release, so the nonatomic writes
159 // just done to the buffer don't get reordered after
160 mEnd.store(mWritten, std::memory_order_release);
161}
162
163//
164// For the reader only:
165// Only reader writes the start, so it can read it again relaxed
166// But it reads the end written by the writer, who also sends sample data
167// with the changes of end; therefore that must be read with acquire order
168// if we do more than merely query the size or throw samples away
169//
170
172{
173 auto end = mEnd.load( std::memory_order_relaxed ); // get away with it here
174 auto start = mStart.load( std::memory_order_relaxed );
175 return Filled( start, end );
176
177 // Writer might increase the available samples after return, but will
178 // never decrease them, so reader can safely assume this much at least
179}
180
182 size_t samplesToCopy)
183{
184 // Must match the writer's release with acquire for well defined reads of
185 // the buffer
186 auto end = mEnd.load( std::memory_order_acquire );
187 auto start = mStart.load( std::memory_order_relaxed );
188 samplesToCopy = std::min( samplesToCopy, Filled( start, end ) );
189 auto dest = buffer;
190 size_t copied = 0;
191
192 while(samplesToCopy) {
193 auto block = std::min( samplesToCopy, mBufferSize - start );
194
196 dest, format,
197 block, DitherType::none);
198
199 dest += block * SAMPLE_SIZE(format);
200 start = (start + block) % mBufferSize;
201 samplesToCopy -= block;
202 copied += block;
203 }
204
205 // Communicate to writer that we have consumed some data,
206 // with nonrelaxed ordering
207 mStart.store( start, std::memory_order_release );
208
209 return copied;
210}
211
212size_t RingBuffer::Discard(size_t samplesToDiscard)
213{
214 auto end = mEnd.load( std::memory_order_relaxed ); // get away with it here
215 auto start = mStart.load( std::memory_order_relaxed );
216 samplesToDiscard = std::min( samplesToDiscard, Filled( start, end ) );
217
218 // Communicate to writer that we have skipped some data, and that's all
219 mStart.store((start + samplesToDiscard) % mBufferSize,
220 std::memory_order_relaxed);
221
222 return samplesToDiscard;
223}
int min(int a, int b)
@ none
Definition: Dither.h:20
int format
Definition: ExportPCM.cpp:56
void ClearSamples(samplePtr dst, sampleFormat format, size_t start, size_t len)
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.
sampleFormat
Definition: SampleFormat.h:29
char * samplePtr
Definition: SampleFormat.h:49
#define SAMPLE_SIZE(SampleFormat)
Definition: SampleFormat.h:44
const char * constSamplePtr
Definition: SampleFormat.h:50
size_t Put(constSamplePtr buffer, sampleFormat format, size_t samples, size_t padding=0)
Does not apply dithering.
Definition: RingBuffer.cpp:72
const size_t mBufferSize
Definition: RingBuffer.h:55
std::pair< samplePtr, size_t > GetUnflushed(unsigned iBlock)
Get access to written but unflushed data, which is in at most two blocks.
Definition: RingBuffer.cpp:133
NonInterfering< std::atomic< size_t > > mEnd
Definition: RingBuffer.h:53
size_t Filled(size_t start, size_t end)
Definition: RingBuffer.cpp:45
size_t Free(size_t start, size_t end)
Definition: RingBuffer.cpp:50
void Flush()
Flush after a sequence of Put (and/or Clear) calls to let consumer see.
Definition: RingBuffer.cpp:156
size_t Clear(sampleFormat format, size_t samples)
Definition: RingBuffer.cpp:110
size_t Get(samplePtr buffer, sampleFormat format, size_t samples)
Does not apply dithering.
Definition: RingBuffer.cpp:181
size_t AvailForGet()
Definition: RingBuffer.cpp:171
const sampleFormat mFormat
Definition: RingBuffer.h:57
size_t mWritten
Definition: RingBuffer.h:50
NonInterfering< std::atomic< size_t > > mStart
Definition: RingBuffer.h:53
size_t AvailForPut()
Definition: RingBuffer.cpp:63
const SampleBuffer mBuffer
Definition: RingBuffer.h:58
size_t Discard(size_t samples)
Definition: RingBuffer.cpp:212
RingBuffer(sampleFormat format, size_t size)
Definition: RingBuffer.cpp:31
samplePtr ptr() const
Definition: SampleFormat.h:110
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:159
STL namespace.