![]() |
Mixxx
|
00001 /* 00002 * $Id: pa_ringbuffer.c 1738 2011-08-18 11:47:28Z rossb $ 00003 * Portable Audio I/O Library 00004 * Ring Buffer utility. 00005 * 00006 * Author: Phil Burk, http://www.softsynth.com 00007 * modified for SMP safety on Mac OS X by Bjorn Roche 00008 * modified for SMP safety on Linux by Leland Lucius 00009 * also, allowed for const where possible 00010 * modified for multiple-byte-sized data elements by Sven Fischer 00011 * 00012 * Note that this is safe only for a single-thread reader and a 00013 * single-thread writer. 00014 * 00015 * This program uses the PortAudio Portable Audio Library. 00016 * For more information see: http://www.portaudio.com 00017 * Copyright (c) 1999-2000 Ross Bencina and Phil Burk 00018 * 00019 * Permission is hereby granted, free of charge, to any person obtaining 00020 * a copy of this software and associated documentation files 00021 * (the "Software"), to deal in the Software without restriction, 00022 * including without limitation the rights to use, copy, modify, merge, 00023 * publish, distribute, sublicense, and/or sell copies of the Software, 00024 * and to permit persons to whom the Software is furnished to do so, 00025 * subject to the following conditions: 00026 * 00027 * The above copyright notice and this permission notice shall be 00028 * included in all copies or substantial portions of the Software. 00029 * 00030 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 00031 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 00032 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 00033 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 00034 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 00035 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 00036 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 00037 */ 00038 00039 /* 00040 * The text above constitutes the entire PortAudio license; however, 00041 * the PortAudio community also makes the following non-binding requests: 00042 * 00043 * Any person wishing to distribute modifications to the Software is 00044 * requested to send the modifications to the original developer so that 00045 * they can be incorporated into the canonical version. It is also 00046 * requested that these non-binding requests be included along with the 00047 * license above. 00048 */ 00049 00055 #include <stdio.h> 00056 #include <stdlib.h> 00057 #include <math.h> 00058 #include <string.h> 00059 00060 #include "util/pa_memorybarrier.h" 00061 #include "util/pa_ringbuffer.h" 00062 00063 /*************************************************************************** 00064 * Initialize FIFO. 00065 * elementCount must be power of 2, returns -1 if not. 00066 */ 00067 ring_buffer_size_t PaUtil_InitializeRingBuffer( PaUtilRingBuffer *rbuf, ring_buffer_size_t elementSizeBytes, ring_buffer_size_t elementCount, void *dataPtr ) 00068 { 00069 if( ((elementCount-1) & elementCount) != 0) return -1; /* Not Power of two. */ 00070 rbuf->bufferSize = elementCount; 00071 rbuf->buffer = (char *)dataPtr; 00072 PaUtil_FlushRingBuffer( rbuf ); 00073 rbuf->bigMask = (elementCount*2)-1; 00074 rbuf->smallMask = (elementCount)-1; 00075 rbuf->elementSizeBytes = elementSizeBytes; 00076 return 0; 00077 } 00078 00079 /*************************************************************************** 00080 ** Return number of elements available for reading. */ 00081 ring_buffer_size_t PaUtil_GetRingBufferReadAvailable( const PaUtilRingBuffer *rbuf ) 00082 { 00083 return ( (rbuf->writeIndex - rbuf->readIndex) & rbuf->bigMask ); 00084 } 00085 /*************************************************************************** 00086 ** Return number of elements available for writing. */ 00087 ring_buffer_size_t PaUtil_GetRingBufferWriteAvailable( const PaUtilRingBuffer *rbuf ) 00088 { 00089 return ( rbuf->bufferSize - PaUtil_GetRingBufferReadAvailable(rbuf)); 00090 } 00091 00092 /*************************************************************************** 00093 ** Clear buffer. Should only be called when buffer is NOT being read or written. */ 00094 void PaUtil_FlushRingBuffer( PaUtilRingBuffer *rbuf ) 00095 { 00096 rbuf->writeIndex = rbuf->readIndex = 0; 00097 } 00098 00099 /*************************************************************************** 00100 ** Get address of region(s) to which we can write data. 00101 ** If the region is contiguous, size2 will be zero. 00102 ** If non-contiguous, size2 will be the size of second region. 00103 ** Returns room available to be written or elementCount, whichever is smaller. 00104 */ 00105 ring_buffer_size_t PaUtil_GetRingBufferWriteRegions( PaUtilRingBuffer *rbuf, ring_buffer_size_t elementCount, 00106 void **dataPtr1, ring_buffer_size_t *sizePtr1, 00107 void **dataPtr2, ring_buffer_size_t *sizePtr2 ) 00108 { 00109 ring_buffer_size_t index; 00110 ring_buffer_size_t available = PaUtil_GetRingBufferWriteAvailable( rbuf ); 00111 if( elementCount > available ) elementCount = available; 00112 /* Check to see if write is not contiguous. */ 00113 index = rbuf->writeIndex & rbuf->smallMask; 00114 if( (index + elementCount) > rbuf->bufferSize ) 00115 { 00116 /* Write data in two blocks that wrap the buffer. */ 00117 ring_buffer_size_t firstHalf = rbuf->bufferSize - index; 00118 *dataPtr1 = &rbuf->buffer[index*rbuf->elementSizeBytes]; 00119 *sizePtr1 = firstHalf; 00120 *dataPtr2 = &rbuf->buffer[0]; 00121 *sizePtr2 = elementCount - firstHalf; 00122 } 00123 else 00124 { 00125 *dataPtr1 = &rbuf->buffer[index*rbuf->elementSizeBytes]; 00126 *sizePtr1 = elementCount; 00127 *dataPtr2 = NULL; 00128 *sizePtr2 = 0; 00129 } 00130 00131 if( available ) 00132 PaUtil_FullMemoryBarrier(); /* (write-after-read) => full barrier */ 00133 00134 return elementCount; 00135 } 00136 00137 00138 /*************************************************************************** 00139 */ 00140 ring_buffer_size_t PaUtil_AdvanceRingBufferWriteIndex( PaUtilRingBuffer *rbuf, ring_buffer_size_t elementCount ) 00141 { 00142 /* ensure that previous writes are seen before we update the write index 00143 (write after write) 00144 */ 00145 PaUtil_WriteMemoryBarrier(); 00146 return rbuf->writeIndex = (rbuf->writeIndex + elementCount) & rbuf->bigMask; 00147 } 00148 00149 /*************************************************************************** 00150 ** Get address of region(s) from which we can read data. 00151 ** If the region is contiguous, size2 will be zero. 00152 ** If non-contiguous, size2 will be the size of second region. 00153 ** Returns room available to be read or elementCount, whichever is smaller. 00154 */ 00155 ring_buffer_size_t PaUtil_GetRingBufferReadRegions( PaUtilRingBuffer *rbuf, ring_buffer_size_t elementCount, 00156 void **dataPtr1, ring_buffer_size_t *sizePtr1, 00157 void **dataPtr2, ring_buffer_size_t *sizePtr2 ) 00158 { 00159 ring_buffer_size_t index; 00160 ring_buffer_size_t available = PaUtil_GetRingBufferReadAvailable( rbuf ); /* doesn't use memory barrier */ 00161 if( elementCount > available ) elementCount = available; 00162 /* Check to see if read is not contiguous. */ 00163 index = rbuf->readIndex & rbuf->smallMask; 00164 if( (index + elementCount) > rbuf->bufferSize ) 00165 { 00166 /* Write data in two blocks that wrap the buffer. */ 00167 ring_buffer_size_t firstHalf = rbuf->bufferSize - index; 00168 *dataPtr1 = &rbuf->buffer[index*rbuf->elementSizeBytes]; 00169 *sizePtr1 = firstHalf; 00170 *dataPtr2 = &rbuf->buffer[0]; 00171 *sizePtr2 = elementCount - firstHalf; 00172 } 00173 else 00174 { 00175 *dataPtr1 = &rbuf->buffer[index*rbuf->elementSizeBytes]; 00176 *sizePtr1 = elementCount; 00177 *dataPtr2 = NULL; 00178 *sizePtr2 = 0; 00179 } 00180 00181 if( available ) 00182 PaUtil_ReadMemoryBarrier(); /* (read-after-read) => read barrier */ 00183 00184 return elementCount; 00185 } 00186 /*************************************************************************** 00187 */ 00188 ring_buffer_size_t PaUtil_AdvanceRingBufferReadIndex( PaUtilRingBuffer *rbuf, ring_buffer_size_t elementCount ) 00189 { 00190 /* ensure that previous reads (copies out of the ring buffer) are always completed before updating (writing) the read index. 00191 (write-after-read) => full barrier 00192 */ 00193 PaUtil_FullMemoryBarrier(); 00194 return rbuf->readIndex = (rbuf->readIndex + elementCount) & rbuf->bigMask; 00195 } 00196 00197 /*************************************************************************** 00198 ** Return elements written. */ 00199 ring_buffer_size_t PaUtil_WriteRingBuffer( PaUtilRingBuffer *rbuf, const void *data, ring_buffer_size_t elementCount ) 00200 { 00201 ring_buffer_size_t size1, size2, numWritten; 00202 void *data1, *data2; 00203 numWritten = PaUtil_GetRingBufferWriteRegions( rbuf, elementCount, &data1, &size1, &data2, &size2 ); 00204 if( size2 > 0 ) 00205 { 00206 00207 memcpy( data1, data, size1*rbuf->elementSizeBytes ); 00208 data = ((char *)data) + size1*rbuf->elementSizeBytes; 00209 memcpy( data2, data, size2*rbuf->elementSizeBytes ); 00210 } 00211 else 00212 { 00213 memcpy( data1, data, size1*rbuf->elementSizeBytes ); 00214 } 00215 PaUtil_AdvanceRingBufferWriteIndex( rbuf, numWritten ); 00216 return numWritten; 00217 } 00218 00219 /*************************************************************************** 00220 ** Return elements read. */ 00221 ring_buffer_size_t PaUtil_ReadRingBuffer( PaUtilRingBuffer *rbuf, void *data, ring_buffer_size_t elementCount ) 00222 { 00223 ring_buffer_size_t size1, size2, numRead; 00224 void *data1, *data2; 00225 numRead = PaUtil_GetRingBufferReadRegions( rbuf, elementCount, &data1, &size1, &data2, &size2 ); 00226 if( size2 > 0 ) 00227 { 00228 memcpy( data, data1, size1*rbuf->elementSizeBytes ); 00229 data = ((char *)data) + size1*rbuf->elementSizeBytes; 00230 memcpy( data, data2, size2*rbuf->elementSizeBytes ); 00231 } 00232 else 00233 { 00234 memcpy( data, data1, size1*rbuf->elementSizeBytes ); 00235 } 00236 PaUtil_AdvanceRingBufferReadIndex( rbuf, numRead ); 00237 return numRead; 00238 }