1 /* 2 * $Id: pa_ringbuffer.c 1421 2009-11-18 16:09:05Z bjornroche $ 3 * Portable Audio I/O Library 4 * Ring Buffer utility. 5 * 6 * Author: Phil Burk, http://www.softsynth.com 7 * modified for SMP safety on Mac OS X by Bjorn Roche 8 * modified for SMP safety on Linux by Leland Lucius 9 * also, allowed for const where possible 10 * modified for multiple-byte-sized data elements by Sven Fischer 11 * 12 * Note that this is safe only for a single-thread reader and a 13 * single-thread writer. 14 * 15 * This program uses the PortAudio Portable Audio Library. 16 * For more information see: http://www.portaudio.com 17 * Copyright (c) 1999-2000 Ross Bencina and Phil Burk 18 * 19 * Permission is hereby granted, free of charge, to any person obtaining 20 * a copy of this software and associated documentation files 21 * (the "Software"), to deal in the Software without restriction, 22 * including without limitation the rights to use, copy, modify, merge, 23 * publish, distribute, sublicense, and/or sell copies of the Software, 24 * and to permit persons to whom the Software is furnished to do so, 25 * subject to the following conditions: 26 * 27 * The above copyright notice and this permission notice shall be 28 * included in all copies or substantial portions of the Software. 29 * 30 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 31 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 32 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 33 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 34 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 35 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 36 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 37 */ 38 39 /* 40 * The text above constitutes the entire PortAudio license; however, 41 * the PortAudio community also makes the following non-binding requests: 42 * 43 * Any person wishing to distribute modifications to the Software is 44 * requested to send the modifications to the original developer so that 45 * they can be incorporated into the canonical version. It is also 46 * requested that these non-binding requests be included along with the 47 * license above. 48 */ 49 50 /** 51 @file 52 @ingroup common_src 53 */ 54 55 #include <math.h> 56 #include <stdio.h> 57 #include <stdlib.h> 58 #include <string.h> 59 #include "webrtc/modules/audio_device/mac/portaudio/pa_memorybarrier.h" 60 #include "webrtc/modules/audio_device/mac/portaudio/pa_ringbuffer.h" 61 62 /*************************************************************************** 63 * Initialize FIFO. 64 * elementCount must be power of 2, returns -1 if not. 65 */ 66 ring_buffer_size_t PaUtil_InitializeRingBuffer( PaUtilRingBuffer *rbuf, ring_buffer_size_t elementSizeBytes, ring_buffer_size_t elementCount, void *dataPtr ) 67 { 68 if( ((elementCount-1) & elementCount) != 0) return -1; /* Not Power of two. */ 69 rbuf->bufferSize = elementCount; 70 rbuf->buffer = (char *)dataPtr; 71 PaUtil_FlushRingBuffer( rbuf ); 72 rbuf->bigMask = (elementCount*2)-1; 73 rbuf->smallMask = (elementCount)-1; 74 rbuf->elementSizeBytes = elementSizeBytes; 75 return 0; 76 } 77 78 /*************************************************************************** 79 ** Return number of elements available for reading. */ 80 ring_buffer_size_t PaUtil_GetRingBufferReadAvailable( PaUtilRingBuffer *rbuf ) 81 { 82 PaUtil_ReadMemoryBarrier(); 83 return ( (rbuf->writeIndex - rbuf->readIndex) & rbuf->bigMask ); 84 } 85 /*************************************************************************** 86 ** Return number of elements available for writing. */ 87 ring_buffer_size_t PaUtil_GetRingBufferWriteAvailable( PaUtilRingBuffer *rbuf ) 88 { 89 /* Since we are calling PaUtil_GetRingBufferReadAvailable, we don't need an aditional MB */ 90 return ( rbuf->bufferSize - PaUtil_GetRingBufferReadAvailable(rbuf)); 91 } 92 93 /*************************************************************************** 94 ** Clear buffer. Should only be called when buffer is NOT being read. */ 95 void PaUtil_FlushRingBuffer( PaUtilRingBuffer *rbuf ) 96 { 97 rbuf->writeIndex = rbuf->readIndex = 0; 98 } 99 100 /*************************************************************************** 101 ** Get address of region(s) to which we can write data. 102 ** If the region is contiguous, size2 will be zero. 103 ** If non-contiguous, size2 will be the size of second region. 104 ** Returns room available to be written or elementCount, whichever is smaller. 105 */ 106 ring_buffer_size_t PaUtil_GetRingBufferWriteRegions( PaUtilRingBuffer *rbuf, ring_buffer_size_t elementCount, 107 void **dataPtr1, ring_buffer_size_t *sizePtr1, 108 void **dataPtr2, ring_buffer_size_t *sizePtr2 ) 109 { 110 ring_buffer_size_t index; 111 ring_buffer_size_t available = PaUtil_GetRingBufferWriteAvailable( rbuf ); 112 if( elementCount > available ) elementCount = available; 113 /* Check to see if write is not contiguous. */ 114 index = rbuf->writeIndex & rbuf->smallMask; 115 if( (index + elementCount) > rbuf->bufferSize ) 116 { 117 /* Write data in two blocks that wrap the buffer. */ 118 ring_buffer_size_t firstHalf = rbuf->bufferSize - index; 119 *dataPtr1 = &rbuf->buffer[index*rbuf->elementSizeBytes]; 120 *sizePtr1 = firstHalf; 121 *dataPtr2 = &rbuf->buffer[0]; 122 *sizePtr2 = elementCount - firstHalf; 123 } 124 else 125 { 126 *dataPtr1 = &rbuf->buffer[index*rbuf->elementSizeBytes]; 127 *sizePtr1 = elementCount; 128 *dataPtr2 = NULL; 129 *sizePtr2 = 0; 130 } 131 return elementCount; 132 } 133 134 135 /*************************************************************************** 136 */ 137 ring_buffer_size_t PaUtil_AdvanceRingBufferWriteIndex( PaUtilRingBuffer *rbuf, ring_buffer_size_t elementCount ) 138 { 139 /* we need to ensure that previous writes are seen before we update the write index */ 140 PaUtil_WriteMemoryBarrier(); 141 return rbuf->writeIndex = (rbuf->writeIndex + elementCount) & rbuf->bigMask; 142 } 143 144 /*************************************************************************** 145 ** Get address of region(s) from which we can read data. 146 ** If the region is contiguous, size2 will be zero. 147 ** If non-contiguous, size2 will be the size of second region. 148 ** Returns room available to be written or elementCount, whichever is smaller. 149 */ 150 ring_buffer_size_t PaUtil_GetRingBufferReadRegions( PaUtilRingBuffer *rbuf, ring_buffer_size_t elementCount, 151 void **dataPtr1, ring_buffer_size_t *sizePtr1, 152 void **dataPtr2, ring_buffer_size_t *sizePtr2 ) 153 { 154 ring_buffer_size_t index; 155 ring_buffer_size_t available = PaUtil_GetRingBufferReadAvailable( rbuf ); 156 if( elementCount > available ) elementCount = available; 157 /* Check to see if read is not contiguous. */ 158 index = rbuf->readIndex & rbuf->smallMask; 159 if( (index + elementCount) > rbuf->bufferSize ) 160 { 161 /* Write data in two blocks that wrap the buffer. */ 162 ring_buffer_size_t firstHalf = rbuf->bufferSize - index; 163 *dataPtr1 = &rbuf->buffer[index*rbuf->elementSizeBytes]; 164 *sizePtr1 = firstHalf; 165 *dataPtr2 = &rbuf->buffer[0]; 166 *sizePtr2 = elementCount - firstHalf; 167 } 168 else 169 { 170 *dataPtr1 = &rbuf->buffer[index*rbuf->elementSizeBytes]; 171 *sizePtr1 = elementCount; 172 *dataPtr2 = NULL; 173 *sizePtr2 = 0; 174 } 175 return elementCount; 176 } 177 /*************************************************************************** 178 */ 179 ring_buffer_size_t PaUtil_AdvanceRingBufferReadIndex( PaUtilRingBuffer *rbuf, ring_buffer_size_t elementCount ) 180 { 181 /* we need to ensure that previous writes are always seen before updating the index. */ 182 PaUtil_WriteMemoryBarrier(); 183 return rbuf->readIndex = (rbuf->readIndex + elementCount) & rbuf->bigMask; 184 } 185 186 /*************************************************************************** 187 ** Return elements written. */ 188 ring_buffer_size_t PaUtil_WriteRingBuffer( PaUtilRingBuffer *rbuf, const void *data, ring_buffer_size_t elementCount ) 189 { 190 ring_buffer_size_t size1, size2, numWritten; 191 void *data1, *data2; 192 numWritten = PaUtil_GetRingBufferWriteRegions( rbuf, elementCount, &data1, &size1, &data2, &size2 ); 193 if( size2 > 0 ) 194 { 195 196 memcpy( data1, data, size1*rbuf->elementSizeBytes ); 197 data = ((char *)data) + size1*rbuf->elementSizeBytes; 198 memcpy( data2, data, size2*rbuf->elementSizeBytes ); 199 } 200 else 201 { 202 memcpy( data1, data, size1*rbuf->elementSizeBytes ); 203 } 204 PaUtil_AdvanceRingBufferWriteIndex( rbuf, numWritten ); 205 return numWritten; 206 } 207 208 /*************************************************************************** 209 ** Return elements read. */ 210 ring_buffer_size_t PaUtil_ReadRingBuffer( PaUtilRingBuffer *rbuf, void *data, ring_buffer_size_t elementCount ) 211 { 212 ring_buffer_size_t size1, size2, numRead; 213 void *data1, *data2; 214 numRead = PaUtil_GetRingBufferReadRegions( rbuf, elementCount, &data1, &size1, &data2, &size2 ); 215 if( size2 > 0 ) 216 { 217 memcpy( data, data1, size1*rbuf->elementSizeBytes ); 218 data = ((char *)data) + size1*rbuf->elementSizeBytes; 219 memcpy( data, data2, size2*rbuf->elementSizeBytes ); 220 } 221 else 222 { 223 memcpy( data, data1, size1*rbuf->elementSizeBytes ); 224 } 225 PaUtil_AdvanceRingBufferReadIndex( rbuf, numRead ); 226 return numRead; 227 } 228