/[svn]/linuxsampler/trunk/src/ringbuffer.h
ViewVC logotype

Annotation of /linuxsampler/trunk/src/ringbuffer.h

Parent Directory Parent Directory | Revision Log Revision Log


Revision 28 - (hide annotations) (download) (as text)
Fri Jan 2 00:02:56 2004 UTC (20 years, 3 months ago) by schoenebeck
File MIME type: text/x-c++hdr
File size: 12877 byte(s)
* src/gig.cpp: fixed looping informations ('LoopStart', 'LoopEnd' and
  'LoopSize')
* src/voice.cpp: pitch sample according to keyboard position only if
  keyrange > 1 key, this was the reason that some gig files were horrible
  detuned
* src/audioio.cpp: bigendian specific fix
* src/ringbuffer.h: removed kernel header dependency by adding own atomic.h

1 schoenebeck 9 /***************************************************************************
2     * *
3     * LinuxSampler - modular, streaming capable sampler *
4     * *
5     * Copyright (C) 2003 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     #include <stdio.h>
30    
31     #include <sys/types.h>
32     #include <pthread.h>
33    
34 schoenebeck 28 #include "atomic.h"
35    
36 schoenebeck 9 template<class T>
37     class RingBuffer
38     {
39     public:
40     RingBuffer (int sz, int wrap_elements = DEFAULT_WRAP_ELEMENTS) {
41     int power_of_two;
42    
43     this->wrap_elements = wrap_elements;
44    
45     for (power_of_two = 1;
46     1<<power_of_two < sz;
47     power_of_two++);
48    
49     size = 1<<power_of_two;
50     size_mask = size;
51     size_mask -= 1;
52     atomic_set(&write_ptr, 0);
53     atomic_set(&read_ptr, 0);
54     buf = new T[size + wrap_elements];
55     };
56    
57     virtual ~RingBuffer() {
58     delete [] buf;
59     }
60    
61     /**
62     * Sets all remaining write space elements to zero. The write pointer
63     * will currently not be incremented after, but that might change in
64     * future.
65     */
66     inline void fill_write_space_with_null() {
67     int w = atomic_read(&write_ptr),
68     r = atomic_read(&read_ptr);
69     memset(get_write_ptr(), 0, write_space_to_end());
70     if (r && w >= r) {
71     memset(get_buffer_begin(), 0, r - 1);
72     }
73    
74     // set the wrap space elements to null
75     if (wrap_elements) memset(&buf[size], 0, wrap_elements);
76     }
77    
78     __inline int read (T *dest, int cnt);
79     __inline int write (T *src, int cnt);
80 schoenebeck 18
81     inline int push(T* src) { return write(src,1); }
82     inline int pop(T* dst) { return read(dst,1); }
83    
84 schoenebeck 9 __inline T *get_buffer_begin();
85    
86     __inline T *get_read_ptr(void) {
87     return(&buf[atomic_read(&read_ptr)]);
88     }
89    
90 schoenebeck 18 /**
91     * Returns a pointer to the element from the current read position,
92     * advanced by \a offset elements.
93     */
94     /*inline T* get_read_ptr(int offset) {
95     int r = atomic_read(&read_ptr);
96     r += offset;
97     r &= size_mask;
98     return &buf[r];
99     }*/
100    
101 schoenebeck 9 __inline T *get_write_ptr();
102     __inline void increment_read_ptr(int cnt) {
103     atomic_set(&read_ptr , (atomic_read(&read_ptr) + cnt) & size_mask);
104     }
105     __inline void set_read_ptr(int val) {
106     atomic_set(&read_ptr , val);
107     }
108    
109     __inline void increment_write_ptr(int cnt) {
110     atomic_set(&write_ptr, (atomic_read(&write_ptr) + cnt) & size_mask);
111     }
112    
113     /* this function increments the write_ptr by cnt, if the buffer wraps then
114     subtract size from the write_ptr value so that it stays within 0<write_ptr<size
115     use this function to increment the write ptr after you filled the buffer
116     with a number of elements given by write_space_to_end_with_wrap().
117     This ensures that the data that is written to the buffer fills up all
118     the wrap space that resides past the regular buffer. The wrap_space is needed for
119     interpolation. So that the audio thread sees the ringbuffer like a linear space
120     which allows us to use faster routines.
121     When the buffer wraps the wrap part is memcpy()ied to the beginning of the buffer
122     and the write ptr incremented accordingly.
123     */
124     __inline void increment_write_ptr_with_wrap(int cnt) {
125     int w=atomic_read(&write_ptr);
126     w += cnt;
127     if(w >= size) {
128     w -= size;
129     memcpy(&buf[0], &buf[size], w*sizeof(T));
130     //printf("DEBUG !!!! increment_write_ptr_with_wrap: buffer wrapped, elements wrapped = %d (wrap_elements %d)\n",w,wrap_elements);
131     }
132     atomic_set(&write_ptr, w);
133     }
134    
135     /* this function returns the available write space in the buffer
136     when the read_ptr > write_ptr it returns the space inbetween, otherwise
137     when the read_ptr < write_ptr it returns the space between write_ptr and
138     the buffer end, including the wrap_space.
139     There is an exception to it. When read_ptr <= wrap_elements. In that
140     case we return the write space to buffer end (-1) without the wrap_elements,
141     this is needed because a subsequent increment_write_ptr which copies the
142     data that resides into the wrap space to the beginning of the buffer and increments
143     the write_ptr would cause the write_ptr overstepping the read_ptr which would be an error.
144     So basically the if(r<=wrap_elements) we return the buffer space to end - 1 which
145     ensures that at the next call there will be one element free to write before the buffer wraps
146     and usually (except in EOF situations) the next write_space_to_end_with_wrap() will return
147     1 + wrap_elements which ensures that the wrap part gets fully filled with data
148     */
149     __inline int write_space_to_end_with_wrap() {
150     int w, r;
151    
152     w = atomic_read(&write_ptr);
153     r = atomic_read(&read_ptr);
154     //printf("write_space_to_end: w=%d r=%d\n",w,r);
155     if(r > w) {
156     //printf("DEBUG: write_space_to_end_with_wrap: r>w r=%d w=%d val=%d\n",r,w,r - w - 1);
157     return(r - w - 1);
158     }
159     if(r <= wrap_elements) {
160     //printf("DEBUG: write_space_to_end_with_wrap: ATTENTION r <= wrap_elements: r=%d w=%d val=%d\n",r,w,size - w -1);
161     return(size - w -1);
162     }
163     if(r) {
164     //printf("DEBUG: write_space_to_end_with_wrap: r=%d w=%d val=%d\n",r,w,size - w + wrap_elements);
165     return(size - w + wrap_elements);
166     }
167     //printf("DEBUG: write_space_to_end_with_wrap: r=0 w=%d val=%d\n",w,size - w - 1 + wrap_elements);
168     return(size - w - 1 + wrap_elements);
169     }
170    
171     /* this function adjusts the number of elements to write into the ringbuffer
172     in a way that the size boundary is avoided and that the wrap space always gets
173     entirely filled.
174     cnt contains the write_space_to_end_with_wrap() amount while
175     capped_cnt contains a capped amount of samples to read.
176     normally capped_cnt == cnt but in some cases eg when the disk thread needs
177     to refill tracks with smaller blocks because lots of streams require immediate
178     refill because lots of notes were started simultaneously.
179     In that case we set for example capped_cnt to a fixed amount (< cnt, eg 64k),
180     which helps to reduce the buffer refill latencies that occur between streams.
181     the first if() checks if the current write_ptr + capped_cnt resides within
182     the wrap area but is < size+wrap_elements. in that case we cannot return
183     capped_cnt because it would lead to a write_ptr wrapping and only a partial fill
184     of wrap space which would lead to errors. So we simply return cnt which ensures
185     that the the entire wrap space will get filled correctly.
186     In all other cases (which are not problematic because no write_ptr wrapping
187     occurs) we simply return capped_cnt.
188     */
189     __inline int adjust_write_space_to_avoid_boundary(int cnt, int capped_cnt) {
190     int w;
191     w = atomic_read(&write_ptr);
192     if((w+capped_cnt) >= size && (w+capped_cnt) < (size+wrap_elements)) {
193     //printf("adjust_write_space_to_avoid_boundary returning cnt = %d\n",cnt);
194     return(cnt);
195     }
196     //printf("adjust_write_space_to_avoid_boundary returning capped_cnt = %d\n",capped_cnt);
197     return(capped_cnt);
198     }
199    
200     __inline int write_space_to_end() {
201     int w, r;
202    
203     w = atomic_read(&write_ptr);
204     r = atomic_read(&read_ptr);
205     //printf("write_space_to_end: w=%d r=%d\n",w,r);
206     if(r > w) return(r - w - 1);
207     if(r) return(size - w);
208     return(size - w - 1);
209     }
210    
211     __inline int read_space_to_end() {
212     int w, r;
213    
214     w = atomic_read(&write_ptr);
215     r = atomic_read(&read_ptr);
216     if(w >= r) return(w - r);
217     return(size - r);
218     }
219     __inline void init() {
220     atomic_set(&write_ptr, 0);
221     atomic_set(&read_ptr, 0);
222     // wrap=0;
223     }
224    
225     int write_space () {
226     int w, r;
227    
228     w = atomic_read(&write_ptr);
229     r = atomic_read(&read_ptr);
230    
231     if (w > r) {
232     return ((r - w + size) & size_mask) - 1;
233     } else if (w < r) {
234     return (r - w) - 1;
235     } else {
236     return size - 1;
237     }
238     }
239    
240     int read_space () {
241     int w, r;
242    
243     w = atomic_read(&write_ptr);
244     r = atomic_read(&read_ptr);
245    
246     if (w >= r) {
247     return w - r;
248     } else {
249     return (w - r + size) & size_mask;
250     }
251     }
252    
253     int size;
254     int wrap_elements;
255    
256     protected:
257     T *buf;
258     atomic_t write_ptr;
259     atomic_t read_ptr;
260     int size_mask;
261     };
262    
263     template<class T> T *
264     RingBuffer<T>::get_write_ptr (void) {
265     return(&buf[atomic_read(&write_ptr)]);
266     }
267    
268     template<class T> T *
269     RingBuffer<T>::get_buffer_begin (void) {
270     return(buf);
271     }
272    
273    
274    
275     template<class T> int
276     RingBuffer<T>::read (T *dest, int cnt)
277    
278     {
279     int free_cnt;
280     int cnt2;
281     int to_read;
282     int n1, n2;
283     int priv_read_ptr;
284    
285     priv_read_ptr=atomic_read(&read_ptr);
286    
287     if ((free_cnt = read_space ()) == 0) {
288     return 0;
289     }
290    
291     to_read = cnt > free_cnt ? free_cnt : cnt;
292    
293     cnt2 = priv_read_ptr + to_read;
294    
295     if (cnt2 > size) {
296     n1 = size - priv_read_ptr;
297     n2 = cnt2 & size_mask;
298     } else {
299     n1 = to_read;
300     n2 = 0;
301     }
302    
303     memcpy (dest, &buf[priv_read_ptr], n1 * sizeof (T));
304     priv_read_ptr = (priv_read_ptr + n1) & size_mask;
305    
306     if (n2) {
307     memcpy (dest+n1, buf, n2 * sizeof (T));
308     priv_read_ptr = n2;
309     }
310    
311     atomic_set(&read_ptr, priv_read_ptr);
312     return to_read;
313     }
314    
315     template<class T> int
316     RingBuffer<T>::write (T *src, int cnt)
317    
318     {
319     int free_cnt;
320     int cnt2;
321     int to_write;
322     int n1, n2;
323     int priv_write_ptr;
324    
325     priv_write_ptr=atomic_read(&write_ptr);
326    
327     if ((free_cnt = write_space ()) == 0) {
328     return 0;
329     }
330    
331     to_write = cnt > free_cnt ? free_cnt : cnt;
332    
333     cnt2 = priv_write_ptr + to_write;
334    
335     if (cnt2 > size) {
336     n1 = size - priv_write_ptr;
337     n2 = cnt2 & size_mask;
338     } else {
339     n1 = to_write;
340     n2 = 0;
341     }
342    
343     memcpy (&buf[priv_write_ptr], src, n1 * sizeof (T));
344     priv_write_ptr = (priv_write_ptr + n1) & size_mask;
345    
346     if (n2) {
347     memcpy (buf, src+n1, n2 * sizeof (T));
348     priv_write_ptr = n2;
349     }
350     atomic_set(&write_ptr, priv_write_ptr);
351     return to_write;
352     }
353    
354    
355     #endif /* RINGBUFFER_H */

  ViewVC Help
Powered by ViewVC