OpenShot Library | OpenShotAudio  0.2.2
juce_AbstractFifo.h
1 
2 /** @weakgroup juce_core-containers
3  * @{
4  */
5 /*
6  ==============================================================================
7 
8  This file is part of the JUCE library.
9  Copyright (c) 2017 - ROLI Ltd.
10 
11  JUCE is an open source library subject to commercial or open-source
12  licensing.
13 
14  The code included in this file is provided under the terms of the ISC license
15  http://www.isc.org/downloads/software-support-policy/isc-license. Permission
16  To use, copy, modify, and/or distribute this software for any purpose with or
17  without fee is hereby granted provided that the above copyright notice and
18  this permission notice appear in all copies.
19 
20  JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
21  EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
22  DISCLAIMED.
23 
24  ==============================================================================
25 */
26 
27 namespace juce
28 {
29 
30 //==============================================================================
31 /**
32  Encapsulates the logic required to implement a lock-free FIFO.
33 
34  This class handles the logic needed when building a single-reader, single-writer FIFO.
35 
36  It doesn't actually hold any data itself, but your FIFO class can use one of these to manage
37  its position and status when reading or writing to it.
38 
39  To use it, you can call prepareToWrite() to determine the position within your own buffer that
40  an incoming block of data should be stored, and prepareToRead() to find out when the next
41  outgoing block should be read from.
42 
43  e.g.
44  @code
45  struct MyFifo
46  {
47  void addToFifo (const int* someData, int numItems)
48  {
49  int start1, size1, start2, size2;
50  abstractFifo.prepareToWrite (numItems, start1, size1, start2, size2);
51 
52  if (size1 > 0)
53  copySomeData (myBuffer + start1, someData, size1);
54 
55  if (size2 > 0)
56  copySomeData (myBuffer + start2, someData + size1, size2);
57 
58  abstractFifo.finishedWrite (size1 + size2);
59  }
60 
61  void readFromFifo (int* someData, int numItems)
62  {
63  int start1, size1, start2, size2;
64  abstractFifo.prepareToRead (numItems, start1, size1, start2, size2);
65 
66  if (size1 > 0)
67  copySomeData (someData, myBuffer + start1, size1);
68 
69  if (size2 > 0)
70  copySomeData (someData + size1, myBuffer + start2, size2);
71 
72  abstractFifo.finishedRead (size1 + size2);
73  }
74 
75  AbstractFifo abstractFifo { 1024 };
76  int myBuffer[1024];
77  };
78  @endcode
79 
80  @tags{Core}
81 */
83 {
84 public:
85  //==============================================================================
86  /** Creates a FIFO to manage a buffer with the specified capacity. */
87  AbstractFifo (int capacity) noexcept;
88 
89  /** Destructor */
90  ~AbstractFifo();
91 
92  //==============================================================================
93  /** Returns the total size of the buffer being managed. */
94  int getTotalSize() const noexcept;
95 
96  /** Returns the number of items that can currently be added to the buffer without it overflowing. */
97  int getFreeSpace() const noexcept;
98 
99  /** Returns the number of items that can currently be read from the buffer. */
100  int getNumReady() const noexcept;
101 
102  /** Clears the buffer positions, so that it appears empty. */
103  void reset() noexcept;
104 
105  /** Changes the buffer's total size.
106  Note that this isn't thread-safe, so don't call it if there's any danger that it
107  might overlap with a call to any other method in this class!
108  */
109  void setTotalSize (int newSize) noexcept;
110 
111  //==============================================================================
112  /** Returns the location within the buffer at which an incoming block of data should be written.
113 
114  Because the section of data that you want to add to the buffer may overlap the end
115  and wrap around to the start, two blocks within your buffer are returned, and you
116  should copy your data into the first one, with any remaining data spilling over into
117  the second.
118 
119  If the number of items you ask for is too large to fit within the buffer's free space, then
120  blockSize1 + blockSize2 may add up to a lower value than numToWrite. If this happens, you
121  may decide to keep waiting and re-trying the method until there's enough space available.
122 
123  After calling this method, if you choose to write your data into the blocks returned, you
124  must call finishedWrite() to tell the FIFO how much data you actually added.
125 
126  e.g.
127  @code
128  void addToFifo (const int* someData, int numItems)
129  {
130  int start1, size1, start2, size2;
131  prepareToWrite (numItems, start1, size1, start2, size2);
132 
133  if (size1 > 0)
134  copySomeData (myBuffer + start1, someData, size1);
135 
136  if (size2 > 0)
137  copySomeData (myBuffer + start2, someData + size1, size2);
138 
139  finishedWrite (size1 + size2);
140  }
141  @endcode
142 
143  @param numToWrite indicates how many items you'd like to add to the buffer
144  @param startIndex1 on exit, this will contain the start index in your buffer at which your data should be written
145  @param blockSize1 on exit, this indicates how many items can be written to the block starting at startIndex1
146  @param startIndex2 on exit, this will contain the start index in your buffer at which any data that didn't fit into
147  the first block should be written
148  @param blockSize2 on exit, this indicates how many items can be written to the block starting at startIndex2
149  @see finishedWrite
150  */
151  void prepareToWrite (int numToWrite, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) const noexcept;
152 
153  /** Called after writing from the FIFO, to indicate that this many items have been added.
154  @see prepareToWrite
155  */
156  void finishedWrite (int numWritten) noexcept;
157 
158  /** Returns the location within the buffer from which the next block of data should be read.
159 
160  Because the section of data that you want to read from the buffer may overlap the end
161  and wrap around to the start, two blocks within your buffer are returned, and you
162  should read from both of them.
163 
164  If the number of items you ask for is greater than the amount of data available, then
165  blockSize1 + blockSize2 may add up to a lower value than numWanted. If this happens, you
166  may decide to keep waiting and re-trying the method until there's enough data available.
167 
168  After calling this method, if you choose to read the data, you must call finishedRead() to
169  tell the FIFO how much data you have consumed.
170 
171  e.g.
172  @code
173  void readFromFifo (int* someData, int numItems)
174  {
175  int start1, size1, start2, size2;
176  prepareToRead (numSamples, start1, size1, start2, size2);
177 
178  if (size1 > 0)
179  copySomeData (someData, myBuffer + start1, size1);
180 
181  if (size2 > 0)
182  copySomeData (someData + size1, myBuffer + start2, size2);
183 
184  finishedRead (size1 + size2);
185  }
186  @endcode
187 
188  @param numWanted indicates how many items you'd like to add to the buffer
189  @param startIndex1 on exit, this will contain the start index in your buffer at which your data should be written
190  @param blockSize1 on exit, this indicates how many items can be written to the block starting at startIndex1
191  @param startIndex2 on exit, this will contain the start index in your buffer at which any data that didn't fit into
192  the first block should be written
193  @param blockSize2 on exit, this indicates how many items can be written to the block starting at startIndex2
194  @see finishedRead
195  */
196  void prepareToRead (int numWanted, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) const noexcept;
197 
198  /** Called after reading from the FIFO, to indicate that this many items have now been consumed.
199  @see prepareToRead
200  */
201  void finishedRead (int numRead) noexcept;
202 
203  //==============================================================================
204 
205 private:
206  enum class ReadOrWrite
207  {
208  read,
209  write
210  };
211 
212 public:
213  /** Class for a scoped reader/writer */
214  template <ReadOrWrite mode>
215  class ScopedReadWrite final
216  {
217  public:
218  /** Construct an unassigned reader/writer. Doesn't do anything upon destruction. */
219  ScopedReadWrite() = default;
220 
221  /** Construct a reader/writer and immediately call prepareRead/prepareWrite
222  on the abstractFifo which was passed in.
223  This object will hold a pointer back to the fifo, so make sure that
224  the fifo outlives this object.
225  */
226  ScopedReadWrite (AbstractFifo& f, int num) noexcept : fifo (&f)
227  {
228  prepare (*fifo, num);
229  }
230 
231  ScopedReadWrite (const ScopedReadWrite&) = delete;
232  ScopedReadWrite (ScopedReadWrite&&) noexcept;
233 
234  ScopedReadWrite& operator= (const ScopedReadWrite&) = delete;
235  ScopedReadWrite& operator= (ScopedReadWrite&&) noexcept;
236 
237  /** Calls finishedRead or finishedWrite if this is a non-null scoped
238  reader/writer.
239  */
240  ~ScopedReadWrite() noexcept
241  {
242  if (fifo != nullptr)
243  finish (*fifo, blockSize1 + blockSize2);
244  }
245 
246  /** Calls the passed function with each index that was deemed valid
247  for the current read/write operation.
248  */
249  template <typename FunctionToApply>
250  void forEach (FunctionToApply&& func) const
251  {
252  for (auto i = startIndex1, e = startIndex1 + blockSize1; i != e; ++i) func (i);
253  for (auto i = startIndex2, e = startIndex2 + blockSize2; i != e; ++i) func (i);
254  }
255 
256  int startIndex1, blockSize1, startIndex2, blockSize2;
257 
258  private:
259  void prepare (AbstractFifo&, int) noexcept;
260  static void finish (AbstractFifo&, int) noexcept;
261  void swap (ScopedReadWrite&) noexcept;
262 
263  AbstractFifo* fifo = nullptr;
264  };
265 
266  using ScopedRead = ScopedReadWrite<ReadOrWrite::read>;
267  using ScopedWrite = ScopedReadWrite<ReadOrWrite::write>;
268 
269  /** Replaces prepareToRead/finishedRead with a single function.
270  This function returns an object which contains the start indices and
271  block sizes, and also automatically finishes the read operation when
272  it goes out of scope.
273  @code
274  {
275  auto readHandle = fifo.read (4);
276 
277  for (auto i = 0; i != readHandle.blockSize1; ++i)
278  {
279  // read the item at index readHandle.startIndex1 + i
280  }
281 
282  for (auto i = 0; i != readHandle.blockSize2; ++i)
283  {
284  // read the item at index readHandle.startIndex2 + i
285  }
286  } // readHandle goes out of scope here, finishing the read operation
287  @endcode
288  */
289  ScopedRead read (int numToRead) noexcept;
290 
291  /** Replaces prepareToWrite/finishedWrite with a single function.
292  This function returns an object which contains the start indices and
293  block sizes, and also automatically finishes the write operation when
294  it goes out of scope.
295  @code
296  {
297  auto writeHandle = fifo.write (5);
298 
299  for (auto i = 0; i != writeHandle.blockSize1; ++i)
300  {
301  // write the item at index writeHandle.startIndex1 + i
302  }
303 
304  for (auto i = 0; i != writeHandle.blockSize2; ++i)
305  {
306  // write the item at index writeHandle.startIndex2 + i
307  }
308  } // writeHandle goes out of scope here, finishing the write operation
309  @endcode
310  */
311  ScopedWrite write (int numToWrite) noexcept;
312 
313 private:
314  //==============================================================================
315  int bufferSize;
316  Atomic<int> validStart, validEnd;
317 
318  JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AbstractFifo)
319 };
320 
321 template<>
322 inline void AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::read>::finish (AbstractFifo& f, int num) noexcept
323 {
324  f.finishedRead (num);
325 }
326 
327 template<>
328 inline void AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::write>::finish (AbstractFifo& f, int num) noexcept
329 {
330  f.finishedWrite (num);
331 }
332 
333 template<>
334 inline void AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::read>::prepare (AbstractFifo& f, int num) noexcept
335 {
336  f.prepareToRead (num, startIndex1, blockSize1, startIndex2, blockSize2);
337 }
338 
339 template<>
340 inline void AbstractFifo::ScopedReadWrite<AbstractFifo::ReadOrWrite::write>::prepare (AbstractFifo& f, int num) noexcept
341 {
342  f.prepareToWrite (num, startIndex1, blockSize1, startIndex2, blockSize2);
343 }
344 
345 
346 } // namespace juce
347 
348 /** @}*/
Class for a scoped reader/writer.
ScopedReadWrite(AbstractFifo &f, int num) noexcept
Construct a reader/writer and immediately call prepareRead/prepareWrite on the abstractFifo which was...
void forEach(FunctionToApply &&func) const
Calls the passed function with each index that was deemed valid for the current read/write operation.
ScopedReadWrite()=default
Construct an unassigned reader/writer.
Encapsulates the logic required to implement a lock-free FIFO.
#define JUCE_API
This macro is added to all JUCE public class declarations.