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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 361 - (hide annotations) (download) (as text)
Wed Feb 9 01:22:18 2005 UTC (19 years, 2 months 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 schoenebeck 53 /***************************************************************************
2     * *
3     * LinuxSampler - modular, streaming capable sampler *
4     * *
5 schoenebeck 56 * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck *
6 schoenebeck 53 * *
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 schoenebeck 243 /**
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 schoenebeck 361 template<class T1>
258 schoenebeck 243 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 schoenebeck 294 * 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 schoenebeck 243 * 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 schoenebeck 294
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 schoenebeck 243 protected:
366 schoenebeck 361 _NonVolatileReader(RingBuffer<T1>* pBuf) {
367 schoenebeck 243 this->pBuf = pBuf;
368     this->read_ptr = atomic_read(&pBuf->read_ptr);
369     }
370    
371 schoenebeck 361 RingBuffer<T1>* pBuf;
372 schoenebeck 243 int read_ptr;
373    
374 schoenebeck 361 friend class RingBuffer<T1>;
375 schoenebeck 243 };
376    
377     typedef _NonVolatileReader<T> NonVolatileReader;
378    
379     NonVolatileReader get_non_volatile_reader() { return NonVolatileReader(this); }
380    
381 schoenebeck 53 protected:
382     T *buf;
383     atomic_t write_ptr;
384     atomic_t read_ptr;
385     int size_mask;
386 schoenebeck 277
387     friend class _NonVolatileReader<T>;
388 schoenebeck 53 };
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