Audacity  3.0.3
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  : mFormat{ format }
33  , mBufferSize{ std::max<size_t>(size, 64) }
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 
45 size_t RingBuffer::Filled( size_t start, size_t end )
46 {
47  return (end + mBufferSize - start) % mBufferSize;
48 }
49 
50 size_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 writes the end, so it can read it again relaxed
58 // And it reads the start written by reader, with acquire order,
59 // so that any reading done in Get() happens-before any reuse of the space.
60 //
61 
63 {
64  auto start = mStart.load( std::memory_order_relaxed );
65  auto end = mEnd.load( std::memory_order_relaxed );
66  return Free( start, end );
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 = mEnd.load( std::memory_order_relaxed );
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 
87  CopySamples(src, format,
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  // Atomically update the end pointer with release, so the nonatomic writes
106  // just done to the buffer don't get reordered after
107  mEnd.store(pos, std::memory_order_release);
108 
109  return copied;
110 }
111 
112 size_t RingBuffer::Clear(sampleFormat format, size_t samplesToClear)
113 {
114  auto start = mStart.load( std::memory_order_acquire );
115  auto end = mEnd.load( std::memory_order_relaxed );
116  samplesToClear = std::min( samplesToClear, Free( start, end ) );
117  size_t cleared = 0;
118  auto pos = end;
119 
120  while(samplesToClear) {
121  auto block = std::min( samplesToClear, mBufferSize - pos );
122 
123  ClearSamples(mBuffer.ptr(), format, pos, block);
124 
125  pos = (pos + block) % mBufferSize;
126  samplesToClear -= block;
127  cleared += block;
128  }
129 
130  // Atomically update the end pointer with release, so the nonatomic writes
131  // just done to the buffer don't get reordered after
132  mEnd.store(pos, std::memory_order_release);
133 
134  return cleared;
135 }
136 
137 //
138 // For the reader only:
139 // Only reader writes the start, so it can read it again relaxed
140 // But it reads the end written by the writer, who also sends sample data
141 // with the changes of end; therefore that must be read with acquire order
142 // if we do more than merely query the size or throw samples away
143 //
144 
146 {
147  auto end = mEnd.load( std::memory_order_relaxed ); // get away with it here
148  auto start = mStart.load( std::memory_order_relaxed );
149  return Filled( start, end );
150 
151  // Writer might increase the available samples after return, but will
152  // never decrease them, so reader can safely assume this much at least
153 }
154 
156  size_t samplesToCopy)
157 {
158  // Must match the writer's release with acquire for well defined reads of
159  // the buffer
160  auto end = mEnd.load( std::memory_order_acquire );
161  auto start = mStart.load( std::memory_order_relaxed );
162  samplesToCopy = std::min( samplesToCopy, Filled( start, end ) );
163  auto dest = buffer;
164  size_t copied = 0;
165 
166  while(samplesToCopy) {
167  auto block = std::min( samplesToCopy, mBufferSize - start );
168 
170  dest, format,
171  block, DitherType::none);
172 
173  dest += block * SAMPLE_SIZE(format);
174  start = (start + block) % mBufferSize;
175  samplesToCopy -= block;
176  copied += block;
177  }
178 
179  // Communicate to writer that we have consumed some data,
180  // with nonrelaxed ordering
181  mStart.store( start, std::memory_order_release );
182 
183  return copied;
184 }
185 
186 size_t RingBuffer::Discard(size_t samplesToDiscard)
187 {
188  auto end = mEnd.load( std::memory_order_relaxed ); // get away with it here
189  auto start = mStart.load( std::memory_order_relaxed );
190  samplesToDiscard = std::min( samplesToDiscard, Filled( start, end ) );
191 
192  // Communicate to writer that we have skipped some data, and that's all
193  mStart.store((start + samplesToDiscard) % mBufferSize,
194  std::memory_order_relaxed);
195 
196  return samplesToDiscard;
197 }
ClearSamples
void ClearSamples(samplePtr dst, sampleFormat format, size_t start, size_t len)
Definition: SampleFormat.cpp:77
RingBuffer::mFormat
sampleFormat mFormat
Definition: RingBuffer.h:51
RingBuffer::mStart
NonInterfering< std::atomic< size_t > > mStart
Definition: RingBuffer.h:47
RingBuffer::Put
size_t Put(constSamplePtr buffer, sampleFormat format, size_t samples, size_t padding=0)
Does not apply dithering.
Definition: RingBuffer.cpp:72
SAMPLE_SIZE
#define SAMPLE_SIZE(SampleFormat)
Definition: SampleFormat.h:44
RingBuffer::Free
size_t Free(size_t start, size_t end)
Definition: RingBuffer.cpp:50
RingBuffer::Filled
size_t Filled(size_t start, size_t end)
Definition: RingBuffer.cpp:45
RingBuffer::AvailForPut
size_t AvailForPut()
Definition: RingBuffer.cpp:62
RingBuffer::Clear
size_t Clear(sampleFormat format, size_t samples)
Definition: RingBuffer.cpp:112
RingBuffer::mEnd
NonInterfering< std::atomic< size_t > > mEnd
Definition: RingBuffer.h:47
RingBuffer::mBuffer
SampleBuffer mBuffer
Definition: RingBuffer.h:52
CopySamples
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.
Definition: SampleFormat.cpp:111
constSamplePtr
const char * constSamplePtr
Definition: SampleFormat.h:50
none
@ none
Definition: Dither.h:20
format
int format
Definition: ExportPCM.cpp:56
RingBuffer::mBufferSize
const size_t mBufferSize
Definition: RingBuffer.h:49
RingBuffer::AvailForGet
size_t AvailForGet()
Definition: RingBuffer.cpp:145
sampleFormat
sampleFormat
Definition: SampleFormat.h:29
samplePtr
char * samplePtr
Definition: SampleFormat.h:49
min
int min(int a, int b)
Definition: CompareAudioCommand.cpp:106
RingBuffer::~RingBuffer
~RingBuffer()
Definition: RingBuffer.cpp:38
RingBuffer::RingBuffer
RingBuffer(sampleFormat format, size_t size)
Definition: RingBuffer.cpp:31
RingBuffer::Discard
size_t Discard(size_t samples)
Definition: RingBuffer.cpp:186
RingBuffer::Get
size_t Get(samplePtr buffer, sampleFormat format, size_t samples)
Does not apply dithering.
Definition: RingBuffer.cpp:155
RingBuffer.h
ZixRingImpl::size
uint32_t size
Size (capacity) in bytes.
Definition: ring.cpp:54
SampleBuffer::ptr
samplePtr ptr() const
Definition: SampleFormat.h:98
Dither.h