1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-09 23:34:20 +00:00
JUCE/modules/juce_audio_plugin_client/AU/AudioUnitSDK/AUBuffer.cpp

188 lines
5.6 KiB
C++

/*!
@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