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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 18 - (show 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 /***************************************************************************
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
80 inline int push(T* src) { return write(src,1); }
81 inline int pop(T* dst) { return read(dst,1); }
82
83 __inline T *get_buffer_begin();
84
85 __inline T *get_read_ptr(void) {
86 return(&buf[atomic_read(&read_ptr)]);
87 }
88
89 /**
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 __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