667 lines
24 KiB
Objective-C
667 lines
24 KiB
Objective-C
//
|
|
// EZAudioUtilities.m
|
|
// EZAudio
|
|
//
|
|
// Created by Syed Haris Ali on 6/23/15.
|
|
// Copyright (c) 2015 Syed Haris Ali. All rights reserved.
|
|
//
|
|
// 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.
|
|
|
|
#import "EZAudioUtilities.h"
|
|
|
|
BOOL __shouldExitOnCheckResultFail = YES;
|
|
|
|
@implementation EZAudioUtilities
|
|
|
|
//------------------------------------------------------------------------------
|
|
#pragma mark - Debugging
|
|
//------------------------------------------------------------------------------
|
|
|
|
+ (void)setShouldExitOnCheckResultFail:(BOOL)shouldExitOnCheckResultFail
|
|
{
|
|
__shouldExitOnCheckResultFail = shouldExitOnCheckResultFail;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
+ (BOOL)shouldExitOnCheckResultFail
|
|
{
|
|
return __shouldExitOnCheckResultFail;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
#pragma mark - AudioBufferList Utility
|
|
//------------------------------------------------------------------------------
|
|
|
|
+ (AudioBufferList *)audioBufferListWithNumberOfFrames:(UInt32)frames
|
|
numberOfChannels:(UInt32)channels
|
|
interleaved:(BOOL)interleaved
|
|
{
|
|
AudioBufferList *audioBufferList = (AudioBufferList*)malloc(sizeof(AudioBufferList) + sizeof(AudioBuffer) * (channels-1));
|
|
UInt32 outputBufferSize = 32 * frames; // 32 KB
|
|
audioBufferList->mNumberBuffers = interleaved ? 1 : channels;
|
|
for(int i = 0; i < audioBufferList->mNumberBuffers; i++)
|
|
{
|
|
audioBufferList->mBuffers[i].mNumberChannels = channels;
|
|
audioBufferList->mBuffers[i].mDataByteSize = channels * outputBufferSize;
|
|
audioBufferList->mBuffers[i].mData = (float *)malloc(channels * sizeof(float) *outputBufferSize);
|
|
memset(audioBufferList->mBuffers[i].mData, 0, frames);
|
|
}
|
|
return audioBufferList;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
+ (float **)floatBuffersWithNumberOfFrames:(UInt32)frames
|
|
numberOfChannels:(UInt32)channels
|
|
{
|
|
size_t size = sizeof(float *) * channels;
|
|
float **buffers = (float **)malloc(size);
|
|
for (int i = 0; i < channels; i++)
|
|
{
|
|
size = sizeof(float) * frames;
|
|
buffers[i] = (float *)malloc(size);
|
|
}
|
|
return buffers;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
+ (void)freeBufferList:(AudioBufferList *)bufferList
|
|
{
|
|
if (bufferList)
|
|
{
|
|
if (bufferList->mNumberBuffers)
|
|
{
|
|
for( int i = 0; i < bufferList->mNumberBuffers; i++)
|
|
{
|
|
if (bufferList->mBuffers[i].mData)
|
|
{
|
|
free(bufferList->mBuffers[i].mData);
|
|
}
|
|
}
|
|
}
|
|
free(bufferList);
|
|
}
|
|
bufferList = NULL;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
+ (void)freeFloatBuffers:(float **)buffers numberOfChannels:(UInt32)channels
|
|
{
|
|
for (int i = 0; i < channels; i++)
|
|
{
|
|
free(buffers[i]);
|
|
}
|
|
free(buffers);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
#pragma mark - AudioStreamBasicDescription Utility
|
|
//------------------------------------------------------------------------------
|
|
|
|
+ (AudioStreamBasicDescription)AIFFFormatWithNumberOfChannels:(UInt32)channels
|
|
sampleRate:(float)sampleRate
|
|
{
|
|
AudioStreamBasicDescription asbd;
|
|
memset(&asbd, 0, sizeof(asbd));
|
|
asbd.mFormatID = kAudioFormatLinearPCM;
|
|
asbd.mFormatFlags = kAudioFormatFlagIsBigEndian|kAudioFormatFlagIsPacked|kAudioFormatFlagIsSignedInteger;
|
|
asbd.mSampleRate = sampleRate;
|
|
asbd.mChannelsPerFrame = channels;
|
|
asbd.mBitsPerChannel = 32;
|
|
asbd.mBytesPerPacket = (asbd.mBitsPerChannel / 8) * asbd.mChannelsPerFrame;
|
|
asbd.mFramesPerPacket = 1;
|
|
asbd.mBytesPerFrame = (asbd.mBitsPerChannel / 8) * asbd.mChannelsPerFrame;
|
|
return asbd;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
+ (AudioStreamBasicDescription)iLBCFormatWithSampleRate:(float)sampleRate
|
|
{
|
|
AudioStreamBasicDescription asbd;
|
|
memset(&asbd, 0, sizeof(asbd));
|
|
asbd.mFormatID = kAudioFormatiLBC;
|
|
asbd.mChannelsPerFrame = 1;
|
|
asbd.mSampleRate = sampleRate;
|
|
|
|
// Fill in the rest of the descriptions using the Audio Format API
|
|
UInt32 propSize = sizeof(asbd);
|
|
[EZAudioUtilities checkResult:AudioFormatGetProperty(kAudioFormatProperty_FormatInfo,
|
|
0,
|
|
NULL,
|
|
&propSize,
|
|
&asbd)
|
|
operation:"Failed to fill out the rest of the m4a AudioStreamBasicDescription"];
|
|
|
|
return asbd;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
+ (AudioStreamBasicDescription)floatFormatWithNumberOfChannels:(UInt32)channels
|
|
sampleRate:(float)sampleRate
|
|
{
|
|
AudioStreamBasicDescription asbd;
|
|
UInt32 floatByteSize = sizeof(float);
|
|
asbd.mBitsPerChannel = 8 * floatByteSize;
|
|
asbd.mBytesPerFrame = floatByteSize;
|
|
asbd.mBytesPerPacket = floatByteSize;
|
|
asbd.mChannelsPerFrame = channels;
|
|
asbd.mFormatFlags = kAudioFormatFlagIsFloat|kAudioFormatFlagIsNonInterleaved;
|
|
asbd.mFormatID = kAudioFormatLinearPCM;
|
|
asbd.mFramesPerPacket = 1;
|
|
asbd.mSampleRate = sampleRate;
|
|
return asbd;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
+ (AudioStreamBasicDescription)M4AFormatWithNumberOfChannels:(UInt32)channels
|
|
sampleRate:(float)sampleRate
|
|
{
|
|
AudioStreamBasicDescription asbd;
|
|
memset(&asbd, 0, sizeof(asbd));
|
|
asbd.mFormatID = kAudioFormatMPEG4AAC;
|
|
asbd.mChannelsPerFrame = channels;
|
|
asbd.mSampleRate = sampleRate;
|
|
|
|
// Fill in the rest of the descriptions using the Audio Format API
|
|
UInt32 propSize = sizeof(asbd);
|
|
[EZAudioUtilities checkResult:AudioFormatGetProperty(kAudioFormatProperty_FormatInfo,
|
|
0,
|
|
NULL,
|
|
&propSize,
|
|
&asbd)
|
|
operation:"Failed to fill out the rest of the m4a AudioStreamBasicDescription"];
|
|
|
|
return asbd;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
+ (AudioStreamBasicDescription)monoFloatFormatWithSampleRate:(float)sampleRate
|
|
{
|
|
AudioStreamBasicDescription asbd;
|
|
UInt32 byteSize = sizeof(float);
|
|
asbd.mBitsPerChannel = 8 * byteSize;
|
|
asbd.mBytesPerFrame = byteSize;
|
|
asbd.mBytesPerPacket = byteSize;
|
|
asbd.mChannelsPerFrame = 1;
|
|
asbd.mFormatFlags = kAudioFormatFlagIsPacked|kAudioFormatFlagIsFloat;
|
|
asbd.mFormatID = kAudioFormatLinearPCM;
|
|
asbd.mFramesPerPacket = 1;
|
|
asbd.mSampleRate = sampleRate;
|
|
return asbd;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
+ (AudioStreamBasicDescription)monoCanonicalFormatWithSampleRate:(float)sampleRate
|
|
{
|
|
AudioStreamBasicDescription asbd;
|
|
UInt32 byteSize = sizeof(float);
|
|
asbd.mBitsPerChannel = 8 * byteSize;
|
|
asbd.mBytesPerFrame = byteSize;
|
|
asbd.mBytesPerPacket = byteSize;
|
|
asbd.mChannelsPerFrame = 1;
|
|
asbd.mFormatFlags = kAudioFormatFlagsNativeFloatPacked|kAudioFormatFlagIsNonInterleaved;
|
|
asbd.mFormatID = kAudioFormatLinearPCM;
|
|
asbd.mFramesPerPacket = 1;
|
|
asbd.mSampleRate = sampleRate;
|
|
return asbd;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
+ (AudioStreamBasicDescription)stereoCanonicalNonInterleavedFormatWithSampleRate:(float)sampleRate
|
|
{
|
|
AudioStreamBasicDescription asbd;
|
|
UInt32 byteSize = sizeof(float);
|
|
asbd.mBitsPerChannel = 8 * byteSize;
|
|
asbd.mBytesPerFrame = byteSize;
|
|
asbd.mBytesPerPacket = byteSize;
|
|
asbd.mChannelsPerFrame = 2;
|
|
asbd.mFormatFlags = kAudioFormatFlagsNativeFloatPacked|kAudioFormatFlagIsNonInterleaved;
|
|
asbd.mFormatID = kAudioFormatLinearPCM;
|
|
asbd.mFramesPerPacket = 1;
|
|
asbd.mSampleRate = sampleRate;
|
|
return asbd;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
+ (AudioStreamBasicDescription)stereoFloatInterleavedFormatWithSampleRate:(float)sampleRate
|
|
{
|
|
AudioStreamBasicDescription asbd;
|
|
UInt32 floatByteSize = sizeof(float);
|
|
asbd.mChannelsPerFrame = 2;
|
|
asbd.mBitsPerChannel = 8 * floatByteSize;
|
|
asbd.mBytesPerFrame = asbd.mChannelsPerFrame * floatByteSize;
|
|
asbd.mFramesPerPacket = 1;
|
|
asbd.mBytesPerPacket = asbd.mFramesPerPacket * asbd.mBytesPerFrame;
|
|
asbd.mFormatFlags = kAudioFormatFlagIsFloat;
|
|
asbd.mFormatID = kAudioFormatLinearPCM;
|
|
asbd.mSampleRate = sampleRate;
|
|
asbd.mReserved = 0;
|
|
return asbd;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
+ (AudioStreamBasicDescription)stereoFloatNonInterleavedFormatWithSampleRate:(float)sampleRate
|
|
{
|
|
AudioStreamBasicDescription asbd;
|
|
UInt32 floatByteSize = sizeof(float);
|
|
asbd.mBitsPerChannel = 8 * floatByteSize;
|
|
asbd.mBytesPerFrame = floatByteSize;
|
|
asbd.mChannelsPerFrame = 2;
|
|
asbd.mFormatFlags = kAudioFormatFlagIsFloat|kAudioFormatFlagIsNonInterleaved;
|
|
asbd.mFormatID = kAudioFormatLinearPCM;
|
|
asbd.mFramesPerPacket = 1;
|
|
asbd.mBytesPerPacket = asbd.mFramesPerPacket * asbd.mBytesPerFrame;
|
|
asbd.mSampleRate = sampleRate;
|
|
return asbd;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
+ (BOOL)isFloatFormat:(AudioStreamBasicDescription)asbd
|
|
{
|
|
return asbd.mFormatFlags & kAudioFormatFlagIsFloat;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
+ (BOOL)isInterleaved:(AudioStreamBasicDescription)asbd
|
|
{
|
|
return !(asbd.mFormatFlags & kAudioFormatFlagIsNonInterleaved);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
+ (BOOL)isLinearPCM:(AudioStreamBasicDescription)asbd
|
|
{
|
|
return asbd.mFormatID == kAudioFormatLinearPCM;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
+ (void)printASBD:(AudioStreamBasicDescription)asbd
|
|
{
|
|
char formatIDString[5];
|
|
UInt32 formatID = CFSwapInt32HostToBig(asbd.mFormatID);
|
|
bcopy (&formatID, formatIDString, 4);
|
|
formatIDString[4] = '\0';
|
|
NSLog (@" Sample Rate: %10.0f", asbd.mSampleRate);
|
|
NSLog (@" Format ID: %10s", formatIDString);
|
|
NSLog (@" Format Flags: %10X", (unsigned int)asbd.mFormatFlags);
|
|
NSLog (@" Bytes per Packet: %10d", (unsigned int)asbd.mBytesPerPacket);
|
|
NSLog (@" Frames per Packet: %10d", (unsigned int)asbd.mFramesPerPacket);
|
|
NSLog (@" Bytes per Frame: %10d", (unsigned int)asbd.mBytesPerFrame);
|
|
NSLog (@" Channels per Frame: %10d", (unsigned int)asbd.mChannelsPerFrame);
|
|
NSLog (@" Bits per Channel: %10d", (unsigned int)asbd.mBitsPerChannel);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
+ (NSString *)displayTimeStringFromSeconds:(NSTimeInterval)seconds
|
|
{
|
|
int totalSeconds = (int)ceil(seconds);
|
|
int secondsComponent = totalSeconds % 60;
|
|
int minutesComponent = (totalSeconds / 60) % 60;
|
|
return [NSString stringWithFormat:@"%02d:%02d", minutesComponent, secondsComponent];
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
+ (NSString *)stringForAudioStreamBasicDescription:(AudioStreamBasicDescription)asbd
|
|
{
|
|
char formatIDString[5];
|
|
UInt32 formatID = CFSwapInt32HostToBig(asbd.mFormatID);
|
|
bcopy (&formatID, formatIDString, 4);
|
|
formatIDString[4] = '\0';
|
|
return [NSString stringWithFormat:
|
|
@"\nSample Rate: %10.0f,\n"
|
|
@"Format ID: %10s,\n"
|
|
@"Format Flags: %10X,\n"
|
|
@"Bytes per Packet: %10d,\n"
|
|
@"Frames per Packet: %10d,\n"
|
|
@"Bytes per Frame: %10d,\n"
|
|
@"Channels per Frame: %10d,\n"
|
|
@"Bits per Channel: %10d,\n"
|
|
@"IsInterleaved: %i,\n"
|
|
@"IsFloat: %i,",
|
|
asbd.mSampleRate,
|
|
formatIDString,
|
|
(unsigned int)asbd.mFormatFlags,
|
|
(unsigned int)asbd.mBytesPerPacket,
|
|
(unsigned int)asbd.mFramesPerPacket,
|
|
(unsigned int)asbd.mBytesPerFrame,
|
|
(unsigned int)asbd.mChannelsPerFrame,
|
|
(unsigned int)asbd.mBitsPerChannel,
|
|
[self isInterleaved:asbd],
|
|
[self isFloatFormat:asbd]];
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
+ (void)setCanonicalAudioStreamBasicDescription:(AudioStreamBasicDescription*)asbd
|
|
numberOfChannels:(UInt32)nChannels
|
|
interleaved:(BOOL)interleaved
|
|
{
|
|
|
|
asbd->mFormatID = kAudioFormatLinearPCM;
|
|
#if TARGET_OS_IPHONE
|
|
int sampleSize = sizeof(float);
|
|
asbd->mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
|
|
#elif TARGET_OS_MAC
|
|
int sampleSize = sizeof(Float32);
|
|
asbd->mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
|
|
#endif
|
|
asbd->mBitsPerChannel = 8 * sampleSize;
|
|
asbd->mChannelsPerFrame = nChannels;
|
|
asbd->mFramesPerPacket = 1;
|
|
if (interleaved)
|
|
asbd->mBytesPerPacket = asbd->mBytesPerFrame = nChannels * sampleSize;
|
|
else {
|
|
asbd->mBytesPerPacket = asbd->mBytesPerFrame = sampleSize;
|
|
asbd->mFormatFlags |= kAudioFormatFlagIsNonInterleaved;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
#pragma mark - Math Utilities
|
|
//------------------------------------------------------------------------------
|
|
|
|
+ (void)appendBufferAndShift:(float*)buffer
|
|
withBufferSize:(int)bufferLength
|
|
toScrollHistory:(float*)scrollHistory
|
|
withScrollHistorySize:(int)scrollHistoryLength
|
|
{
|
|
int shiftLength = scrollHistoryLength - bufferLength;
|
|
size_t floatByteSize = sizeof(float);
|
|
size_t shiftByteSize = shiftLength * floatByteSize;
|
|
size_t bufferByteSize = bufferLength * floatByteSize;
|
|
memmove(&scrollHistory[0],
|
|
&scrollHistory[bufferLength],
|
|
shiftByteSize);
|
|
memmove(&scrollHistory[shiftLength],
|
|
&buffer[0],
|
|
bufferByteSize);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
+ (void) appendValue:(float)value
|
|
toScrollHistory:(float*)scrollHistory
|
|
withScrollHistorySize:(int)scrollHistoryLength
|
|
{
|
|
float val[1]; val[0] = value;
|
|
[self appendBufferAndShift:val
|
|
withBufferSize:1
|
|
toScrollHistory:scrollHistory
|
|
withScrollHistorySize:scrollHistoryLength];
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
+(float)MAP:(float)value
|
|
leftMin:(float)leftMin
|
|
leftMax:(float)leftMax
|
|
rightMin:(float)rightMin
|
|
rightMax:(float)rightMax
|
|
{
|
|
float leftSpan = leftMax - leftMin;
|
|
float rightSpan = rightMax - rightMin;
|
|
float valueScaled = ( value - leftMin) / leftSpan;
|
|
return rightMin + (valueScaled * rightSpan);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
+(float)RMS:(float *)buffer
|
|
length:(int)bufferSize
|
|
{
|
|
float sum = 0.0;
|
|
for(int i = 0; i < bufferSize; i++)
|
|
sum += buffer[i] * buffer[i];
|
|
return sqrtf( sum / bufferSize);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
+(float)SGN:(float)value
|
|
{
|
|
return value < 0 ? -1.0f : ( value > 0 ? 1.0f : 0.0f);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
#pragma mark - OSStatus Utility
|
|
//------------------------------------------------------------------------------
|
|
|
|
+ (void)checkResult:(OSStatus)result operation:(const char *)operation
|
|
{
|
|
if (result == noErr) return;
|
|
char errorString[20];
|
|
// see if it appears to be a 4-char-code
|
|
*(UInt32 *)(errorString + 1) = CFSwapInt32HostToBig(result);
|
|
if (isprint(errorString[1]) && isprint(errorString[2]) && isprint(errorString[3]) && isprint(errorString[4]))
|
|
{
|
|
errorString[0] = errorString[5] = '\'';
|
|
errorString[6] = '\0';
|
|
} else
|
|
// no, format it as an integer
|
|
sprintf(errorString, "%d", (int)result);
|
|
fprintf(stderr, "Error: %s (%s)\n", operation, errorString);
|
|
if (__shouldExitOnCheckResultFail)
|
|
{
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
+ (NSString *)stringFromUInt32Code:(UInt32)code
|
|
{
|
|
char errorString[20];
|
|
// see if it appears to be a 4-char-code
|
|
*(UInt32 *)(errorString + 1) = CFSwapInt32HostToBig(code);
|
|
if (isprint(errorString[1]) &&
|
|
isprint(errorString[2]) &&
|
|
isprint(errorString[3]) &&
|
|
isprint(errorString[4]))
|
|
{
|
|
errorString[0] = errorString[5] = '\'';
|
|
errorString[6] = '\0';
|
|
}
|
|
return [NSString stringWithUTF8String:errorString];
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
#pragma mark - Plot Utility
|
|
//------------------------------------------------------------------------------
|
|
|
|
+ (void)updateScrollHistory:(float **)scrollHistory
|
|
withLength:(int)scrollHistoryLength
|
|
atIndex:(int *)index
|
|
withBuffer:(float *)buffer
|
|
withBufferSize:(int)bufferSize
|
|
isResolutionChanging:(BOOL *)isChanging
|
|
{
|
|
//
|
|
size_t floatByteSize = sizeof(float);
|
|
if(*scrollHistory == NULL)
|
|
{
|
|
// Create the history buffer
|
|
*scrollHistory = (float *)calloc(8192, floatByteSize);
|
|
}
|
|
|
|
//
|
|
if(!*isChanging)
|
|
{
|
|
float rms = [EZAudioUtilities RMS:buffer length:bufferSize];
|
|
if(*index < scrollHistoryLength)
|
|
{
|
|
float *hist = *scrollHistory;
|
|
hist[*index] = rms;
|
|
(*index)++;
|
|
}
|
|
else
|
|
{
|
|
[EZAudioUtilities appendValue:rms
|
|
toScrollHistory:*scrollHistory
|
|
withScrollHistorySize:scrollHistoryLength];
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
#pragma mark - Color Utility
|
|
//------------------------------------------------------------------------------
|
|
|
|
///-----------------------------------------------------------
|
|
/// @name Color Utility
|
|
///-----------------------------------------------------------
|
|
|
|
+ (void)getColorComponentsFromCGColor:(CGColorRef)color
|
|
red:(CGFloat *)red
|
|
green:(CGFloat *)green
|
|
blue:(CGFloat *)blue
|
|
alpha:(CGFloat *)alpha
|
|
{
|
|
size_t componentCount = CGColorGetNumberOfComponents(color);
|
|
if (componentCount == 4)
|
|
{
|
|
const CGFloat *components = CGColorGetComponents(color);
|
|
*red = components[0];
|
|
*green = components[1];
|
|
*blue = components[2];
|
|
*alpha = components[3];
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
#pragma mark - TPCircularBuffer Utility
|
|
//------------------------------------------------------------------------------
|
|
|
|
+ (void)appendDataToCircularBuffer:(TPCircularBuffer *)circularBuffer
|
|
fromAudioBufferList:(AudioBufferList *)audioBufferList
|
|
{
|
|
TPCircularBufferProduceBytes(circularBuffer,
|
|
audioBufferList->mBuffers[0].mData,
|
|
audioBufferList->mBuffers[0].mDataByteSize);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
+ (void)circularBuffer:(TPCircularBuffer *)circularBuffer withSize:(int)size
|
|
{
|
|
TPCircularBufferInit(circularBuffer, size);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
+ (void)freeCircularBuffer:(TPCircularBuffer *)circularBuffer
|
|
{
|
|
TPCircularBufferClear(circularBuffer);
|
|
TPCircularBufferCleanup(circularBuffer);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
#pragma mark - EZPlotHistoryInfo Utility
|
|
//------------------------------------------------------------------------------
|
|
|
|
+ (void)appendBuffer:(float *)buffer
|
|
withBufferSize:(UInt32)bufferSize
|
|
toHistoryInfo:(EZPlotHistoryInfo *)historyInfo
|
|
{
|
|
//
|
|
// Do nothing if there is no buffer
|
|
//
|
|
if (bufferSize == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Update the scroll history datasource
|
|
//
|
|
float rms = [EZAudioUtilities RMS:buffer length:bufferSize];
|
|
float src[1];
|
|
src[0] = isnan(rms) ? 0.0 : rms;
|
|
TPCircularBufferProduceBytes(&historyInfo->circularBuffer, src, sizeof(src));
|
|
int32_t targetBytes = historyInfo->bufferSize * sizeof(float);
|
|
int32_t availableBytes = 0;
|
|
float *historyBuffer = TPCircularBufferTail(&historyInfo->circularBuffer, &availableBytes);
|
|
int32_t bytes = MIN(targetBytes, availableBytes);
|
|
memmove(historyInfo->buffer, historyBuffer, bytes);
|
|
if (targetBytes <= availableBytes)
|
|
{
|
|
TPCircularBufferConsume(&historyInfo->circularBuffer, availableBytes - targetBytes);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
+ (void)clearHistoryInfo:(EZPlotHistoryInfo *)historyInfo
|
|
{
|
|
memset(historyInfo->buffer, 0, historyInfo->bufferSize * sizeof(float));
|
|
TPCircularBufferClear(&historyInfo->circularBuffer);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
+ (void)freeHistoryInfo:(EZPlotHistoryInfo *)historyInfo
|
|
{
|
|
free(historyInfo->buffer);
|
|
free(historyInfo);
|
|
TPCircularBufferCleanup(&historyInfo->circularBuffer);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
+ (EZPlotHistoryInfo *)historyInfoWithDefaultLength:(int)defaultLength
|
|
maximumLength:(int)maximumLength
|
|
{
|
|
//
|
|
// Setup buffers
|
|
//
|
|
EZPlotHistoryInfo *historyInfo = (EZPlotHistoryInfo *)malloc(sizeof(EZPlotHistoryInfo));
|
|
historyInfo->bufferSize = defaultLength;
|
|
historyInfo->buffer = calloc(maximumLength, sizeof(float));
|
|
TPCircularBufferInit(&historyInfo->circularBuffer, maximumLength);
|
|
|
|
//
|
|
// Zero out circular buffer
|
|
//
|
|
float emptyBuffer[maximumLength];
|
|
memset(emptyBuffer, 0, sizeof(emptyBuffer));
|
|
TPCircularBufferProduceBytes(&historyInfo->circularBuffer,
|
|
emptyBuffer,
|
|
(int32_t)sizeof(emptyBuffer));
|
|
|
|
return historyInfo;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
@end |