/[svn]/linuxsampler/trunk/src/common/RingBuffer.h
ViewVC logotype

Contents of /linuxsampler/trunk/src/common/RingBuffer.h

Parent Directory Parent Directory | Revision Log Revision Log


Revision 56 - (show 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 /***************************************************************************
2 * *
3 * LinuxSampler - modular, streaming capable sampler *
4 * *
5 * Copyright (C) 2003, 2004 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
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