pa_ringbuffer.c 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. /*
  2. * $Id: pa_ringbuffer.c 1738 2011-08-18 11:47:28Z rossb $
  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. * The text above constitutes the entire PortAudio license; however,
  40. * the PortAudio community also makes the following non-binding requests:
  41. *
  42. * Any person wishing to distribute modifications to the Software is
  43. * requested to send the modifications to the original developer so that
  44. * they can be incorporated into the canonical version. It is also
  45. * requested that these non-binding requests be included along with the
  46. * license above.
  47. */
  48. /**
  49. @file
  50. @ingroup common_src
  51. */
  52. #include <stdio.h>
  53. #include <stdlib.h>
  54. #include <math.h>
  55. #include "pa_ringbuffer.h"
  56. #include <string.h>
  57. #include "pa_memorybarrier.h"
  58. /***************************************************************************
  59. * Initialize FIFO.
  60. * elementCount must be power of 2, returns -1 if not.
  61. */
  62. ring_buffer_size_t PaUtil_InitializeRingBuffer( PaUtilRingBuffer *rbuf, ring_buffer_size_t elementSizeBytes, ring_buffer_size_t elementCount, void *dataPtr )
  63. {
  64. if( ((elementCount-1) & elementCount) != 0) return -1; /* Not Power of two. */
  65. rbuf->bufferSize = elementCount;
  66. rbuf->buffer = (char *)dataPtr;
  67. PaUtil_FlushRingBuffer( rbuf );
  68. rbuf->bigMask = (elementCount*2)-1;
  69. rbuf->smallMask = (elementCount)-1;
  70. rbuf->elementSizeBytes = elementSizeBytes;
  71. return 0;
  72. }
  73. /***************************************************************************
  74. ** Return number of elements available for reading. */
  75. ring_buffer_size_t PaUtil_GetRingBufferReadAvailable( const PaUtilRingBuffer *rbuf )
  76. {
  77. return ( (rbuf->writeIndex - rbuf->readIndex) & rbuf->bigMask );
  78. }
  79. /***************************************************************************
  80. ** Return number of elements available for writing. */
  81. ring_buffer_size_t PaUtil_GetRingBufferWriteAvailable( const PaUtilRingBuffer *rbuf )
  82. {
  83. return ( rbuf->bufferSize - PaUtil_GetRingBufferReadAvailable(rbuf));
  84. }
  85. /***************************************************************************
  86. ** Clear buffer. Should only be called when buffer is NOT being read or written. */
  87. void PaUtil_FlushRingBuffer( PaUtilRingBuffer *rbuf )
  88. {
  89. rbuf->writeIndex = rbuf->readIndex = 0;
  90. }
  91. /***************************************************************************
  92. ** Get address of region(s) to which we can write data.
  93. ** If the region is contiguous, size2 will be zero.
  94. ** If non-contiguous, size2 will be the size of second region.
  95. ** Returns room available to be written or elementCount, whichever is smaller.
  96. */
  97. ring_buffer_size_t PaUtil_GetRingBufferWriteRegions( PaUtilRingBuffer *rbuf, ring_buffer_size_t elementCount,
  98. void **dataPtr1, ring_buffer_size_t *sizePtr1,
  99. void **dataPtr2, ring_buffer_size_t *sizePtr2 )
  100. {
  101. ring_buffer_size_t index;
  102. ring_buffer_size_t available = PaUtil_GetRingBufferWriteAvailable( rbuf );
  103. if( elementCount > available ) elementCount = available;
  104. /* Check to see if write is not contiguous. */
  105. index = rbuf->writeIndex & rbuf->smallMask;
  106. if( (index + elementCount) > rbuf->bufferSize )
  107. {
  108. /* Write data in two blocks that wrap the buffer. */
  109. ring_buffer_size_t firstHalf = rbuf->bufferSize - index;
  110. *dataPtr1 = &rbuf->buffer[index*rbuf->elementSizeBytes];
  111. *sizePtr1 = firstHalf;
  112. *dataPtr2 = &rbuf->buffer[0];
  113. *sizePtr2 = elementCount - firstHalf;
  114. }
  115. else
  116. {
  117. *dataPtr1 = &rbuf->buffer[index*rbuf->elementSizeBytes];
  118. *sizePtr1 = elementCount;
  119. *dataPtr2 = NULL;
  120. *sizePtr2 = 0;
  121. }
  122. if( available )
  123. PaUtil_FullMemoryBarrier(); /* (write-after-read) => full barrier */
  124. return elementCount;
  125. }
  126. /***************************************************************************
  127. */
  128. ring_buffer_size_t PaUtil_AdvanceRingBufferWriteIndex( PaUtilRingBuffer *rbuf, ring_buffer_size_t elementCount )
  129. {
  130. /* ensure that previous writes are seen before we update the write index
  131. (write after write)
  132. */
  133. PaUtil_WriteMemoryBarrier();
  134. return rbuf->writeIndex = (rbuf->writeIndex + elementCount) & rbuf->bigMask;
  135. }
  136. /***************************************************************************
  137. ** Get address of region(s) from which we can read data.
  138. ** If the region is contiguous, size2 will be zero.
  139. ** If non-contiguous, size2 will be the size of second region.
  140. ** Returns room available to be read or elementCount, whichever is smaller.
  141. */
  142. ring_buffer_size_t PaUtil_GetRingBufferReadRegions( PaUtilRingBuffer *rbuf, ring_buffer_size_t elementCount,
  143. void **dataPtr1, ring_buffer_size_t *sizePtr1,
  144. void **dataPtr2, ring_buffer_size_t *sizePtr2 )
  145. {
  146. ring_buffer_size_t index;
  147. ring_buffer_size_t available = PaUtil_GetRingBufferReadAvailable( rbuf ); /* doesn't use memory barrier */
  148. if( elementCount > available ) elementCount = available;
  149. /* Check to see if read is not contiguous. */
  150. index = rbuf->readIndex & rbuf->smallMask;
  151. if( (index + elementCount) > rbuf->bufferSize )
  152. {
  153. /* Write data in two blocks that wrap the buffer. */
  154. ring_buffer_size_t firstHalf = rbuf->bufferSize - index;
  155. *dataPtr1 = &rbuf->buffer[index*rbuf->elementSizeBytes];
  156. *sizePtr1 = firstHalf;
  157. *dataPtr2 = &rbuf->buffer[0];
  158. *sizePtr2 = elementCount - firstHalf;
  159. }
  160. else
  161. {
  162. *dataPtr1 = &rbuf->buffer[index*rbuf->elementSizeBytes];
  163. *sizePtr1 = elementCount;
  164. *dataPtr2 = NULL;
  165. *sizePtr2 = 0;
  166. }
  167. if( available )
  168. PaUtil_ReadMemoryBarrier(); /* (read-after-read) => read barrier */
  169. return elementCount;
  170. }
  171. /***************************************************************************
  172. */
  173. ring_buffer_size_t PaUtil_AdvanceRingBufferReadIndex( PaUtilRingBuffer *rbuf, ring_buffer_size_t elementCount )
  174. {
  175. /* ensure that previous reads (copies out of the ring buffer) are always completed before updating (writing) the read index.
  176. (write-after-read) => full barrier
  177. */
  178. PaUtil_FullMemoryBarrier();
  179. return rbuf->readIndex = (rbuf->readIndex + elementCount) & rbuf->bigMask;
  180. }
  181. /***************************************************************************
  182. ** Return elements written. */
  183. ring_buffer_size_t PaUtil_WriteRingBuffer( PaUtilRingBuffer *rbuf, const void *data, ring_buffer_size_t elementCount )
  184. {
  185. ring_buffer_size_t size1, size2, numWritten;
  186. void *data1, *data2;
  187. numWritten = PaUtil_GetRingBufferWriteRegions( rbuf, elementCount, &data1, &size1, &data2, &size2 );
  188. if( size2 > 0 )
  189. {
  190. memcpy( data1, data, size1*rbuf->elementSizeBytes );
  191. data = ((char *)data) + size1*rbuf->elementSizeBytes;
  192. memcpy( data2, data, size2*rbuf->elementSizeBytes );
  193. }
  194. else
  195. {
  196. memcpy( data1, data, size1*rbuf->elementSizeBytes );
  197. }
  198. PaUtil_AdvanceRingBufferWriteIndex( rbuf, numWritten );
  199. return numWritten;
  200. }
  201. /***************************************************************************
  202. ** Return elements read. */
  203. ring_buffer_size_t PaUtil_ReadRingBuffer( PaUtilRingBuffer *rbuf, void *data, ring_buffer_size_t elementCount )
  204. {
  205. ring_buffer_size_t size1, size2, numRead;
  206. void *data1, *data2;
  207. numRead = PaUtil_GetRingBufferReadRegions( rbuf, elementCount, &data1, &size1, &data2, &size2 );
  208. if( size2 > 0 )
  209. {
  210. memcpy( data, data1, size1*rbuf->elementSizeBytes );
  211. data = ((char *)data) + size1*rbuf->elementSizeBytes;
  212. memcpy( data, data2, size2*rbuf->elementSizeBytes );
  213. }
  214. else
  215. {
  216. memcpy( data, data1, size1*rbuf->elementSizeBytes );
  217. }
  218. PaUtil_AdvanceRingBufferReadIndex( rbuf, numRead );
  219. return numRead;
  220. }