/[svn]/linuxsampler/trunk/src/common/RingBuffer.h
ViewVC logotype

Contents of /linuxsampler/trunk/src/common/RingBuffer.h

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1895 - (show annotations) (download) (as text)
Sun May 3 12:15:40 2009 UTC (14 years, 11 months ago) by persson
File MIME type: text/x-c++hdr
File size: 20507 byte(s)
* fixes for using large audio device buffers
* VST: added support for sample rate and buffer size changes
* VST: close editor (Fantasia) when the VST is removed
* minor fix in configure for mmsystem.h detection on MinGW
* removed warnings from gcc 4.4 and valgrind

1 /***************************************************************************
2 * *
3 * LinuxSampler - modular, streaming capable sampler *
4 * *
5 * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck *
6 * Copyright (C) 2005 - 2008 Christian Schoenebeck *
7 * *
8 * This program is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 2 of the License, or *
11 * (at your option) any later version. *
12 * *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License *
19 * along with this program; if not, write to the Free Software *
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, *
21 * MA 02111-1307 USA *
22 ***************************************************************************/
23
24 #ifndef RINGBUFFER_H
25 #define RINGBUFFER_H
26
27 #define DEFAULT_WRAP_ELEMENTS 0
28
29 #include <string.h>
30
31 #include "lsatomic.h"
32
33 using LinuxSampler::atomic;
34 using LinuxSampler::memory_order_relaxed;
35 using LinuxSampler::memory_order_acquire;
36 using LinuxSampler::memory_order_release;
37
38
39 /** @brief Real-time safe and type safe RingBuffer implementation.
40 *
41 * This constant size buffer can be used to send data from exactly one
42 * sender / writing thread to exactly one receiver / reading thread. It is
43 * real-time safe due to the fact that data is only allocated when this
44 * RingBuffer is created and no system level mechanisms are used for
45 * ensuring thread safety of this class.
46 *
47 * <b>Important:</b> There are two distinct behaviors of this RingBuffer
48 * which has to be given as template argument @c T_DEEP_COPY, which is a
49 * boolean flag:
50 *
51 * - @c true: The RingBuffer will copy elements of type @c T by using type
52 * @c T's assignment operator. This behavior is mandatory for all data
53 * structures (classes) which additionally allocate memory on the heap.
54 * Type @c T's needs to have an assignment operator implementation though,
55 * otherwise this will cause a compilation error. This behavior is more
56 * safe, but usually slower (except for very small buffer sizes, where it
57 * might be even faster).
58 * - @c false: The RingBuffer will copy elements of type @c T by flatly
59 * copying their structural data ( i.e. with @c memcpy() ) in one piece.
60 * This will only work if class @c T (and all of its subelements) does not
61 * allocate any additional data on the heap by itself. So use this option
62 * with great care, because otherwise it will result in very ugly behavior
63 * and crashes! For larger buffer sizes, this behavior will most probably
64 * be faster.
65 */
66 template<class T, bool T_DEEP_COPY>
67 class RingBuffer
68 {
69 public:
70 RingBuffer (int sz, int wrap_elements = DEFAULT_WRAP_ELEMENTS) :
71 write_ptr(0), read_ptr(0) {
72 int power_of_two;
73
74 this->wrap_elements = wrap_elements;
75
76 // the write-with-wrap functions need wrap_elements extra
77 // space in the buffer to be able to copy the wrap space
78 sz += wrap_elements;
79
80 for (power_of_two = 1;
81 1<<power_of_two < sz;
82 power_of_two++);
83
84 size = 1<<power_of_two;
85 size_mask = size;
86 size_mask -= 1;
87 buf = new T[size + wrap_elements];
88 };
89
90 virtual ~RingBuffer() {
91 delete [] buf;
92 }
93
94 /**
95 * Sets all remaining write space elements to zero. The write pointer
96 * will currently not be incremented after, but that might change in
97 * future.
98 *
99 * @e Caution: for @c T_DEEP_COPY=true you might probably @e NOT want
100 * to call this method at all, at least not in case type @c T allocates
101 * any additional data on the heap by itself.
102 */
103 inline void fill_write_space_with_null() {
104 int w = write_ptr.load(memory_order_relaxed),
105 r = read_ptr.load(memory_order_acquire);
106 memset(get_write_ptr(), 0, sizeof(T)*write_space_to_end());
107 if (r && w >= r) {
108 memset(get_buffer_begin(), 0, sizeof(T)*(r - 1));
109 }
110
111 // set the wrap space elements to null
112 if (wrap_elements) memset(&buf[size], 0, sizeof(T)*wrap_elements);
113 }
114
115 __inline int read (T *dest, int cnt);
116 __inline int write (T *src, int cnt);
117
118 inline int push(T* src) { return write(src,1); }
119 inline int pop(T* dst) { return read(dst,1); }
120
121 __inline T *get_buffer_begin();
122
123 __inline T *get_read_ptr(void) {
124 return(&buf[read_ptr.load(memory_order_relaxed)]);
125 }
126
127 /**
128 * Returns a pointer to the element from the current read position,
129 * advanced by \a offset elements.
130 */
131 /*inline T* get_read_ptr(int offset) {
132 int r = read_ptr.load(memory_order_relaxed);
133 r += offset;
134 r &= size_mask;
135 return &buf[r];
136 }*/
137
138 __inline T *get_write_ptr();
139 __inline void increment_read_ptr(int cnt) {
140 read_ptr.store((read_ptr.load(memory_order_relaxed) + cnt) & size_mask, memory_order_release);
141 }
142 __inline void set_read_ptr(int val) {
143 read_ptr.store(val, memory_order_release);
144 }
145
146 __inline void increment_write_ptr(int cnt) {
147 write_ptr.store((write_ptr.load(memory_order_relaxed) + cnt) & size_mask, memory_order_release);
148 }
149
150 /* this function increments the write_ptr by cnt, if the buffer wraps then
151 subtract size from the write_ptr value so that it stays within 0<write_ptr<size
152 use this function to increment the write ptr after you filled the buffer
153 with a number of elements given by write_space_to_end_with_wrap().
154 This ensures that the data that is written to the buffer fills up all
155 the wrap space that resides past the regular buffer. The wrap_space is needed for
156 interpolation. So that the audio thread sees the ringbuffer like a linear space
157 which allows us to use faster routines.
158 When the buffer wraps the wrap part is memcpy()ied to the beginning of the buffer
159 and the write ptr incremented accordingly.
160 */
161 __inline void increment_write_ptr_with_wrap(int cnt) {
162 int w = write_ptr.load(memory_order_relaxed);
163 w += cnt;
164 if(w >= size) {
165 w -= size;
166 copy(&buf[0], &buf[size], w);
167 //printf("DEBUG !!!! increment_write_ptr_with_wrap: buffer wrapped, elements wrapped = %d (wrap_elements %d)\n",w,wrap_elements);
168 }
169 write_ptr.store(w, memory_order_release);
170 }
171
172 /* this function returns the available write space in the buffer
173 when the read_ptr > write_ptr it returns the space inbetween, otherwise
174 when the read_ptr < write_ptr it returns the space between write_ptr and
175 the buffer end, including the wrap_space.
176 There is an exception to it. When read_ptr <= wrap_elements. In that
177 case we return the write space to buffer end (-1) without the wrap_elements,
178 this is needed because a subsequent increment_write_ptr which copies the
179 data that resides into the wrap space to the beginning of the buffer and increments
180 the write_ptr would cause the write_ptr overstepping the read_ptr which would be an error.
181 So basically the if(r<=wrap_elements) we return the buffer space to end - 1 which
182 ensures that at the next call there will be one element free to write before the buffer wraps
183 and usually (except in EOF situations) the next write_space_to_end_with_wrap() will return
184 1 + wrap_elements which ensures that the wrap part gets fully filled with data
185 */
186 __inline int write_space_to_end_with_wrap() {
187 int w, r;
188
189 w = write_ptr.load(memory_order_relaxed);
190 r = read_ptr.load(memory_order_acquire);
191 //printf("write_space_to_end: w=%d r=%d\n",w,r);
192 if(r > w) {
193 //printf("DEBUG: write_space_to_end_with_wrap: r>w r=%d w=%d val=%d\n",r,w,r - w - 1);
194 return(r - w - 1);
195 }
196 if(r <= wrap_elements) {
197 //printf("DEBUG: write_space_to_end_with_wrap: ATTENTION r <= wrap_elements: r=%d w=%d val=%d\n",r,w,size - w -1);
198 return(size - w -1);
199 }
200 if(r) {
201 //printf("DEBUG: write_space_to_end_with_wrap: r=%d w=%d val=%d\n",r,w,size - w + wrap_elements);
202 return(size - w + wrap_elements);
203 }
204 //printf("DEBUG: write_space_to_end_with_wrap: r=0 w=%d val=%d\n",w,size - w - 1 + wrap_elements);
205 return(size - w - 1 + wrap_elements);
206 }
207
208 /* this function adjusts the number of elements to write into the ringbuffer
209 in a way that the size boundary is avoided and that the wrap space always gets
210 entirely filled.
211 cnt contains the write_space_to_end_with_wrap() amount while
212 capped_cnt contains a capped amount of samples to read.
213 normally capped_cnt == cnt but in some cases eg when the disk thread needs
214 to refill tracks with smaller blocks because lots of streams require immediate
215 refill because lots of notes were started simultaneously.
216 In that case we set for example capped_cnt to a fixed amount (< cnt, eg 64k),
217 which helps to reduce the buffer refill latencies that occur between streams.
218 the first if() checks if the current write_ptr + capped_cnt resides within
219 the wrap area but is < size+wrap_elements. in that case we cannot return
220 capped_cnt because it would lead to a write_ptr wrapping and only a partial fill
221 of wrap space which would lead to errors. So we simply return cnt which ensures
222 that the the entire wrap space will get filled correctly.
223 In all other cases (which are not problematic because no write_ptr wrapping
224 occurs) we simply return capped_cnt.
225 */
226 __inline int adjust_write_space_to_avoid_boundary(int cnt, int capped_cnt) {
227 int w;
228 w = write_ptr.load(memory_order_relaxed);
229 if((w+capped_cnt) >= size && (w+capped_cnt) < (size+wrap_elements)) {
230 //printf("adjust_write_space_to_avoid_boundary returning cnt = %d\n",cnt);
231 return(cnt);
232 }
233 //printf("adjust_write_space_to_avoid_boundary returning capped_cnt = %d\n",capped_cnt);
234 return(capped_cnt);
235 }
236
237 __inline int write_space_to_end() {
238 int w, r;
239
240 w = write_ptr.load(memory_order_relaxed);
241 r = read_ptr.load(memory_order_acquire);
242 //printf("write_space_to_end: w=%d r=%d\n",w,r);
243 if(r > w) return(r - w - 1);
244 if(r) return(size - w);
245 return(size - w - 1);
246 }
247
248 __inline int read_space_to_end() {
249 int w, r;
250
251 w = write_ptr.load(memory_order_acquire);
252 r = read_ptr.load(memory_order_relaxed);
253 if(w >= r) return(w - r);
254 return(size - r);
255 }
256 __inline void init() {
257 write_ptr.store(0, memory_order_relaxed);
258 read_ptr.store(0, memory_order_relaxed);
259 // wrap=0;
260 }
261
262 int write_space () {
263 int w, r;
264
265 w = write_ptr.load(memory_order_relaxed);
266 r = read_ptr.load(memory_order_acquire);
267
268 if (w > r) {
269 return ((r - w + size) & size_mask) - 1;
270 } else if (w < r) {
271 return (r - w) - 1;
272 } else {
273 return size - 1;
274 }
275 }
276
277 int read_space () {
278 int w, r;
279
280 w = write_ptr.load(memory_order_acquire);
281 r = read_ptr.load(memory_order_relaxed);
282
283 if (w >= r) {
284 return w - r;
285 } else {
286 return (w - r + size) & size_mask;
287 }
288 }
289
290 int size;
291 int wrap_elements;
292
293 /**
294 * Independent, random access reading from a RingBuffer. This class
295 * allows to read from a RingBuffer without being forced to free read
296 * data while reading / positioning.
297 */
298 template<class T1, bool T1_DEEP_COPY>
299 class _NonVolatileReader {
300 public:
301 int read_space() {
302 int r = read_ptr;
303 int w = pBuf->write_ptr.load(memory_order_acquire);
304 return (w >= r) ? w - r : (w - r + pBuf->size) & pBuf->size_mask;
305 }
306
307 /**
308 * Prefix decrement operator, for reducing NonVolatileReader's
309 * read position by one.
310 */
311 inline void operator--() {
312 if (read_ptr == pBuf->read_ptr.load(memory_order_relaxed)) return; //TODO: or should we react oh this case (e.g. force segfault), as this is a very odd case?
313 read_ptr = (read_ptr-1) & pBuf->size_mask;
314 }
315
316 /**
317 * Postfix decrement operator, for reducing NonVolatileReader's
318 * read position by one.
319 */
320 inline void operator--(int) {
321 --*this;
322 }
323
324 /**
325 * Returns pointer to the RingBuffer data of current
326 * NonVolatileReader's read position and increments
327 * NonVolatileReader's read position by one.
328 *
329 * @returns pointer to element of current read position
330 */
331 T* pop() {
332 if (!read_space()) return NULL;
333 T* pData = &pBuf->buf[read_ptr];
334 read_ptr++;
335 read_ptr &= pBuf->size_mask;
336 return pData;
337 }
338
339 /**
340 * Reads one element from the NonVolatileReader's current read
341 * position and copies it to the variable pointed by \a dst and
342 * finally increments the NonVolatileReader's read position by
343 * one.
344 *
345 * @param dst - where the element is copied to
346 * @returns 1 on success, 0 otherwise
347 */
348 int pop(T* dst) { return read(dst,1); }
349
350 /**
351 * Reads \a cnt elements from the NonVolatileReader's current
352 * read position and copies it to the buffer pointed by \a dest
353 * and finally increments the NonVolatileReader's read position
354 * by the number of read elements.
355 *
356 * @param dest - destination buffer
357 * @param cnt - number of elements to read
358 * @returns number of read elements
359 */
360 int read(T* dest, int cnt) {
361 int free_cnt;
362 int cnt2;
363 int to_read;
364 int n1, n2;
365 int priv_read_ptr;
366
367 priv_read_ptr = read_ptr;
368
369 if ((free_cnt = read_space()) == 0) return 0;
370
371 to_read = cnt > free_cnt ? free_cnt : cnt;
372
373 cnt2 = priv_read_ptr + to_read;
374
375 if (cnt2 > pBuf->size) {
376 n1 = pBuf->size - priv_read_ptr;
377 n2 = cnt2 & pBuf->size_mask;
378 } else {
379 n1 = to_read;
380 n2 = 0;
381 }
382
383 copy(dest, &pBuf->buf[priv_read_ptr], n1);
384 priv_read_ptr = (priv_read_ptr + n1) & pBuf->size_mask;
385
386 if (n2) {
387 copy(dest+n1, pBuf->buf, n2);
388 priv_read_ptr = n2;
389 }
390
391 this->read_ptr = priv_read_ptr;
392 return to_read;
393 }
394
395 /**
396 * Finally when the read data is not needed anymore, this method
397 * should be called to free the data in the RingBuffer up to the
398 * current read position of this NonVolatileReader.
399 *
400 * @see RingBuffer::increment_read_ptr()
401 */
402 void free() {
403 pBuf->read_ptr.store(read_ptr, memory_order_release);
404 }
405
406 protected:
407 _NonVolatileReader(RingBuffer<T1,T1_DEEP_COPY>* pBuf) {
408 this->pBuf = pBuf;
409 this->read_ptr = pBuf->read_ptr.load(memory_order_relaxed);
410 }
411
412 RingBuffer<T1,T1_DEEP_COPY>* pBuf;
413 int read_ptr;
414
415 friend class RingBuffer<T1,T1_DEEP_COPY>;
416 };
417
418 typedef _NonVolatileReader<T,T_DEEP_COPY> NonVolatileReader;
419
420 NonVolatileReader get_non_volatile_reader() { return NonVolatileReader(this); }
421
422 protected:
423 T *buf;
424 atomic<int> write_ptr;
425 atomic<int> read_ptr;
426 int size_mask;
427
428 /**
429 * Copies \a n amount of elements from the buffer given by
430 * \a pSrc to the buffer given by \a pDst.
431 */
432 inline static void copy(T* pDst, T* pSrc, int n);
433
434 friend class _NonVolatileReader<T,T_DEEP_COPY>;
435 };
436
437 template<class T, bool T_DEEP_COPY>
438 T* RingBuffer<T,T_DEEP_COPY>::get_write_ptr (void) {
439 return(&buf[write_ptr.load(memory_order_relaxed)]);
440 }
441
442 template<class T, bool T_DEEP_COPY>
443 T* RingBuffer<T,T_DEEP_COPY>::get_buffer_begin (void) {
444 return(buf);
445 }
446
447
448
449 template<class T, bool T_DEEP_COPY>
450 int RingBuffer<T,T_DEEP_COPY>::read(T* dest, int cnt)
451 {
452 int free_cnt;
453 int cnt2;
454 int to_read;
455 int n1, n2;
456 int priv_read_ptr;
457
458 priv_read_ptr = read_ptr.load(memory_order_relaxed);
459
460 if ((free_cnt = read_space ()) == 0) {
461 return 0;
462 }
463
464 to_read = cnt > free_cnt ? free_cnt : cnt;
465
466 cnt2 = priv_read_ptr + to_read;
467
468 if (cnt2 > size) {
469 n1 = size - priv_read_ptr;
470 n2 = cnt2 & size_mask;
471 } else {
472 n1 = to_read;
473 n2 = 0;
474 }
475
476 copy(dest, &buf[priv_read_ptr], n1);
477 priv_read_ptr = (priv_read_ptr + n1) & size_mask;
478
479 if (n2) {
480 copy(dest+n1, buf, n2);
481 priv_read_ptr = n2;
482 }
483
484 read_ptr.store(priv_read_ptr, memory_order_release);
485 return to_read;
486 }
487
488 template<class T, bool T_DEEP_COPY>
489 int RingBuffer<T,T_DEEP_COPY>::write(T* src, int cnt)
490 {
491 int free_cnt;
492 int cnt2;
493 int to_write;
494 int n1, n2;
495 int priv_write_ptr;
496
497 priv_write_ptr = write_ptr.load(memory_order_relaxed);
498
499 if ((free_cnt = write_space ()) == 0) {
500 return 0;
501 }
502
503 to_write = cnt > free_cnt ? free_cnt : cnt;
504
505 cnt2 = priv_write_ptr + to_write;
506
507 if (cnt2 > size) {
508 n1 = size - priv_write_ptr;
509 n2 = cnt2 & size_mask;
510 } else {
511 n1 = to_write;
512 n2 = 0;
513 }
514
515 copy(&buf[priv_write_ptr], src, n1);
516 priv_write_ptr = (priv_write_ptr + n1) & size_mask;
517
518 if (n2) {
519 copy(buf, src+n1, n2);
520 priv_write_ptr = n2;
521 }
522 write_ptr.store(priv_write_ptr, memory_order_release);
523 return to_write;
524 }
525
526 template<class T, bool T_DEEP_COPY>
527 void RingBuffer<T,T_DEEP_COPY>::copy(T* pDst, T* pSrc, int n) {
528 if (T_DEEP_COPY) { // deep copy - won't work for data structures without assignment operator implementation
529 for (int i = 0; i < n; i++) pDst[i] = pSrc[i];
530 } else { // flat copy - won't work for complex data structures !
531 memcpy(pDst, pSrc, n * sizeof(T));
532 }
533 }
534
535 #endif /* RINGBUFFER_H */

  ViewVC Help
Powered by ViewVC