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