/[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 56 - (hide annotations) (download) (as text)
Tue Apr 27 09:21:58 2004 UTC (19 years, 11 months ago) by schoenebeck
File MIME type: text/x-c++hdr
File size: 12713 byte(s)
updated copyright header for 2004

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     protected:
253     T *buf;
254     atomic_t write_ptr;
255     atomic_t read_ptr;
256     int size_mask;
257     };
258    
259     template<class T> T *
260     RingBuffer<T>::get_write_ptr (void) {
261     return(&buf[atomic_read(&write_ptr)]);
262     }
263    
264     template<class T> T *
265     RingBuffer<T>::get_buffer_begin (void) {
266     return(buf);
267     }
268    
269    
270    
271     template<class T> int
272     RingBuffer<T>::read (T *dest, int cnt)
273    
274     {
275     int free_cnt;
276     int cnt2;
277     int to_read;
278     int n1, n2;
279     int priv_read_ptr;
280    
281     priv_read_ptr=atomic_read(&read_ptr);
282    
283     if ((free_cnt = read_space ()) == 0) {
284     return 0;
285     }
286    
287     to_read = cnt > free_cnt ? free_cnt : cnt;
288    
289     cnt2 = priv_read_ptr + to_read;
290    
291     if (cnt2 > size) {
292     n1 = size - priv_read_ptr;
293     n2 = cnt2 & size_mask;
294     } else {
295     n1 = to_read;
296     n2 = 0;
297     }
298    
299     memcpy (dest, &buf[priv_read_ptr], n1 * sizeof (T));
300     priv_read_ptr = (priv_read_ptr + n1) & size_mask;
301    
302     if (n2) {
303     memcpy (dest+n1, buf, n2 * sizeof (T));
304     priv_read_ptr = n2;
305     }
306    
307     atomic_set(&read_ptr, priv_read_ptr);
308     return to_read;
309     }
310    
311     template<class T> int
312     RingBuffer<T>::write (T *src, int cnt)
313    
314     {
315     int free_cnt;
316     int cnt2;
317     int to_write;
318     int n1, n2;
319     int priv_write_ptr;
320    
321     priv_write_ptr=atomic_read(&write_ptr);
322    
323     if ((free_cnt = write_space ()) == 0) {
324     return 0;
325     }
326    
327     to_write = cnt > free_cnt ? free_cnt : cnt;
328    
329     cnt2 = priv_write_ptr + to_write;
330    
331     if (cnt2 > size) {
332     n1 = size - priv_write_ptr;
333     n2 = cnt2 & size_mask;
334     } else {
335     n1 = to_write;
336     n2 = 0;
337     }
338    
339     memcpy (&buf[priv_write_ptr], src, n1 * sizeof (T));
340     priv_write_ptr = (priv_write_ptr + n1) & size_mask;
341    
342     if (n2) {
343     memcpy (buf, src+n1, n2 * sizeof (T));
344     priv_write_ptr = n2;
345     }
346     atomic_set(&write_ptr, priv_write_ptr);
347     return to_write;
348     }
349    
350    
351     #endif /* RINGBUFFER_H */

  ViewVC Help
Powered by ViewVC