/[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 277 - (hide 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 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     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 schoenebeck 53 protected:
338     T *buf;
339     atomic_t write_ptr;
340     atomic_t read_ptr;
341     int size_mask;
342 schoenebeck 277
343     friend class _NonVolatileReader<T>;
344 schoenebeck 53 };
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