1 /*
2 * $Id: ringbuffer.c,v 1.3 2006/06/10 21:30:55 dmazzoni Exp $
3 * ringbuffer.c
4 * Ring Buffer utility..
5 *
6 * Author: Phil Burk, http://www.softsynth.com
7 *
8 * This program uses the PortAudio Portable Audio Library.
9 * For more information see: http://www.audiomulch.com/portaudio/
10 * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
11 *
12 * Permission is hereby granted, free of charge, to any person obtaining
13 * a copy of this software and associated documentation files
14 * (the "Software"), to deal in the Software without restriction,
15 * including without limitation the rights to use, copy, modify, merge,
16 * publish, distribute, sublicense, and/or sell copies of the Software,
17 * and to permit persons to whom the Software is furnished to do so,
18 * subject to the following conditions:
19 *
20 * The above copyright notice and this permission notice shall be
21 * included in all copies or substantial portions of the Software.
22 *
23 * Any person wishing to distribute modifications to the Software is
24 * requested to send the modifications to the original developer so that
25 * they can be incorporated into the canonical version.
26 *
27 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
30 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
31 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
32 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 *
35 */
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <math.h>
39 #include "ringbuffer.h"
40 #include <string.h>
41
42 /***************************************************************************
43 * Initialize FIFO.
44 * numBytes must be power of 2, returns -1 if not.
45 */
RingBuffer_Init(RingBuffer * rbuf,long numBytes,void * dataPtr)46 long RingBuffer_Init( RingBuffer *rbuf, long numBytes, void *dataPtr )
47 {
48 if( ((numBytes-1) & numBytes) != 0) return -1; /* Not Power of two. */
49 rbuf->bufferSize = numBytes;
50 rbuf->buffer = (char *)dataPtr;
51 RingBuffer_Flush( rbuf );
52 rbuf->bigMask = (numBytes*2)-1;
53 rbuf->smallMask = (numBytes)-1;
54 return 0;
55 }
56 /***************************************************************************
57 ** Return number of bytes available for reading. */
RingBuffer_GetReadAvailable(RingBuffer * rbuf)58 long RingBuffer_GetReadAvailable( RingBuffer *rbuf )
59 {
60 return ( (rbuf->writeIndex - rbuf->readIndex) & rbuf->bigMask );
61 }
62 /***************************************************************************
63 ** Return number of bytes available for writing. */
RingBuffer_GetWriteAvailable(RingBuffer * rbuf)64 long RingBuffer_GetWriteAvailable( RingBuffer *rbuf )
65 {
66 return ( rbuf->bufferSize - RingBuffer_GetReadAvailable(rbuf));
67 }
68
69 /***************************************************************************
70 ** Clear buffer. Should only be called when buffer is NOT being read. */
RingBuffer_Flush(RingBuffer * rbuf)71 void RingBuffer_Flush( RingBuffer *rbuf )
72 {
73 rbuf->writeIndex = rbuf->readIndex = 0;
74 }
75
76 /***************************************************************************
77 ** Get address of region(s) to which we can write data.
78 ** If the region is contiguous, size2 will be zero.
79 ** If non-contiguous, size2 will be the size of second region.
80 ** Returns room available to be written or numBytes, whichever is smaller.
81 */
RingBuffer_GetWriteRegions(RingBuffer * rbuf,long numBytes,void ** dataPtr1,long * sizePtr1,void ** dataPtr2,long * sizePtr2)82 long RingBuffer_GetWriteRegions( RingBuffer *rbuf, long numBytes,
83 void **dataPtr1, long *sizePtr1,
84 void **dataPtr2, long *sizePtr2 )
85 {
86 long index;
87 long available = RingBuffer_GetWriteAvailable( rbuf );
88 if( numBytes > available ) numBytes = available;
89 /* Check to see if write is not contiguous. */
90 index = rbuf->writeIndex & rbuf->smallMask;
91 if( (index + numBytes) > rbuf->bufferSize )
92 {
93 /* Write data in two blocks that wrap the buffer. */
94 long firstHalf = rbuf->bufferSize - index;
95 *dataPtr1 = &rbuf->buffer[index];
96 *sizePtr1 = firstHalf;
97 *dataPtr2 = &rbuf->buffer[0];
98 *sizePtr2 = numBytes - firstHalf;
99 }
100 else
101 {
102 *dataPtr1 = &rbuf->buffer[index];
103 *sizePtr1 = numBytes;
104 *dataPtr2 = NULL;
105 *sizePtr2 = 0;
106 }
107 return numBytes;
108 }
109
110
111 /***************************************************************************
112 */
RingBuffer_AdvanceWriteIndex(RingBuffer * rbuf,long numBytes)113 long RingBuffer_AdvanceWriteIndex( RingBuffer *rbuf, long numBytes )
114 {
115 return rbuf->writeIndex = (rbuf->writeIndex + numBytes) & rbuf->bigMask;
116 }
117
118 /***************************************************************************
119 ** Get address of region(s) from which we can read data.
120 ** If the region is contiguous, size2 will be zero.
121 ** If non-contiguous, size2 will be the size of second region.
122 ** Returns room available to be written or numBytes, whichever is smaller.
123 */
RingBuffer_GetReadRegions(RingBuffer * rbuf,long numBytes,void ** dataPtr1,long * sizePtr1,void ** dataPtr2,long * sizePtr2)124 long RingBuffer_GetReadRegions( RingBuffer *rbuf, long numBytes,
125 void **dataPtr1, long *sizePtr1,
126 void **dataPtr2, long *sizePtr2 )
127 {
128 long index;
129 long available = RingBuffer_GetReadAvailable( rbuf );
130 if( numBytes > available ) numBytes = available;
131 /* Check to see if read is not contiguous. */
132 index = rbuf->readIndex & rbuf->smallMask;
133 if( (index + numBytes) > rbuf->bufferSize )
134 {
135 /* Write data in two blocks that wrap the buffer. */
136 long firstHalf = rbuf->bufferSize - index;
137 *dataPtr1 = &rbuf->buffer[index];
138 *sizePtr1 = firstHalf;
139 *dataPtr2 = &rbuf->buffer[0];
140 *sizePtr2 = numBytes - firstHalf;
141 }
142 else
143 {
144 *dataPtr1 = &rbuf->buffer[index];
145 *sizePtr1 = numBytes;
146 *dataPtr2 = NULL;
147 *sizePtr2 = 0;
148 }
149 return numBytes;
150 }
151 /***************************************************************************
152 */
RingBuffer_AdvanceReadIndex(RingBuffer * rbuf,long numBytes)153 long RingBuffer_AdvanceReadIndex( RingBuffer *rbuf, long numBytes )
154 {
155 return rbuf->readIndex = (rbuf->readIndex + numBytes) & rbuf->bigMask;
156 }
157
158 /***************************************************************************
159 ** Return bytes written. */
RingBuffer_Write(RingBuffer * rbuf,void * data,long numBytes)160 long RingBuffer_Write( RingBuffer *rbuf, void *data, long numBytes )
161 {
162 long size1, size2, numWritten;
163 void *data1, *data2;
164 numWritten = RingBuffer_GetWriteRegions( rbuf, numBytes, &data1, &size1, &data2, &size2 );
165 if( size2 > 0 )
166 {
167
168 memcpy( data1, data, size1 );
169 data = ((char *)data) + size1;
170 memcpy( data2, data, size2 );
171 }
172 else
173 {
174 memcpy( data1, data, size1 );
175 }
176 RingBuffer_AdvanceWriteIndex( rbuf, numWritten );
177 return numWritten;
178 }
179
180 /***************************************************************************
181 ** Return bytes read. */
RingBuffer_Read(RingBuffer * rbuf,void * data,long numBytes)182 long RingBuffer_Read( RingBuffer *rbuf, void *data, long numBytes )
183 {
184 long size1, size2, numRead;
185 void *data1, *data2;
186 numRead = RingBuffer_GetReadRegions( rbuf, numBytes, &data1, &size1, &data2, &size2 );
187 if( size2 > 0 )
188 {
189 memcpy( data, data1, size1 );
190 data = ((char *)data) + size1;
191 memcpy( data, data2, size2 );
192 }
193 else
194 {
195 memcpy( data, data1, size1 );
196 }
197 RingBuffer_AdvanceReadIndex( rbuf, numRead );
198 return numRead;
199 }
200