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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 18 - (hide annotations) (download) (as text)
Sun Dec 7 05:03:43 2003 UTC (20 years, 4 months ago) by schoenebeck
File MIME type: text/x-c++hdr
File size: 12880 byte(s)
* src/audioio.cpp: added support for Alsa 1.0.0
* src/audiothread.cpp: fixed several bugs in sustain pedal handling
* src/diskthread.cpp: fixed several bugs which occured under extreme
  conditions (endless loop in audiothread, freezing the whole application,
  outage of available disk streams)
* src/voice.cpp: fixed cubic interpolation (disabled by default; you can
  enable it by setting USE_LINEAR_INTERPOLATION to 0 in src/voice.h)
* src/configure.in: added check for Alsa version

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

  ViewVC Help
Powered by ViewVC