AudioRingBuffer.h 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #pragma once
  9. #include <AzCore/std/parallel/lock.h>
  10. #include <AzCore/std/parallel/mutex.h>
  11. #include <AzCore/std/typetraits/is_arithmetic.h>
  12. #define AUDIO_ALLOCATION_ALIGNMENT 16
  13. namespace Audio
  14. {
  15. /*
  16. * RingBufferBase defines an interface for a generic ring buffer.
  17. */
  18. class RingBufferBase
  19. {
  20. public:
  21. AZ_CLASS_ALLOCATOR(RingBufferBase, AZ::SystemAllocator);
  22. RingBufferBase()
  23. {}
  24. RingBufferBase([[maybe_unused]] const size_t numSamples)
  25. {}
  26. virtual ~RingBufferBase()
  27. {}
  28. /**
  29. * Adds new data to the ringbuffer.
  30. * @param source Source buffer to copy from.
  31. * @param numFrames Number of sample frames available to copy.
  32. * @param numChannels Number of channels in the sample data, samples = numFrames * numChannels.
  33. * @return Number of sample frames copied.
  34. */
  35. virtual size_t AddData(const void* source, size_t numFrames, size_t numChannels) = 0;
  36. /**
  37. * Adds new multi-track/multi-channel data to the ringbuffer in interleaved format.
  38. * Not a required interface.
  39. * @param source Source buffer to copy from.
  40. * @param numFrames Number of sample frames available to copy.
  41. * @param numChannels Number of tracks/channels in the source data, numSamples = numFrames * numChannels.
  42. * @return Number of sample frames copied.
  43. */
  44. virtual size_t AddMultiTrackDataInterleaved([[maybe_unused]] const void** source, [[maybe_unused]] size_t numFrames, [[maybe_unused]] size_t numChannels) { return 0; }
  45. /**
  46. * Consumes stored data from the ringbuffer.
  47. * @param dest Where the data will be written to, typically an array of SampleType pointers.
  48. * @param numFrames Number of sample frames requested to consume.
  49. * @param numChannels Number of channels laid out in the dest parameter.
  50. * @param deinterleaveMultichannel In the case of multichannel data, if true do a deinterleaved copy into the dest array channels otherwise straight copy into dest[0].
  51. * @return Number of sample frames consumed.
  52. */
  53. virtual size_t ConsumeData(void** dest, size_t numFrames, size_t numChannels, bool deinterleaveMultichannel = false) = 0;
  54. /**
  55. * Zeros the ringbuffer data and resets indices.
  56. */
  57. virtual void ResetBuffer() = 0;
  58. };
  59. /*
  60. * RingBuffer<T>
  61. *
  62. * m_read ----> m_write ---->
  63. * V V
  64. * +-------------------------------------------------+
  65. * | DATADATADATADATADATADATADATA |
  66. * +-------------------------------------------------+
  67. * ^
  68. * m_buffer
  69. *
  70. * <---------------------m_size---------------------->
  71. */
  72. template <typename SampleType, typename = AZStd::enable_if_t<AZStd::is_arithmetic<SampleType>::value>>
  73. class RingBuffer
  74. : public RingBufferBase
  75. {
  76. public:
  77. AZ_CLASS_ALLOCATOR(RingBuffer<SampleType>, AZ::SystemAllocator);
  78. static const size_t s_bytesPerSample = sizeof(SampleType);
  79. ///////////////////////////////////////////////////////////////////////////////////////////
  80. RingBuffer(size_t numSamples)
  81. {
  82. AZStd::lock_guard<AZStd::mutex> lock(m_mutex);
  83. AllocateData(numSamples);
  84. }
  85. ///////////////////////////////////////////////////////////////////////////////////////////
  86. ~RingBuffer() override
  87. {
  88. AZStd::lock_guard<AZStd::mutex> lock(m_mutex);
  89. DeallocateData();
  90. }
  91. ///////////////////////////////////////////////////////////////////////////////////////////
  92. size_t AddData(const void* source, size_t numFrames, size_t numChannels) override
  93. {
  94. AZStd::lock_guard<AZStd::mutex> lock(m_mutex);
  95. const size_t numSamples = numFrames * numChannels;
  96. if (numSamples > SamplesUnused())
  97. {
  98. // Writing this many samples will cross the read index, can't proceed.
  99. return 0;
  100. }
  101. const SampleType* sourceBuffer = static_cast<const SampleType*>(source);
  102. if (m_write + numSamples < m_size)
  103. {
  104. // write operation won't wrap the buffer
  105. if (sourceBuffer)
  106. {
  107. size_t copySize = numSamples * s_bytesPerSample;
  108. ::memcpy(&m_buffer[m_write], sourceBuffer, copySize);
  109. }
  110. else
  111. {
  112. ::memset(&m_buffer[m_write], 0, numSamples * s_bytesPerSample);
  113. }
  114. m_write += numSamples;
  115. }
  116. else
  117. {
  118. // Split the copy operations to handle wrap-around
  119. size_t currentToEndSamples = m_size - m_write;
  120. size_t wraparoundSamples = numSamples - currentToEndSamples;
  121. if (sourceBuffer)
  122. {
  123. ::memcpy(&m_buffer[m_write], sourceBuffer, currentToEndSamples * s_bytesPerSample);
  124. ::memcpy(m_buffer, &sourceBuffer[currentToEndSamples], wraparoundSamples * s_bytesPerSample);
  125. }
  126. else
  127. {
  128. ::memset(&m_buffer[m_write], 0, currentToEndSamples * s_bytesPerSample);
  129. ::memset(m_buffer, 0, wraparoundSamples * s_bytesPerSample);
  130. }
  131. m_write = wraparoundSamples;
  132. }
  133. return numFrames;
  134. }
  135. ///////////////////////////////////////////////////////////////////////////////////////////
  136. size_t AddMultiTrackDataInterleaved(const void** source, size_t numFrames, size_t numChannels) override
  137. {
  138. AZStd::lock_guard<AZStd::mutex> lock(m_mutex);
  139. const size_t numSamples = numFrames * numChannels;
  140. if (numSamples > SamplesUnused())
  141. {
  142. // Writing this many samples will cross the read index, can't proceed.
  143. // Consumption needs to occur first to free up room for input.
  144. return 0;
  145. }
  146. if (m_write + numSamples < m_size)
  147. {
  148. // write operation won't wrap the buffer
  149. const SampleType** sourceBufferChannels = reinterpret_cast<const SampleType**>(source);
  150. AZ_ErrorOnce("AudioRingBuffer", sourceBufferChannels, "AudioRingBuffer - Multi-track source buffers not found!\n");
  151. if (sourceBufferChannels)
  152. {
  153. for (size_t channel = 0; channel < numChannels; ++channel)
  154. {
  155. AZ_ErrorOnce("AudioRingBuffer", sourceBufferChannels[channel], "AudioRingBufffer - Multi-track source contains a null buffer at channel %zu!\n", channel);
  156. const SampleType* sourceBuffer = sourceBufferChannels[channel];
  157. if (sourceBuffer)
  158. {
  159. for (size_t frame = 0; frame < numFrames; ++frame)
  160. {
  161. m_buffer[m_write + channel + (numChannels * frame)] = *sourceBuffer++;
  162. }
  163. }
  164. else
  165. {
  166. for (size_t frame = 0; frame < numFrames; ++frame)
  167. {
  168. m_buffer[m_write + channel + (numChannels * frame)] = 0;
  169. }
  170. }
  171. }
  172. }
  173. else
  174. {
  175. ::memset(&m_buffer[m_write], 0, numSamples * s_bytesPerSample);
  176. }
  177. m_write += numSamples;
  178. }
  179. else
  180. {
  181. // split the copy operations to handle wrap-around
  182. size_t currentToEndSamples = m_size - m_write;
  183. size_t wraparoundSamples = numSamples - currentToEndSamples;
  184. const SampleType** sourceBufferChannels = reinterpret_cast<const SampleType**>(source);
  185. AZ_ErrorOnce("AudioRingBuffer", sourceBufferChannels, "AudioRingBuffer - Multi-track source buffers not found!\n");
  186. if (sourceBufferChannels)
  187. {
  188. size_t currentToEndFrames = (currentToEndSamples) / numChannels;
  189. size_t wraparoundFrames = (numFrames - currentToEndFrames);
  190. for (size_t channel = 0; channel < numChannels; ++channel)
  191. {
  192. AZ_ErrorOnce("AudioRingBuffer", sourceBufferChannels[channel], "AudioRingBufffer - Multi-track source contains a null buffer at channel %zu!\n", channel);
  193. const SampleType* sourceBuffer = sourceBufferChannels[channel];
  194. if (sourceBuffer)
  195. {
  196. for (size_t frame = 0; frame < currentToEndFrames; ++frame)
  197. {
  198. m_buffer[m_write + channel + (numChannels * frame)] = *sourceBuffer++;
  199. }
  200. for (size_t frame = 0; frame < wraparoundFrames; ++frame)
  201. {
  202. m_buffer[channel + (numChannels * frame)] = *sourceBuffer++;
  203. }
  204. }
  205. else
  206. {
  207. for (size_t frame = 0; frame < currentToEndFrames; ++frame)
  208. {
  209. m_buffer[m_write + channel + (numChannels * frame)] = 0;
  210. }
  211. for (size_t frame = 0; frame < wraparoundFrames; ++frame)
  212. {
  213. m_buffer[channel + (numChannels * frame)] = 0;
  214. }
  215. }
  216. }
  217. }
  218. else
  219. {
  220. ::memset(&m_buffer[m_write], 0, currentToEndSamples * s_bytesPerSample);
  221. ::memset(m_buffer, 0, wraparoundSamples * s_bytesPerSample);
  222. }
  223. m_write = wraparoundSamples;
  224. }
  225. return numFrames;
  226. }
  227. ///////////////////////////////////////////////////////////////////////////////////////////
  228. size_t ConsumeData(void** dest, size_t numFrames, size_t numChannels, bool deinterleaveMultichannel) override
  229. {
  230. if (!dest)
  231. {
  232. return 0;
  233. }
  234. AZStd::lock_guard<AZStd::mutex> lock(m_mutex);
  235. SampleType** destBuffers = reinterpret_cast<SampleType**>(dest);
  236. size_t numSamples = numFrames * numChannels;
  237. size_t samplesReady = SamplesReady();
  238. if (samplesReady == 0)
  239. {
  240. return 0;
  241. }
  242. else if (numSamples > samplesReady)
  243. {
  244. numSamples = samplesReady;
  245. numFrames = numSamples / numChannels; // ?? could this give incorrect number ??
  246. }
  247. if (numChannels == 2 && deinterleaveMultichannel)
  248. {
  249. if (m_write > m_read)
  250. {
  251. // do 2ch deinterleaved copy
  252. for (AZ::u32 frame = 0; frame < numFrames; ++frame)
  253. {
  254. destBuffers[0][frame] = m_buffer[m_read++];
  255. destBuffers[1][frame] = m_buffer[m_read++];
  256. }
  257. }
  258. else
  259. {
  260. size_t currentToEndFrames = (m_size - m_read) / numChannels;
  261. if (currentToEndFrames > numFrames)
  262. {
  263. // do 2ch deinterleaved copy
  264. for (AZ::u32 frame = 0; frame < numFrames; ++frame)
  265. {
  266. destBuffers[0][frame] = m_buffer[m_read++];
  267. destBuffers[1][frame] = m_buffer[m_read++];
  268. }
  269. }
  270. else
  271. {
  272. // do two partial 2ch deinterleaved copies
  273. AZ::u32 frame = 0;
  274. for (; frame < currentToEndFrames; ++frame)
  275. {
  276. destBuffers[0][frame] = m_buffer[m_read++];
  277. destBuffers[1][frame] = m_buffer[m_read++];
  278. }
  279. m_read = 0;
  280. for (; frame < numFrames; ++frame)
  281. {
  282. destBuffers[0][frame] = m_buffer[m_read++];
  283. destBuffers[1][frame] = m_buffer[m_read++];
  284. }
  285. }
  286. }
  287. }
  288. else if (numChannels == 6 && deinterleaveMultichannel)
  289. {
  290. if (m_write > m_read)
  291. {
  292. // do 6ch deinterleaved copy
  293. for (AZ::u32 frame = 0; frame < numFrames; ++frame)
  294. {
  295. destBuffers[0][frame] = m_buffer[m_read++];
  296. destBuffers[1][frame] = m_buffer[m_read++];
  297. destBuffers[2][frame] = m_buffer[m_read++];
  298. destBuffers[3][frame] = m_buffer[m_read++];
  299. destBuffers[4][frame] = m_buffer[m_read++];
  300. destBuffers[5][frame] = m_buffer[m_read++];
  301. }
  302. }
  303. else
  304. {
  305. size_t currentToEndFrames = (m_size - m_read) / numChannels;
  306. if (currentToEndFrames > numFrames)
  307. {
  308. // do 6ch deinterleaved copy
  309. for (AZ::u32 frame = 0; frame < numFrames; ++frame)
  310. {
  311. destBuffers[0][frame] = m_buffer[m_read++];
  312. destBuffers[1][frame] = m_buffer[m_read++];
  313. destBuffers[2][frame] = m_buffer[m_read++];
  314. destBuffers[3][frame] = m_buffer[m_read++];
  315. destBuffers[4][frame] = m_buffer[m_read++];
  316. destBuffers[5][frame] = m_buffer[m_read++];
  317. }
  318. }
  319. else
  320. {
  321. // do two partial 6ch deinterleaved copies
  322. AZ::u32 frame = 0;
  323. for (; frame < currentToEndFrames; ++frame)
  324. {
  325. destBuffers[0][frame] = m_buffer[m_read++];
  326. destBuffers[1][frame] = m_buffer[m_read++];
  327. destBuffers[2][frame] = m_buffer[m_read++];
  328. destBuffers[3][frame] = m_buffer[m_read++];
  329. destBuffers[4][frame] = m_buffer[m_read++];
  330. destBuffers[5][frame] = m_buffer[m_read++];
  331. }
  332. m_read = 0;
  333. for (; frame < numFrames; ++frame)
  334. {
  335. destBuffers[0][frame] = m_buffer[m_read++];
  336. destBuffers[1][frame] = m_buffer[m_read++];
  337. destBuffers[2][frame] = m_buffer[m_read++];
  338. destBuffers[3][frame] = m_buffer[m_read++];
  339. destBuffers[4][frame] = m_buffer[m_read++];
  340. destBuffers[5][frame] = m_buffer[m_read++];
  341. }
  342. }
  343. }
  344. }
  345. else
  346. {
  347. // single channel or interleaved copy, can do straight memcpy's
  348. if (m_write > m_read)
  349. {
  350. ::memcpy(destBuffers[0], &m_buffer[m_read], numSamples * s_bytesPerSample);
  351. m_read += numSamples;
  352. }
  353. else
  354. {
  355. size_t currentToEndSamples = m_size - m_read;
  356. if (currentToEndSamples > numSamples)
  357. {
  358. ::memcpy(destBuffers[0], &m_buffer[m_read], numSamples * s_bytesPerSample);
  359. m_read += numSamples;
  360. }
  361. else
  362. {
  363. size_t wraparoundSamples = numSamples - currentToEndSamples;
  364. ::memcpy(destBuffers[0], &m_buffer[m_read], currentToEndSamples * s_bytesPerSample);
  365. ::memcpy(&destBuffers[0][currentToEndSamples], m_buffer, wraparoundSamples * s_bytesPerSample);
  366. m_read = wraparoundSamples;
  367. }
  368. }
  369. }
  370. return numFrames;
  371. }
  372. protected:
  373. ///////////////////////////////////////////////////////////////////////////////////////////
  374. void ResetBuffer() override
  375. {
  376. // no lock! should only be called inside api that has already locked the buffer.
  377. // 0xAA is used rather than 0 to intitialze the buffer to make debugging easier as samples often
  378. // lead with 0's, it's more obvious when and where data is being written
  379. ::memset(m_buffer, 0xAA, m_size * s_bytesPerSample);
  380. m_write = 0;
  381. m_read = 0;
  382. }
  383. ///////////////////////////////////////////////////////////////////////////////////////////
  384. void AllocateData(size_t numSamples)
  385. {
  386. // no lock! should only be called inside api that has already locked the buffer.
  387. if (m_buffer)
  388. {
  389. DeallocateData();
  390. }
  391. m_size = numSamples;
  392. m_buffer = static_cast<SampleType*>(azmalloc(numSamples * s_bytesPerSample, AUDIO_ALLOCATION_ALIGNMENT, AZ::SystemAllocator));
  393. ResetBuffer();
  394. }
  395. ///////////////////////////////////////////////////////////////////////////////////////////
  396. void DeallocateData()
  397. {
  398. // no lock! should only be called inside api that has already locked the buffer.
  399. if (m_buffer)
  400. {
  401. azfree(m_buffer, AZ::SystemAllocator);
  402. m_buffer = nullptr;
  403. }
  404. }
  405. ///////////////////////////////////////////////////////////////////////////////////////////
  406. // Returns the number of samples in the ring buffer that are ready for consumption.
  407. size_t SamplesReady() const
  408. {
  409. // no lock! should only be called inside api that has already locked the buffer.
  410. if (m_read > m_write)
  411. { // read-head needs to wrap around to catch up to write-head
  412. return (m_write + m_size - m_read);
  413. }
  414. else
  415. { // read-head is behind write-head and won't wrap around
  416. return (m_write - m_read);
  417. }
  418. }
  419. ///////////////////////////////////////////////////////////////////////////////////////////
  420. // Returns the number of samples in the ring buffer that can be filled.
  421. size_t SamplesUnused() const
  422. {
  423. // no lock! should only be called inside api that has already locked the buffer.
  424. return m_size - SamplesReady();
  425. }
  426. private:
  427. SampleType* m_buffer = nullptr; // sample buffer
  428. size_t m_write = 0; // write-head index into buffer
  429. size_t m_read = 0; // read-head index into buffer
  430. size_t m_size = 0; // total size of buffer in samples. bytes = (m_size * s_bytesPerSample)
  431. AZStd::mutex m_mutex; // protects buffer access
  432. };
  433. } // namespace Audio