Home | History | Annotate | Download | only in portaudio
      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