The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

189 lines
5.6KB

  1. /*!
  2. @file AudioUnitSDK/AUBuffer.cpp
  3. @copyright © 2000-2021 Apple Inc. All rights reserved.
  4. */
  5. #include <AudioUnitSDK/AUBuffer.h>
  6. #include <AudioUnitSDK/AUUtility.h>
  7. namespace ausdk {
  8. inline void ThrowBadAlloc()
  9. {
  10. AUSDK_LogError("AUBuffer throwing bad_alloc");
  11. throw std::bad_alloc();
  12. }
  13. // x: number to be rounded; y: the power of 2 to which to round
  14. constexpr uint32_t RoundUpToMultipleOfPowerOf2(uint32_t x, uint32_t y) noexcept
  15. {
  16. const auto mask = y - 1;
  17. #if DEBUG
  18. assert((mask & y) == 0u); // verifies that y is a power of 2 NOLINT
  19. #endif
  20. return (x + mask) & ~mask;
  21. }
  22. // a * b + c
  23. static UInt32 SafeMultiplyAddUInt32(UInt32 a, UInt32 b, UInt32 c)
  24. {
  25. if (a == 0 || b == 0) {
  26. return c; // prevent zero divide
  27. }
  28. if (a > (0xFFFFFFFF - c) / b) { // NOLINT magic
  29. ThrowBadAlloc();
  30. }
  31. return a * b + c;
  32. }
  33. AllocatedBuffer* BufferAllocator::Allocate(
  34. UInt32 numberBuffers, UInt32 maxBytesPerBuffer, UInt32 /*reservedFlags*/)
  35. {
  36. constexpr size_t kAlignment = 16;
  37. constexpr size_t kMaxBufferListSize = 65536;
  38. // Check for a reasonable number of buffers (obviate a more complicated check with offsetof).
  39. if (numberBuffers > kMaxBufferListSize / sizeof(AudioBuffer)) {
  40. throw std::out_of_range("AudioBuffers::Allocate: Too many buffers");
  41. }
  42. maxBytesPerBuffer = RoundUpToMultipleOfPowerOf2(maxBytesPerBuffer, kAlignment);
  43. const auto bufferDataSize = SafeMultiplyAddUInt32(numberBuffers, maxBytesPerBuffer, 0);
  44. void* bufferData = nullptr;
  45. if (bufferDataSize > 0) {
  46. bufferData = malloc(bufferDataSize);
  47. // don't use calloc(); it might not actually touch the memory and cause a VM fault later
  48. memset(bufferData, 0, bufferDataSize);
  49. }
  50. const auto implSize = static_cast<uint32_t>(
  51. offsetof(AllocatedBuffer, mAudioBufferList.mBuffers[std::max(UInt32(1), numberBuffers)]));
  52. auto* const implMem = malloc(implSize);
  53. auto* const allocatedBuffer =
  54. new (implMem) AllocatedBuffer{ .mMaximumNumberBuffers = numberBuffers,
  55. .mMaximumBytesPerBuffer = maxBytesPerBuffer,
  56. .mHeaderSize = implSize,
  57. .mBufferDataSize = bufferDataSize,
  58. .mBufferData = bufferData };
  59. allocatedBuffer->mAudioBufferList.mNumberBuffers = numberBuffers;
  60. return allocatedBuffer;
  61. }
  62. void BufferAllocator::Deallocate(AllocatedBuffer* allocatedBuffer)
  63. {
  64. if (allocatedBuffer->mBufferData != nullptr) {
  65. free(allocatedBuffer->mBufferData);
  66. }
  67. allocatedBuffer->~AllocatedBuffer();
  68. free(allocatedBuffer);
  69. }
  70. AudioBufferList& AllocatedBuffer::Prepare(UInt32 channelsPerBuffer, UInt32 bytesPerBuffer)
  71. {
  72. if (mAudioBufferList.mNumberBuffers > mMaximumNumberBuffers) {
  73. throw std::out_of_range("AllocatedBuffer::Prepare(): too many buffers");
  74. }
  75. if (bytesPerBuffer > mMaximumBytesPerBuffer) {
  76. throw std::out_of_range("AllocatedBuffer::Prepare(): insufficient capacity");
  77. }
  78. auto* ptr = static_cast<Byte*>(mBufferData);
  79. auto* const ptrend = ptr + mBufferDataSize;
  80. for (UInt32 bufIdx = 0, nBufs = mAudioBufferList.mNumberBuffers; bufIdx < nBufs; ++bufIdx) {
  81. auto& buf = mAudioBufferList.mBuffers[bufIdx]; // NOLINT
  82. buf.mNumberChannels = channelsPerBuffer;
  83. buf.mDataByteSize = bytesPerBuffer;
  84. buf.mData = ptr;
  85. ptr += mMaximumBytesPerBuffer; // NOLINT ptr math
  86. }
  87. if (ptr > ptrend) {
  88. throw std::out_of_range("AllocatedBuffer::Prepare(): insufficient capacity");
  89. }
  90. return mAudioBufferList;
  91. }
  92. AudioBufferList& AllocatedBuffer::PrepareNull(UInt32 channelsPerBuffer, UInt32 bytesPerBuffer)
  93. {
  94. if (mAudioBufferList.mNumberBuffers > mMaximumNumberBuffers) {
  95. throw std::out_of_range("AllocatedBuffer::PrepareNull(): too many buffers");
  96. }
  97. for (UInt32 bufIdx = 0, nBufs = mAudioBufferList.mNumberBuffers; bufIdx < nBufs; ++bufIdx) {
  98. auto& buf = mAudioBufferList.mBuffers[bufIdx]; // NOLINT
  99. buf.mNumberChannels = channelsPerBuffer;
  100. buf.mDataByteSize = bytesPerBuffer;
  101. buf.mData = nullptr;
  102. }
  103. return mAudioBufferList;
  104. }
  105. AudioBufferList& AUBufferList::PrepareBuffer(
  106. const AudioStreamBasicDescription& format, UInt32 nFrames)
  107. {
  108. ausdk::ThrowExceptionIf(nFrames > mAllocatedFrames, kAudioUnitErr_TooManyFramesToProcess);
  109. UInt32 nStreams = 0;
  110. UInt32 channelsPerStream = 0;
  111. if (ASBD::IsInterleaved(format)) {
  112. nStreams = 1;
  113. channelsPerStream = format.mChannelsPerFrame;
  114. } else {
  115. nStreams = format.mChannelsPerFrame;
  116. channelsPerStream = 1;
  117. }
  118. ausdk::ThrowExceptionIf(nStreams > mAllocatedStreams, kAudioUnitErr_FormatNotSupported);
  119. auto& abl = mBuffers->Prepare(channelsPerStream, nFrames * format.mBytesPerFrame);
  120. mPtrState = EPtrState::ToMyMemory;
  121. return abl;
  122. }
  123. AudioBufferList& AUBufferList::PrepareNullBuffer(
  124. const AudioStreamBasicDescription& format, UInt32 nFrames)
  125. {
  126. UInt32 nStreams = 0;
  127. UInt32 channelsPerStream = 0;
  128. if (ASBD::IsInterleaved(format)) {
  129. nStreams = 1;
  130. channelsPerStream = format.mChannelsPerFrame;
  131. } else {
  132. nStreams = format.mChannelsPerFrame;
  133. channelsPerStream = 1;
  134. }
  135. ausdk::ThrowExceptionIf(nStreams > mAllocatedStreams, kAudioUnitErr_FormatNotSupported);
  136. auto& abl = mBuffers->PrepareNull(channelsPerStream, nFrames * format.mBytesPerFrame);
  137. mPtrState = EPtrState::ToExternalMemory;
  138. return abl;
  139. }
  140. void AUBufferList::Allocate(const AudioStreamBasicDescription& format, UInt32 nFrames)
  141. {
  142. auto& alloc = BufferAllocator::instance();
  143. if (mBuffers != nullptr) {
  144. alloc.Deallocate(mBuffers);
  145. }
  146. const uint32_t nstreams = ASBD::IsInterleaved(format) ? 1 : format.mChannelsPerFrame;
  147. mBuffers = alloc.Allocate(nstreams, nFrames * format.mBytesPerFrame, 0u);
  148. mAllocatedFrames = nFrames;
  149. mAllocatedStreams = nstreams;
  150. mPtrState = EPtrState::Invalid;
  151. }
  152. void AUBufferList::Deallocate()
  153. {
  154. if (mBuffers != nullptr) {
  155. BufferAllocator::instance().Deallocate(mBuffers);
  156. mBuffers = nullptr;
  157. }
  158. mAllocatedFrames = 0;
  159. mAllocatedStreams = 0;
  160. mPtrState = EPtrState::Invalid;
  161. }
  162. } // namespace ausdk