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

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

Parent Directory Parent Directory | Revision Log Revision Log


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