/[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 277 - (show annotations) (download) (as text)
Sat Oct 9 15:48:32 2004 UTC (19 years, 6 months ago) by schoenebeck
File MIME type: text/x-c++hdr
File size: 15654 byte(s)
* compatibility fixes for old gcc 2.95.4

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 _T>
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 * Reads one element from the NonVolatileReader's current read
268 * position and copies it to the variable pointed by \a dst and
269 * finally increments the NonVolatileReader's read position by
270 * one.
271 *
272 * @param dst - where the element is copied to
273 * @returns 1 on success, 0 otherwise
274 */
275 int pop(T* dst) { return read(dst,1); }
276
277 /**
278 * Reads \a cnt elements from the NonVolatileReader's current
279 * read position and copies it to the buffer pointed by \a dest
280 * and finally increments the NonVolatileReader's read position
281 * by the number of read elements.
282 *
283 * @param dest - destination buffer
284 * @param cnt - number of elements to read
285 * @returns number of read elements
286 */
287 int read(T* dest, int cnt) {
288 int free_cnt;
289 int cnt2;
290 int to_read;
291 int n1, n2;
292 int priv_read_ptr;
293
294 priv_read_ptr = read_ptr;
295
296 if ((free_cnt = read_space()) == 0) return 0;
297
298 to_read = cnt > free_cnt ? free_cnt : cnt;
299
300 cnt2 = priv_read_ptr + to_read;
301
302 if (cnt2 > pBuf->size) {
303 n1 = pBuf->size - priv_read_ptr;
304 n2 = cnt2 & pBuf->size_mask;
305 } else {
306 n1 = to_read;
307 n2 = 0;
308 }
309
310 memcpy(dest, &pBuf->buf[priv_read_ptr], n1 * sizeof(T));
311 priv_read_ptr = (priv_read_ptr + n1) & pBuf->size_mask;
312
313 if (n2) {
314 memcpy(dest+n1, pBuf->buf, n2 * sizeof(T));
315 priv_read_ptr = n2;
316 }
317
318 this->read_ptr = priv_read_ptr;
319 return to_read;
320 }
321 protected:
322 _NonVolatileReader(RingBuffer<_T>* pBuf) {
323 this->pBuf = pBuf;
324 this->read_ptr = atomic_read(&pBuf->read_ptr);
325 }
326
327 RingBuffer<_T>* pBuf;
328 int read_ptr;
329
330 friend class RingBuffer<_T>;
331 };
332
333 typedef _NonVolatileReader<T> NonVolatileReader;
334
335 NonVolatileReader get_non_volatile_reader() { return NonVolatileReader(this); }
336
337 protected:
338 T *buf;
339 atomic_t write_ptr;
340 atomic_t read_ptr;
341 int size_mask;
342
343 friend class _NonVolatileReader<T>;
344 };
345
346 template<class T> T *
347 RingBuffer<T>::get_write_ptr (void) {
348 return(&buf[atomic_read(&write_ptr)]);
349 }
350
351 template<class T> T *
352 RingBuffer<T>::get_buffer_begin (void) {
353 return(buf);
354 }
355
356
357
358 template<class T> int
359 RingBuffer<T>::read (T *dest, int cnt)
360
361 {
362 int free_cnt;
363 int cnt2;
364 int to_read;
365 int n1, n2;
366 int priv_read_ptr;
367
368 priv_read_ptr=atomic_read(&read_ptr);
369
370 if ((free_cnt = read_space ()) == 0) {
371 return 0;
372 }
373
374 to_read = cnt > free_cnt ? free_cnt : cnt;
375
376 cnt2 = priv_read_ptr + to_read;
377
378 if (cnt2 > size) {
379 n1 = size - priv_read_ptr;
380 n2 = cnt2 & size_mask;
381 } else {
382 n1 = to_read;
383 n2 = 0;
384 }
385
386 memcpy (dest, &buf[priv_read_ptr], n1 * sizeof (T));
387 priv_read_ptr = (priv_read_ptr + n1) & size_mask;
388
389 if (n2) {
390 memcpy (dest+n1, buf, n2 * sizeof (T));
391 priv_read_ptr = n2;
392 }
393
394 atomic_set(&read_ptr, priv_read_ptr);
395 return to_read;
396 }
397
398 template<class T> int
399 RingBuffer<T>::write (T *src, int cnt)
400
401 {
402 int free_cnt;
403 int cnt2;
404 int to_write;
405 int n1, n2;
406 int priv_write_ptr;
407
408 priv_write_ptr=atomic_read(&write_ptr);
409
410 if ((free_cnt = write_space ()) == 0) {
411 return 0;
412 }
413
414 to_write = cnt > free_cnt ? free_cnt : cnt;
415
416 cnt2 = priv_write_ptr + to_write;
417
418 if (cnt2 > size) {
419 n1 = size - priv_write_ptr;
420 n2 = cnt2 & size_mask;
421 } else {
422 n1 = to_write;
423 n2 = 0;
424 }
425
426 memcpy (&buf[priv_write_ptr], src, n1 * sizeof (T));
427 priv_write_ptr = (priv_write_ptr + n1) & size_mask;
428
429 if (n2) {
430 memcpy (buf, src+n1, n2 * sizeof (T));
431 priv_write_ptr = n2;
432 }
433 atomic_set(&write_ptr, priv_write_ptr);
434 return to_write;
435 }
436
437
438 #endif /* RINGBUFFER_H */

  ViewVC Help
Powered by ViewVC