/[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 361 - (show annotations) (download) (as text)
Wed Feb 9 01:22:18 2005 UTC (19 years, 1 month ago) by schoenebeck
File MIME type: text/x-c++hdr
File size: 17240 byte(s)
* bunch of fixes for OSX (patch by Stephane Letz)

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

  ViewVC Help
Powered by ViewVC