From 478058f6cc00c8c81a639864e123a14d395eb46a Mon Sep 17 00:00:00 2001 From: rpm-build Date: Aug 05 2020 14:21:45 +0000 Subject: Various changes --- diff --git a/src/TPCircularBuffer/README.markdown b/src/TPCircularBuffer/README.markdown deleted file mode 100644 index 04056d0..0000000 --- a/src/TPCircularBuffer/README.markdown +++ /dev/null @@ -1,55 +0,0 @@ -A simple, fast circular buffer implementation for audio processing -================================================================== - -A simple C implementation for a circular (ring) buffer. Thread-safe with a single producer and a single consumer, using OSAtomic.h primitives, and avoids any need for buffer wrapping logic by using a virtual memory map technique to place a virtual copy of the buffer straight after the end of the real buffer. - -Usage ------ - -Initialisation and cleanup: `TPCircularBufferInit` and `TPCircularBufferCleanup` to allocate and free resources. - -Producing: Use `TPCircularBufferHead` to get a pointer to write to the buffer, followed by `TPCircularBufferProduce` to submit the written data. `TPCircularBufferProduceBytes` is a convenience routine for writing data straight to the buffer. - -Consuming: Use `TPCircularBufferTail` to get a pointer to the next data to read, followed by `TPCircularBufferConsume` to free up the space once processed. - -TPCircularBuffer+AudioBufferList.(c,h) contain helper functions to queue and dequeue AudioBufferList -structures. These will automatically adjust the mData fields of each buffer to point to 16-byte aligned -regions within the circular buffer. - -Thread safety -------------- - -As long as you restrict multithreaded access to just one producer, and just one consumer, this utility should be thread safe. - -Only one shared variable is used (the buffer fill count), and OSAtomic primitives are used to write to this value to ensure atomicity. - -License -------- - -Copyright (C) 2012-2013 A Tasty Pixel - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - -3. This notice may not be removed or altered from any source distribution. - - ------------------------------------------------------ - -Virtual memory technique originally proposed by [Philip Howard](http://vrb.slashusr.org/), and [adapted to Darwin](http://www.snoize.com/Code/PlayBufferedSoundFile.tar.gz) by [Kurt Revis](http://www.snoize.com) - -See more info at [atastypixel.com](http://atastypixel.com/blog/a-simple-fast-circular-buffer-implementation-for-audio-processing/) - diff --git a/src/TPCircularBuffer/TPCircularBuffer+AudioBufferList.c b/src/TPCircularBuffer/TPCircularBuffer+AudioBufferList.c deleted file mode 100644 index 7b913dc..0000000 --- a/src/TPCircularBuffer/TPCircularBuffer+AudioBufferList.c +++ /dev/null @@ -1,319 +0,0 @@ -// -// TPCircularBuffer+AudioBufferList.c -// Circular/Ring buffer implementation -// -// https://github.com/michaeltyson/TPCircularBuffer -// -// Created by Michael Tyson on 20/03/2012. -// -// Copyright (C) 2012-2013 A Tasty Pixel -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would be -// appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source distribution. -// - -#include "TPCircularBuffer+AudioBufferList.h" -#import - -static double __secondsToHostTicks = 0.0; - -static inline long align16byte(long val) { - if ( val & (16-1) ) { - return val + (16 - (val & (16-1))); - } - return val; -} - -static inline long min(long a, long b) { - return a > b ? b : a; -} - -AudioBufferList *TPCircularBufferPrepareEmptyAudioBufferList(TPCircularBuffer *buffer, int numberOfBuffers, int bytesPerBuffer, const AudioTimeStamp *inTimestamp) { - int32_t availableBytes; - TPCircularBufferABLBlockHeader *block = (TPCircularBufferABLBlockHeader*)TPCircularBufferHead(buffer, &availableBytes); - if ( !block || availableBytes < sizeof(TPCircularBufferABLBlockHeader)+((numberOfBuffers-1)*sizeof(AudioBuffer))+(numberOfBuffers*bytesPerBuffer) ) return NULL; - - #ifdef DEBUG - assert(!((unsigned long)block & 0xF) /* Beware unaligned accesses */); - #endif - - if ( inTimestamp ) { - memcpy(&block->timestamp, inTimestamp, sizeof(AudioTimeStamp)); - } else { - memset(&block->timestamp, 0, sizeof(AudioTimeStamp)); - } - - memset(&block->bufferList, 0, sizeof(AudioBufferList)+((numberOfBuffers-1)*sizeof(AudioBuffer))); - block->bufferList.mNumberBuffers = numberOfBuffers; - - char *dataPtr = (char*)&block->bufferList + sizeof(AudioBufferList)+((numberOfBuffers-1)*sizeof(AudioBuffer)); - for ( int i=0; i availableBytes ) { - return NULL; - } - - block->bufferList.mBuffers[i].mData = dataPtr; - block->bufferList.mBuffers[i].mDataByteSize = bytesPerBuffer; - block->bufferList.mBuffers[i].mNumberChannels = 1; - - dataPtr += bytesPerBuffer; - } - - // Make sure whole buffer (including timestamp and length value) is 16-byte aligned in length - block->totalLength = (UInt32)align16byte(dataPtr - (char*)block); - if ( block->totalLength > availableBytes ) { - return NULL; - } - - return &block->bufferList; -} - -AudioBufferList *TPCircularBufferPrepareEmptyAudioBufferListWithAudioFormat(TPCircularBuffer *buffer, const AudioStreamBasicDescription *audioFormat, UInt32 frameCount, const AudioTimeStamp *timestamp) { - return TPCircularBufferPrepareEmptyAudioBufferList(buffer, - (audioFormat->mFormatFlags & kAudioFormatFlagIsNonInterleaved) ? audioFormat->mChannelsPerFrame : 1, - audioFormat->mBytesPerFrame * frameCount, - timestamp); -} - -void TPCircularBufferProduceAudioBufferList(TPCircularBuffer *buffer, const AudioTimeStamp *inTimestamp) { - int32_t availableBytes; - TPCircularBufferABLBlockHeader *block = (TPCircularBufferABLBlockHeader*)TPCircularBufferHead(buffer, &availableBytes); - - assert(block); - - #ifdef DEBUG - assert(!((unsigned long)block & 0xF) /* Beware unaligned accesses */); - #endif - - assert(block->bufferList.mBuffers[0].mDataByteSize > 0); - - if ( inTimestamp ) { - memcpy(&block->timestamp, inTimestamp, sizeof(AudioTimeStamp)); - } - - UInt32 calculatedLength = (UInt32)(((char*)block->bufferList.mBuffers[block->bufferList.mNumberBuffers-1].mData + block->bufferList.mBuffers[block->bufferList.mNumberBuffers-1].mDataByteSize) - (char*)block); - - // Make sure whole buffer (including timestamp and length value) is 16-byte aligned in length - calculatedLength = (UInt32)align16byte(calculatedLength); - - assert(calculatedLength <= block->totalLength && calculatedLength <= availableBytes); - - block->totalLength = calculatedLength; - - TPCircularBufferProduce(buffer, block->totalLength); -} - -bool TPCircularBufferCopyAudioBufferList(TPCircularBuffer *buffer, const AudioBufferList *inBufferList, const AudioTimeStamp *inTimestamp, UInt32 frames, const AudioStreamBasicDescription *audioDescription) { - if ( frames == 0 ) return true; - - int byteCount = inBufferList->mBuffers[0].mDataByteSize; - if ( frames != kTPCircularBufferCopyAll ) { - byteCount = frames * audioDescription->mBytesPerFrame; - assert(byteCount <= inBufferList->mBuffers[0].mDataByteSize); - } - - if ( byteCount == 0 ) return true; - - AudioBufferList *bufferList = TPCircularBufferPrepareEmptyAudioBufferList(buffer, inBufferList->mNumberBuffers, byteCount, inTimestamp); - if ( !bufferList ) return false; - - for ( int i=0; imNumberBuffers; i++ ) { - memcpy(bufferList->mBuffers[i].mData, inBufferList->mBuffers[i].mData, byteCount); - } - - TPCircularBufferProduceAudioBufferList(buffer, NULL); - - return true; -} - -AudioBufferList *TPCircularBufferNextBufferListAfter(TPCircularBuffer *buffer, const AudioBufferList *bufferList, AudioTimeStamp *outTimestamp) { - int32_t availableBytes; - void *tail = TPCircularBufferTail(buffer, &availableBytes); - void *end = (char*)tail + availableBytes; - assert((void*)bufferList > (void*)tail && (void*)bufferList < end); - - TPCircularBufferABLBlockHeader *originalBlock = (TPCircularBufferABLBlockHeader*)((char*)bufferList - offsetof(TPCircularBufferABLBlockHeader, bufferList)); - - #ifdef DEBUG - assert(!((unsigned long)originalBlock & 0xF) /* Beware unaligned accesses */); - #endif - - TPCircularBufferABLBlockHeader *nextBlock = (TPCircularBufferABLBlockHeader*)((char*)originalBlock + originalBlock->totalLength); - if ( (void*)nextBlock >= end ) return NULL; - - #ifdef DEBUG - assert(!((unsigned long)nextBlock & 0xF) /* Beware unaligned accesses */); - #endif - - if ( outTimestamp ) { - memcpy(outTimestamp, &nextBlock->timestamp, sizeof(AudioTimeStamp)); - } - - return &nextBlock->bufferList; -} - -void TPCircularBufferConsumeNextBufferListPartial(TPCircularBuffer *buffer, int framesToConsume, const AudioStreamBasicDescription *audioFormat) { - assert(framesToConsume >= 0); - - int32_t dontcare; - TPCircularBufferABLBlockHeader *block = (TPCircularBufferABLBlockHeader*)TPCircularBufferTail(buffer, &dontcare); - if ( !block ) return; - - #ifdef DEBUG - assert(!((unsigned long)block & 0xF)); // Beware unaligned accesses - #endif - - int bytesToConsume = (int)min(framesToConsume * audioFormat->mBytesPerFrame, block->bufferList.mBuffers[0].mDataByteSize); - - if ( bytesToConsume == block->bufferList.mBuffers[0].mDataByteSize ) { - TPCircularBufferConsumeNextBufferList(buffer); - return; - } - - for ( int i=0; ibufferList.mNumberBuffers; i++ ) { - assert(bytesToConsume <= block->bufferList.mBuffers[i].mDataByteSize); - - block->bufferList.mBuffers[i].mData = (char*)block->bufferList.mBuffers[i].mData + bytesToConsume; - block->bufferList.mBuffers[i].mDataByteSize -= bytesToConsume; - } - - if ( block->timestamp.mFlags & kAudioTimeStampSampleTimeValid ) { - block->timestamp.mSampleTime += framesToConsume; - } - if ( block->timestamp.mFlags & kAudioTimeStampHostTimeValid ) { - if ( !__secondsToHostTicks ) { - mach_timebase_info_data_t tinfo; - mach_timebase_info(&tinfo); - __secondsToHostTicks = 1.0 / (((double)tinfo.numer / tinfo.denom) * 1.0e-9); - } - - block->timestamp.mHostTime += ((double)framesToConsume / audioFormat->mSampleRate) * __secondsToHostTicks; - } - - // Reposition block forward, just before the audio data, ensuring 16-byte alignment - TPCircularBufferABLBlockHeader *newBlock = (TPCircularBufferABLBlockHeader*)(((unsigned long)block + bytesToConsume) & ~0xFul); - memmove(newBlock, block, sizeof(TPCircularBufferABLBlockHeader) + (block->bufferList.mNumberBuffers-1)*sizeof(AudioBuffer)); - intptr_t bytesFreed = (intptr_t)newBlock - (intptr_t)block; - newBlock->totalLength -= bytesFreed; - TPCircularBufferConsume(buffer, (int32_t)bytesFreed); -} - -void TPCircularBufferDequeueBufferListFrames(TPCircularBuffer *buffer, UInt32 *ioLengthInFrames, const AudioBufferList *outputBufferList, AudioTimeStamp *outTimestamp, const AudioStreamBasicDescription *audioFormat) { - bool hasTimestamp = false; - UInt32 bytesToGo = *ioLengthInFrames * audioFormat->mBytesPerFrame; - UInt32 bytesCopied = 0; - while ( bytesToGo > 0 ) { - AudioBufferList *bufferList = TPCircularBufferNextBufferList(buffer, !hasTimestamp ? outTimestamp : NULL); - if ( !bufferList ) break; - - hasTimestamp = true; - long bytesToCopy = min(bytesToGo, bufferList->mBuffers[0].mDataByteSize); - - if ( outputBufferList ) { - for ( int i=0; imNumberBuffers; i++ ) { - assert(bytesCopied + bytesToCopy <= outputBufferList->mBuffers[i].mDataByteSize); - memcpy((char*)outputBufferList->mBuffers[i].mData + bytesCopied, bufferList->mBuffers[i].mData, bytesToCopy); - } - } - - TPCircularBufferConsumeNextBufferListPartial(buffer, (int)bytesToCopy/audioFormat->mBytesPerFrame, audioFormat); - - bytesToGo -= bytesToCopy; - bytesCopied += bytesToCopy; - } - - *ioLengthInFrames -= bytesToGo / audioFormat->mBytesPerFrame; -} - -UInt32 TPCircularBufferPeekContiguousWrapped(TPCircularBuffer *buffer, AudioTimeStamp *outTimestamp, const AudioStreamBasicDescription *audioFormat, UInt32 contiguousToleranceSampleTime, UInt32 wrapPoint) { - int32_t availableBytes; - TPCircularBufferABLBlockHeader *block = (TPCircularBufferABLBlockHeader*)TPCircularBufferTail(buffer, &availableBytes); - if ( !block ) return 0; - - #ifdef DEBUG - assert(!((unsigned long)block & 0xF) /* Beware unaligned accesses */); - #endif - - if ( outTimestamp ) { - memcpy(outTimestamp, &block->timestamp, sizeof(AudioTimeStamp)); - } - - void *end = (char*)block + availableBytes; - - UInt32 byteCount = 0; - - while ( 1 ) { - byteCount += block->bufferList.mBuffers[0].mDataByteSize; - TPCircularBufferABLBlockHeader *nextBlock = (TPCircularBufferABLBlockHeader*)((char*)block + block->totalLength); - if ( (void*)nextBlock >= end ) { - break; - } - - if ( contiguousToleranceSampleTime != UINT32_MAX ) { - UInt32 frames = block->bufferList.mBuffers[0].mDataByteSize / audioFormat->mBytesPerFrame; - Float64 nextTime = block->timestamp.mSampleTime + frames; - if ( wrapPoint && nextTime > wrapPoint ) nextTime = fmod(nextTime, wrapPoint); - Float64 diff = fabs(nextBlock->timestamp.mSampleTime - nextTime); - if ( diff > contiguousToleranceSampleTime && (!wrapPoint || fabs(diff-wrapPoint) > contiguousToleranceSampleTime) ) { - break; - } - } - - #ifdef DEBUG - assert(!((unsigned long)nextBlock & 0xF) /* Beware unaligned accesses */); - #endif - - block = nextBlock; - } - - return byteCount / audioFormat->mBytesPerFrame; -} - -UInt32 TPCircularBufferPeek(TPCircularBuffer *buffer, AudioTimeStamp *outTimestamp, const AudioStreamBasicDescription *audioFormat) { - return TPCircularBufferPeekContiguousWrapped(buffer, outTimestamp, audioFormat, UINT32_MAX, 0); -} - -UInt32 TPCircularBufferPeekContiguous(TPCircularBuffer *buffer, AudioTimeStamp *outTimestamp, const AudioStreamBasicDescription *audioFormat, UInt32 contiguousToleranceSampleTime) { - return TPCircularBufferPeekContiguousWrapped(buffer, outTimestamp, audioFormat, contiguousToleranceSampleTime, 0); -} - -UInt32 TPCircularBufferGetAvailableSpace(TPCircularBuffer *buffer, const AudioStreamBasicDescription *audioFormat) { - // Look at buffer head; make sure there's space for the block metadata - int32_t availableBytes; - TPCircularBufferABLBlockHeader *block = (TPCircularBufferABLBlockHeader*)TPCircularBufferHead(buffer, &availableBytes); - if ( !block ) return 0; - - #ifdef DEBUG - assert(!((unsigned long)block & 0xF) /* Beware unaligned accesses */); - #endif - - // Now find out how much 16-byte aligned audio we can store in the space available - int numberOfBuffers = audioFormat->mFormatFlags & kAudioFormatFlagIsNonInterleaved ? audioFormat->mChannelsPerFrame : 1; - char * endOfBuffer = (char*)block + availableBytes; - char * dataPtr = (char*)align16byte((long)(&block->bufferList + sizeof(AudioBufferList)+((numberOfBuffers-1)*sizeof(AudioBuffer)))); - if ( dataPtr >= endOfBuffer ) return 0; - int32_t availableAudioBytes = (int)(endOfBuffer - dataPtr); - - int32_t availableAudioBytesPerBuffer = availableAudioBytes / numberOfBuffers; - availableAudioBytesPerBuffer -= (availableAudioBytesPerBuffer % (16-1)); - - return availableAudioBytesPerBuffer > 0 ? availableAudioBytesPerBuffer / audioFormat->mBytesPerFrame : 0; -} diff --git a/src/TPCircularBuffer/TPCircularBuffer+AudioBufferList.h b/src/TPCircularBuffer/TPCircularBuffer+AudioBufferList.h deleted file mode 100644 index dee426f..0000000 --- a/src/TPCircularBuffer/TPCircularBuffer+AudioBufferList.h +++ /dev/null @@ -1,232 +0,0 @@ -// -// TPCircularBuffer+AudioBufferList.h -// Circular/Ring buffer implementation -// -// https://github.com/michaeltyson/TPCircularBuffer -// -// Created by Michael Tyson on 20/03/2012. -// -// Copyright (C) 2012-2013 A Tasty Pixel -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would be -// appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source distribution. -// - -#ifndef TPCircularBuffer_AudioBufferList_h -#define TPCircularBuffer_AudioBufferList_h - -#ifdef __cplusplus -extern "C" { -#endif - -#include "TPCircularBuffer.h" -#include - -#define kTPCircularBufferCopyAll UINT32_MAX - -typedef struct { - AudioTimeStamp timestamp; - UInt32 totalLength; - AudioBufferList bufferList; -} TPCircularBufferABLBlockHeader; - - -/*! - * Prepare an empty buffer list, stored on the circular buffer - * - * @param buffer Circular buffer - * @param numberOfBuffers The number of buffers to be contained within the buffer list - * @param bytesPerBuffer The number of bytes to store for each buffer - * @param timestamp The timestamp associated with the buffer, or NULL. Note that you can also pass a timestamp into TPCircularBufferProduceAudioBufferList, to set it there instead. - * @return The empty buffer list, or NULL if circular buffer has insufficient space - */ -AudioBufferList *TPCircularBufferPrepareEmptyAudioBufferList(TPCircularBuffer *buffer, int numberOfBuffers, int bytesPerBuffer, const AudioTimeStamp *timestamp); - -/*! - * Prepare an empty buffer list, stored on the circular buffer, using an audio description to automatically configure buffer - * - * @param buffer Circular buffer - * @param audioFormat The kind of audio that will be stored - * @param frameCount The number of frames that will be stored - * @param timestamp The timestamp associated with the buffer, or NULL. Note that you can also pass a timestamp into TPCircularBufferProduceAudioBufferList, to set it there instead. - * @return The empty buffer list, or NULL if circular buffer has insufficient space - */ -AudioBufferList *TPCircularBufferPrepareEmptyAudioBufferListWithAudioFormat(TPCircularBuffer *buffer, const AudioStreamBasicDescription *audioFormat, UInt32 frameCount, const AudioTimeStamp *timestamp); - -/*! - * Mark next audio buffer list as ready for reading - * - * This marks the audio buffer list prepared using TPCircularBufferPrepareEmptyAudioBufferList - * as ready for reading. You must not call this function without first calling - * TPCircularBufferPrepareEmptyAudioBufferList. - * - * @param buffer Circular buffer - * @param inTimestamp The timestamp associated with the buffer, or NULL to leave as-is. Note that you can also pass a timestamp into TPCircularBufferPrepareEmptyAudioBufferList, to set it there instead. - */ -void TPCircularBufferProduceAudioBufferList(TPCircularBuffer *buffer, const AudioTimeStamp *inTimestamp); - -/*! - * Copy the audio buffer list onto the buffer - * - * @param buffer Circular buffer - * @param bufferList Buffer list containing audio to copy to buffer - * @param timestamp The timestamp associated with the buffer, or NULL - * @param frames Length of audio in frames. Specify kTPCircularBufferCopyAll to copy the whole buffer (audioFormat can be NULL, in this case) - * @param audioFormat The AudioStreamBasicDescription describing the audio, or NULL if you specify kTPCircularBufferCopyAll to the `frames` argument - * @return YES if buffer list was successfully copied; NO if there was insufficient space - */ -bool TPCircularBufferCopyAudioBufferList(TPCircularBuffer *buffer, const AudioBufferList *bufferList, const AudioTimeStamp *timestamp, UInt32 frames, const AudioStreamBasicDescription *audioFormat); - -/*! - * Get a pointer to the next stored buffer list - * - * @param buffer Circular buffer - * @param outTimestamp On output, if not NULL, the timestamp corresponding to the buffer - * @return Pointer to the next buffer list in the buffer - */ -static __inline__ __attribute__((always_inline)) AudioBufferList *TPCircularBufferNextBufferList(TPCircularBuffer *buffer, AudioTimeStamp *outTimestamp) { - int32_t dontcare; // Length of segment is contained within buffer list, so we can ignore this - TPCircularBufferABLBlockHeader *block = (TPCircularBufferABLBlockHeader*)TPCircularBufferTail(buffer, &dontcare); - if ( !block ) { - if ( outTimestamp ) { - memset(outTimestamp, 0, sizeof(AudioTimeStamp)); - } - return NULL; - } - if ( outTimestamp ) { - memcpy(outTimestamp, &block->timestamp, sizeof(AudioTimeStamp)); - } - return &block->bufferList; -} - -/*! - * Get a pointer to the next stored buffer list after the given one - * - * @param buffer Circular buffer - * @param bufferList Preceding buffer list - * @param outTimestamp On output, if not NULL, the timestamp corresponding to the buffer - * @return Pointer to the next buffer list in the buffer, or NULL - */ -AudioBufferList *TPCircularBufferNextBufferListAfter(TPCircularBuffer *buffer, const AudioBufferList *bufferList, AudioTimeStamp *outTimestamp); - -/*! - * Consume the next buffer list - * - * @param buffer Circular buffer - */ -static __inline__ __attribute__((always_inline)) void TPCircularBufferConsumeNextBufferList(TPCircularBuffer *buffer) { - int32_t dontcare; - TPCircularBufferABLBlockHeader *block = (TPCircularBufferABLBlockHeader*)TPCircularBufferTail(buffer, &dontcare); - if ( !block ) return; - TPCircularBufferConsume(buffer, block->totalLength); -} - -/*! - * Consume a portion of the next buffer list - * - * This will also increment the sample time and host time portions of the timestamp of - * the buffer list, if present. - * - * @param buffer Circular buffer - * @param framesToConsume The number of frames to consume from the buffer list - * @param audioFormat The AudioStreamBasicDescription describing the audio - */ -void TPCircularBufferConsumeNextBufferListPartial(TPCircularBuffer *buffer, int framesToConsume, const AudioStreamBasicDescription *audioFormat); - -/*! - * Consume a certain number of frames from the buffer, possibly from multiple queued buffer lists - * - * Copies the given number of frames from the buffer into outputBufferList, of the - * given audio description, then consumes the audio buffers. If an audio buffer has - * not been entirely consumed, then updates the queued buffer list structure to point - * to the unconsumed data only. - * - * @param buffer Circular buffer - * @param ioLengthInFrames On input, the number of frames in the given audio format to consume; on output, the number of frames provided - * @param outputBufferList The buffer list to copy audio to, or NULL to discard audio. If not NULL, the structure must be initialised properly, and the mData pointers must not be NULL. - * @param outTimestamp On output, if not NULL, the timestamp corresponding to the first audio frame returned - * @param audioFormat The format of the audio stored in the buffer - */ -void TPCircularBufferDequeueBufferListFrames(TPCircularBuffer *buffer, UInt32 *ioLengthInFrames, const AudioBufferList *outputBufferList, AudioTimeStamp *outTimestamp, const AudioStreamBasicDescription *audioFormat); - -/*! - * Determine how many frames of audio are buffered - * - * Given the provided audio format, determines the frame count of all queued buffers - * - * Note: This function should only be used on the consumer thread, not the producer thread. - * - * @param buffer Circular buffer - * @param outTimestamp On output, if not NULL, the timestamp corresponding to the first audio frame - * @param audioFormat The format of the audio stored in the buffer - * @return The number of frames in the given audio format that are in the buffer - */ -UInt32 TPCircularBufferPeek(TPCircularBuffer *buffer, AudioTimeStamp *outTimestamp, const AudioStreamBasicDescription *audioFormat); - -/*! - * Determine how many contiguous frames of audio are buffered - * - * Given the provided audio format, determines the frame count of all queued buffers that are contiguous, - * given their corresponding timestamps (sample time). - * - * Note: This function should only be used on the consumer thread, not the producer thread. - * - * @param buffer Circular buffer - * @param outTimestamp On output, if not NULL, the timestamp corresponding to the first audio frame - * @param audioFormat The format of the audio stored in the buffer - * @param contiguousToleranceSampleTime The number of samples of discrepancy to tolerate - * @return The number of frames in the given audio format that are in the buffer - */ -UInt32 TPCircularBufferPeekContiguous(TPCircularBuffer *buffer, AudioTimeStamp *outTimestamp, const AudioStreamBasicDescription *audioFormat, UInt32 contiguousToleranceSampleTime); - -/*! - * Determine how many contiguous frames of audio are buffered, with wrap around - * - * Like TPCircularBufferPeekContiguous, determines how many contiguous frames are buffered, - * but considers audio that wraps around a region of a given length as also contiguous. This - * is good for audio that loops. - * - * Note: This function should only be used on the consumer thread, not the producer thread. - * - * @param buffer Circular buffer - * @param outTimestamp On output, if not NULL, the timestamp corresponding to the first audio frame - * @param audioFormat The format of the audio stored in the buffer - * @param contiguousToleranceSampleTime The number of samples of discrepancy to tolerate - * @param wrapPoint The point around which the audio may wrap and still be considered contiguous, or 0 to disable - * @return The number of frames in the given audio format that are in the buffer - */ -UInt32 TPCircularBufferPeekContiguousWrapped(TPCircularBuffer *buffer, AudioTimeStamp *outTimestamp, const AudioStreamBasicDescription *audioFormat, UInt32 contiguousToleranceSampleTime, UInt32 wrapPoint); - -/*! - * Determine how many much space there is in the buffer - * - * Given the provided audio format, determines the number of frames of audio that can be buffered. - * - * Note: This function should only be used on the producer thread, not the consumer thread. - * - * @param buffer Circular buffer - * @param audioFormat The format of the audio stored in the buffer - * @return The number of frames in the given audio format that can be stored in the buffer - */ -UInt32 TPCircularBufferGetAvailableSpace(TPCircularBuffer *buffer, const AudioStreamBasicDescription *audioFormat); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/TPCircularBuffer/TPCircularBuffer.c b/src/TPCircularBuffer/TPCircularBuffer.c deleted file mode 100644 index 1d85d6c..0000000 --- a/src/TPCircularBuffer/TPCircularBuffer.c +++ /dev/null @@ -1,149 +0,0 @@ -// -// TPCircularBuffer.c -// Circular/Ring buffer implementation -// -// https://github.com/michaeltyson/TPCircularBuffer -// -// Created by Michael Tyson on 10/12/2011. -// -// Copyright (C) 2012-2013 A Tasty Pixel -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would be -// appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source distribution. -// - -#include "TPCircularBuffer.h" -#include -#include -#include - -#define reportResult(result,operation) (_reportResult((result),(operation),strrchr(__FILE__, '/')+1,__LINE__)) -static inline bool _reportResult(kern_return_t result, const char *operation, const char* file, int line) { - if ( result != ERR_SUCCESS ) { - printf("%s:%d: %s: %s\n", file, line, operation, mach_error_string(result)); - return false; - } - return true; -} - -bool _TPCircularBufferInit(TPCircularBuffer *buffer, int32_t length, size_t structSize) { - - assert(length > 0); - - if ( structSize != sizeof(TPCircularBuffer) ) { - fprintf(stderr, "TPCircularBuffer: Header version mismatch. Check for old versions of TPCircularBuffer in your project\n"); - abort(); - } - - // Keep trying until we get our buffer, needed to handle race conditions - int retries = 3; - while ( true ) { - - buffer->length = (int32_t)round_page(length); // We need whole page sizes - - // Temporarily allocate twice the length, so we have the contiguous address space to - // support a second instance of the buffer directly after - vm_address_t bufferAddress; - kern_return_t result = vm_allocate(mach_task_self(), - &bufferAddress, - buffer->length * 2, - VM_FLAGS_ANYWHERE); // allocate anywhere it'll fit - if ( result != ERR_SUCCESS ) { - if ( retries-- == 0 ) { - reportResult(result, "Buffer allocation"); - return false; - } - // Try again if we fail - continue; - } - - // Now replace the second half of the allocation with a virtual copy of the first half. Deallocate the second half... - result = vm_deallocate(mach_task_self(), - bufferAddress + buffer->length, - buffer->length); - if ( result != ERR_SUCCESS ) { - if ( retries-- == 0 ) { - reportResult(result, "Buffer deallocation"); - return false; - } - // If this fails somehow, deallocate the whole region and try again - vm_deallocate(mach_task_self(), bufferAddress, buffer->length); - continue; - } - - // Re-map the buffer to the address space immediately after the buffer - vm_address_t virtualAddress = bufferAddress + buffer->length; - vm_prot_t cur_prot, max_prot; - result = vm_remap(mach_task_self(), - &virtualAddress, // mirror target - buffer->length, // size of mirror - 0, // auto alignment - 0, // force remapping to virtualAddress - mach_task_self(), // same task - bufferAddress, // mirror source - 0, // MAP READ-WRITE, NOT COPY - &cur_prot, // unused protection struct - &max_prot, // unused protection struct - VM_INHERIT_DEFAULT); - if ( result != ERR_SUCCESS ) { - if ( retries-- == 0 ) { - reportResult(result, "Remap buffer memory"); - return false; - } - // If this remap failed, we hit a race condition, so deallocate and try again - vm_deallocate(mach_task_self(), bufferAddress, buffer->length); - continue; - } - - if ( virtualAddress != bufferAddress+buffer->length ) { - // If the memory is not contiguous, clean up both allocated buffers and try again - if ( retries-- == 0 ) { - printf("Couldn't map buffer memory to end of buffer\n"); - return false; - } - - vm_deallocate(mach_task_self(), virtualAddress, buffer->length); - vm_deallocate(mach_task_self(), bufferAddress, buffer->length); - continue; - } - - buffer->buffer = (void*)bufferAddress; - buffer->fillCount = 0; - buffer->head = buffer->tail = 0; - buffer->atomic = true; - - return true; - } - return false; -} - -void TPCircularBufferCleanup(TPCircularBuffer *buffer) { - vm_deallocate(mach_task_self(), (vm_address_t)buffer->buffer, buffer->length * 2); - memset(buffer, 0, sizeof(TPCircularBuffer)); -} - -void TPCircularBufferClear(TPCircularBuffer *buffer) { - int32_t fillCount; - if ( TPCircularBufferTail(buffer, &fillCount) ) { - TPCircularBufferConsume(buffer, fillCount); - } -} - -void TPCircularBufferSetAtomic(TPCircularBuffer *buffer, bool atomic) { - buffer->atomic = atomic; -} diff --git a/src/TPCircularBuffer/TPCircularBuffer.h b/src/TPCircularBuffer/TPCircularBuffer.h deleted file mode 100644 index f354cfd..0000000 --- a/src/TPCircularBuffer/TPCircularBuffer.h +++ /dev/null @@ -1,240 +0,0 @@ -// -// TPCircularBuffer.h -// Circular/Ring buffer implementation -// -// https://github.com/michaeltyson/TPCircularBuffer -// -// Created by Michael Tyson on 10/12/2011. -// -// -// This implementation makes use of a virtual memory mapping technique that inserts a virtual copy -// of the buffer memory directly after the buffer's end, negating the need for any buffer wrap-around -// logic. Clients can simply use the returned memory address as if it were contiguous space. -// -// The implementation is thread-safe in the case of a single producer and single consumer. -// -// Virtual memory technique originally proposed by Philip Howard (http://vrb.slashusr.org/), and -// adapted to Darwin by Kurt Revis (http://www.snoize.com, -// http://www.snoize.com/Code/PlayBufferedSoundFile.tar.gz) -// -// -// Copyright (C) 2012-2013 A Tasty Pixel -// -// This software is provided 'as-is', without any express or implied -// warranty. In no event will the authors be held liable for any damages -// arising from the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software -// in a product, an acknowledgment in the product documentation would be -// appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source distribution. -// - -#ifndef TPCircularBuffer_h -#define TPCircularBuffer_h - -#include -#include -#include - -#ifdef __cplusplus - extern "C++" { - #include - using namespace std; - } -#else - #include -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct { - void *buffer; - int32_t length; - int32_t tail; - int32_t head; - volatile atomic_int fillCount; - bool atomic; -} TPCircularBuffer; - -/*! - * Initialise buffer - * - * Note that the length is advisory only: Because of the way the - * memory mirroring technique works, the true buffer length will - * be multiples of the device page size (e.g. 4096 bytes) - * - * If you intend to use the AudioBufferList utilities, you should - * always allocate a bit more space than you need for pure audio - * data, so there's room for the metadata. How much extra is required - * depends on how many AudioBufferList structures are used, which is - * a function of how many audio frames each buffer holds. A good rule - * of thumb is to add 15%, or at least another 2048 bytes or so. - * - * @param buffer Circular buffer - * @param length Length of buffer - */ -#define TPCircularBufferInit(buffer, length) \ - _TPCircularBufferInit(buffer, length, sizeof(*buffer)) -bool _TPCircularBufferInit(TPCircularBuffer *buffer, int32_t length, size_t structSize); - -/*! - * Cleanup buffer - * - * Releases buffer resources. - */ -void TPCircularBufferCleanup(TPCircularBuffer *buffer); - -/*! - * Clear buffer - * - * Resets buffer to original, empty state. - * - * This is safe for use by consumer while producer is accessing - * buffer. - */ -void TPCircularBufferClear(TPCircularBuffer *buffer); - -/*! - * Set the atomicity - * - * If you set the atomiticy to false using this method, the buffer will - * not use atomic operations. This can be used to give the compiler a little - * more optimisation opportunities when the buffer is only used on one thread. - * - * Important note: Only set this to false if you know what you're doing! - * - * The default value is true (the buffer will use atomic operations) - * - * @param buffer Circular buffer - * @param atomic Whether the buffer is atomic (default true) - */ -void TPCircularBufferSetAtomic(TPCircularBuffer *buffer, bool atomic); - -// Reading (consuming) - -/*! - * Access end of buffer - * - * This gives you a pointer to the end of the buffer, ready - * for reading, and the number of available bytes to read. - * - * @param buffer Circular buffer - * @param availableBytes On output, the number of bytes ready for reading - * @return Pointer to the first bytes ready for reading, or NULL if buffer is empty - */ -static __inline__ __attribute__((always_inline)) void* TPCircularBufferTail(TPCircularBuffer *buffer, int32_t* availableBytes) { - *availableBytes = buffer->fillCount; - if ( *availableBytes == 0 ) return NULL; - return (void*)((char*)buffer->buffer + buffer->tail); -} - -/*! - * Consume bytes in buffer - * - * This frees up the just-read bytes, ready for writing again. - * - * @param buffer Circular buffer - * @param amount Number of bytes to consume - */ -static __inline__ __attribute__((always_inline)) void TPCircularBufferConsume(TPCircularBuffer *buffer, int32_t amount) { - buffer->tail = (buffer->tail + amount) % buffer->length; - if ( buffer->atomic ) { - atomic_fetch_add(&buffer->fillCount, -amount); - } else { - buffer->fillCount -= amount; - } - assert(buffer->fillCount >= 0); -} - -/*! - * Access front of buffer - * - * This gives you a pointer to the front of the buffer, ready - * for writing, and the number of available bytes to write. - * - * @param buffer Circular buffer - * @param availableBytes On output, the number of bytes ready for writing - * @return Pointer to the first bytes ready for writing, or NULL if buffer is full - */ -static __inline__ __attribute__((always_inline)) void* TPCircularBufferHead(TPCircularBuffer *buffer, int32_t* availableBytes) { - *availableBytes = (buffer->length - buffer->fillCount); - if ( *availableBytes == 0 ) return NULL; - return (void*)((char*)buffer->buffer + buffer->head); -} - -// Writing (producing) - -/*! - * Produce bytes in buffer - * - * This marks the given section of the buffer ready for reading. - * - * @param buffer Circular buffer - * @param amount Number of bytes to produce - */ -static __inline__ __attribute__((always_inline)) void TPCircularBufferProduce(TPCircularBuffer *buffer, int32_t amount) { - buffer->head = (buffer->head + amount) % buffer->length; - if ( buffer->atomic ) { - atomic_fetch_add(&buffer->fillCount, amount); - } else { - buffer->fillCount += amount; - } - assert(buffer->fillCount <= buffer->length); -} - -/*! - * Helper routine to copy bytes to buffer - * - * This copies the given bytes to the buffer, and marks them ready for reading. - * - * @param buffer Circular buffer - * @param src Source buffer - * @param len Number of bytes in source buffer - * @return true if bytes copied, false if there was insufficient space - */ -static __inline__ __attribute__((always_inline)) bool TPCircularBufferProduceBytes(TPCircularBuffer *buffer, const void* src, int32_t len) { - int32_t space; - void *ptr = TPCircularBufferHead(buffer, &space); - if ( space < len ) return false; - memcpy(ptr, src, len); - TPCircularBufferProduce(buffer, len); - return true; -} - -/*! - * Deprecated method - */ -static __inline__ __attribute__((always_inline)) __deprecated_msg("use TPCircularBufferSetAtomic(false) and TPCircularBufferConsume instead") -void TPCircularBufferConsumeNoBarrier(TPCircularBuffer *buffer, int32_t amount) { - buffer->tail = (buffer->tail + amount) % buffer->length; - buffer->fillCount -= amount; - assert(buffer->fillCount >= 0); -} - -/*! - * Deprecated method - */ -static __inline__ __attribute__((always_inline)) __deprecated_msg("use TPCircularBufferSetAtomic(false) and TPCircularBufferProduce instead") -void TPCircularBufferProduceNoBarrier(TPCircularBuffer *buffer, int32_t amount) { - buffer->head = (buffer->head + amount) % buffer->length; - buffer->fillCount += amount; - assert(buffer->fillCount <= buffer->length); -} - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/TPCircularBuffer/TPCircularBuffer.podspec b/src/TPCircularBuffer/TPCircularBuffer.podspec deleted file mode 100644 index 70419fd..0000000 --- a/src/TPCircularBuffer/TPCircularBuffer.podspec +++ /dev/null @@ -1,20 +0,0 @@ -license = <<-END -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." -END - -Pod::Spec.new do |s| - s.name = "TPCircularBuffer" - s.version = '1.4' - s.summary = 'A simple, fast circular buffer implementation.' - s.homepage = 'https://github.com/michaeltyson/TPCircularBuffer' - s.authors = { 'Michael Tyson' => 'michael@atastypixel.com' } - s.license = { :type => 'MIT', :text => license } - s.source = { :git => 'https://github.com/michaeltyson/TPCircularBuffer.git', :tag => '1.4' } - s.source_files = '*.{c,h}' - s.requires_arc = false - s.frameworks = 'AudioToolbox' - s.ios.deployment_target = '4.3' - s.osx.deployment_target = '10.8' -end