|
- /*!
- @file AudioUnitSDK/AUBuffer.cpp
- @copyright © 2000-2021 Apple Inc. All rights reserved.
- */
- #include <AudioUnitSDK/AUBuffer.h>
- #include <AudioUnitSDK/AUUtility.h>
-
- namespace ausdk {
-
- inline void ThrowBadAlloc()
- {
- AUSDK_LogError("AUBuffer throwing bad_alloc");
- throw std::bad_alloc();
- }
-
- // x: number to be rounded; y: the power of 2 to which to round
- constexpr uint32_t RoundUpToMultipleOfPowerOf2(uint32_t x, uint32_t y) noexcept
- {
- const auto mask = y - 1;
- #if DEBUG
- assert((mask & y) == 0u); // verifies that y is a power of 2 NOLINT
- #endif
- return (x + mask) & ~mask;
- }
-
- // a * b + c
- static UInt32 SafeMultiplyAddUInt32(UInt32 a, UInt32 b, UInt32 c)
- {
- if (a == 0 || b == 0) {
- return c; // prevent zero divide
- }
-
- if (a > (0xFFFFFFFF - c) / b) { // NOLINT magic
- ThrowBadAlloc();
- }
-
- return a * b + c;
- }
-
- AllocatedBuffer* BufferAllocator::Allocate(
- UInt32 numberBuffers, UInt32 maxBytesPerBuffer, UInt32 /*reservedFlags*/)
- {
- constexpr size_t kAlignment = 16;
- constexpr size_t kMaxBufferListSize = 65536;
-
- // Check for a reasonable number of buffers (obviate a more complicated check with offsetof).
- if (numberBuffers > kMaxBufferListSize / sizeof(AudioBuffer)) {
- throw std::out_of_range("AudioBuffers::Allocate: Too many buffers");
- }
-
- maxBytesPerBuffer = RoundUpToMultipleOfPowerOf2(maxBytesPerBuffer, kAlignment);
-
- const auto bufferDataSize = SafeMultiplyAddUInt32(numberBuffers, maxBytesPerBuffer, 0);
- void* bufferData = nullptr;
- if (bufferDataSize > 0) {
- bufferData = malloc(bufferDataSize);
- // don't use calloc(); it might not actually touch the memory and cause a VM fault later
- memset(bufferData, 0, bufferDataSize);
- }
-
- const auto implSize = static_cast<uint32_t>(
- offsetof(AllocatedBuffer, mAudioBufferList.mBuffers[std::max(UInt32(1), numberBuffers)]));
- auto* const implMem = malloc(implSize);
- auto* const allocatedBuffer =
- new (implMem) AllocatedBuffer{ .mMaximumNumberBuffers = numberBuffers,
- .mMaximumBytesPerBuffer = maxBytesPerBuffer,
- .mHeaderSize = implSize,
- .mBufferDataSize = bufferDataSize,
- .mBufferData = bufferData };
- allocatedBuffer->mAudioBufferList.mNumberBuffers = numberBuffers;
- return allocatedBuffer;
- }
-
- void BufferAllocator::Deallocate(AllocatedBuffer* allocatedBuffer)
- {
- if (allocatedBuffer->mBufferData != nullptr) {
- free(allocatedBuffer->mBufferData);
- }
- allocatedBuffer->~AllocatedBuffer();
- free(allocatedBuffer);
- }
-
-
- AudioBufferList& AllocatedBuffer::Prepare(UInt32 channelsPerBuffer, UInt32 bytesPerBuffer)
- {
- if (mAudioBufferList.mNumberBuffers > mMaximumNumberBuffers) {
- throw std::out_of_range("AllocatedBuffer::Prepare(): too many buffers");
- }
- if (bytesPerBuffer > mMaximumBytesPerBuffer) {
- throw std::out_of_range("AllocatedBuffer::Prepare(): insufficient capacity");
- }
-
- auto* ptr = static_cast<Byte*>(mBufferData);
- auto* const ptrend = ptr + mBufferDataSize;
-
- for (UInt32 bufIdx = 0, nBufs = mAudioBufferList.mNumberBuffers; bufIdx < nBufs; ++bufIdx) {
- auto& buf = mAudioBufferList.mBuffers[bufIdx]; // NOLINT
- buf.mNumberChannels = channelsPerBuffer;
- buf.mDataByteSize = bytesPerBuffer;
- buf.mData = ptr;
- ptr += mMaximumBytesPerBuffer; // NOLINT ptr math
- }
- if (ptr > ptrend) {
- throw std::out_of_range("AllocatedBuffer::Prepare(): insufficient capacity");
- }
- return mAudioBufferList;
- }
-
- AudioBufferList& AllocatedBuffer::PrepareNull(UInt32 channelsPerBuffer, UInt32 bytesPerBuffer)
- {
- if (mAudioBufferList.mNumberBuffers > mMaximumNumberBuffers) {
- throw std::out_of_range("AllocatedBuffer::PrepareNull(): too many buffers");
- }
- for (UInt32 bufIdx = 0, nBufs = mAudioBufferList.mNumberBuffers; bufIdx < nBufs; ++bufIdx) {
- auto& buf = mAudioBufferList.mBuffers[bufIdx]; // NOLINT
- buf.mNumberChannels = channelsPerBuffer;
- buf.mDataByteSize = bytesPerBuffer;
- buf.mData = nullptr;
- }
- return mAudioBufferList;
- }
-
- AudioBufferList& AUBufferList::PrepareBuffer(
- const AudioStreamBasicDescription& format, UInt32 nFrames)
- {
- ausdk::ThrowExceptionIf(nFrames > mAllocatedFrames, kAudioUnitErr_TooManyFramesToProcess);
-
- UInt32 nStreams = 0;
- UInt32 channelsPerStream = 0;
- if (ASBD::IsInterleaved(format)) {
- nStreams = 1;
- channelsPerStream = format.mChannelsPerFrame;
- } else {
- nStreams = format.mChannelsPerFrame;
- channelsPerStream = 1;
- }
-
- ausdk::ThrowExceptionIf(nStreams > mAllocatedStreams, kAudioUnitErr_FormatNotSupported);
- auto& abl = mBuffers->Prepare(channelsPerStream, nFrames * format.mBytesPerFrame);
- mPtrState = EPtrState::ToMyMemory;
- return abl;
- }
-
- AudioBufferList& AUBufferList::PrepareNullBuffer(
- const AudioStreamBasicDescription& format, UInt32 nFrames)
- {
- UInt32 nStreams = 0;
- UInt32 channelsPerStream = 0;
- if (ASBD::IsInterleaved(format)) {
- nStreams = 1;
- channelsPerStream = format.mChannelsPerFrame;
- } else {
- nStreams = format.mChannelsPerFrame;
- channelsPerStream = 1;
- }
-
- ausdk::ThrowExceptionIf(nStreams > mAllocatedStreams, kAudioUnitErr_FormatNotSupported);
- auto& abl = mBuffers->PrepareNull(channelsPerStream, nFrames * format.mBytesPerFrame);
- mPtrState = EPtrState::ToExternalMemory;
- return abl;
- }
-
- void AUBufferList::Allocate(const AudioStreamBasicDescription& format, UInt32 nFrames)
- {
- auto& alloc = BufferAllocator::instance();
- if (mBuffers != nullptr) {
- alloc.Deallocate(mBuffers);
- }
- const uint32_t nstreams = ASBD::IsInterleaved(format) ? 1 : format.mChannelsPerFrame;
- mBuffers = alloc.Allocate(nstreams, nFrames * format.mBytesPerFrame, 0u);
- mAllocatedFrames = nFrames;
- mAllocatedStreams = nstreams;
- mPtrState = EPtrState::Invalid;
- }
-
- void AUBufferList::Deallocate()
- {
- if (mBuffers != nullptr) {
- BufferAllocator::instance().Deallocate(mBuffers);
- mBuffers = nullptr;
- }
-
- mAllocatedFrames = 0;
- mAllocatedStreams = 0;
- mPtrState = EPtrState::Invalid;
- }
-
- } // namespace ausdk
|