Compare commits
88 Commits
0.0.3
...
new_examples
| Author | SHA1 | Date | |
|---|---|---|---|
| c4fb3e27e7 | |||
| a6252b02dd | |||
| 7f0bf3bb6c | |||
| 62b79ce6c2 | |||
| 1415b5591e | |||
| ab892b9f74 | |||
| fde27c2db7 | |||
| 1055e3b1dc | |||
| ceba0dd415 | |||
| 6d6ae3e761 | |||
| 5261cb2961 | |||
| 65491d453d | |||
| 4389a48875 | |||
| 3b4382986f | |||
| b327e5b140 | |||
| 73adf239ec | |||
| c2c05b683b | |||
| 0bdade977d | |||
| 3b0f32abaa | |||
| 804cb1c3ef | |||
| faf64915d9 | |||
| b8b7a27261 | |||
| 7a751bcf53 | |||
| 1588ca62f6 | |||
| e21d17da26 | |||
| 6f225bfcb5 | |||
| d0181ce2eb | |||
| f274e6a330 | |||
| ea04f3a508 | |||
| 5487488318 | |||
| 55cdbfeaec | |||
| 5215465565 | |||
| 934e55a104 | |||
| 5e3a299a8e | |||
| c2ae03aaae | |||
| b443b8d385 | |||
| fb777f81ed | |||
| 78dc908a6d | |||
| b5880bce5a | |||
| 9277585014 | |||
| 346d0f93fc | |||
| 77af3da1a3 | |||
| 1705daf0d8 | |||
| ddadfc512c | |||
| afcfa793ca | |||
| 895606b46e | |||
| 873ce017ea | |||
| 584256f20f | |||
| a957fed152 | |||
| 7eac04b186 | |||
| 3030569caa | |||
| 7a5971208c | |||
| 86601e7079 | |||
| e0161288b4 | |||
| 1b8fa4f1eb | |||
| ab20d29a71 | |||
| 3da3f8ad3d | |||
| 4cdccac2e0 | |||
| 54f82b9657 | |||
| 25fef28b40 | |||
| ec72b33b2f | |||
| 993883488b | |||
| 56b17904e3 | |||
| 9f2b6b9c3e | |||
| aa23d9e375 | |||
| b91675349c | |||
| 29ce0141ce | |||
| e90a48a367 | |||
| f482234fae | |||
| 01352aed7d | |||
| b992ce964a | |||
| 76f1e35774 | |||
| ead5c84ee3 | |||
| 5c21e8d097 | |||
| 10208fdc27 | |||
| da8f9dfcbe | |||
| 4a8fd7bee5 | |||
| 4b6d2327d4 | |||
| ede1fdc404 | |||
| 0363d977f0 | |||
| c5e8dbc368 | |||
| 21205284f5 | |||
| cd5ca676eb | |||
| cf0e36e858 | |||
| 684056541f | |||
| 5b9f5ec8ef | |||
| b4d607e9d5 | |||
| 47531a99a0 |
+3
-3
@@ -1,6 +1,6 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "EZAudio"
|
||||
s.version = "0.0.2"
|
||||
s.version = "0.0.6"
|
||||
s.summary = "A simple, intuitive audio framework for iOS and OSX useful for anyone doing audio processing and/or audio-based visualizations."
|
||||
s.homepage = "http://syedharisali.com/projects/EZAudio/getting-started"
|
||||
s.screenshots = "https://s3-us-west-1.amazonaws.com/ezaudio-media/EZAudioSummary.png"
|
||||
@@ -8,10 +8,10 @@ Pod::Spec.new do |s|
|
||||
s.author = { "Syed Haris Ali" => "syedhali07@gmail.com" }
|
||||
s.ios.deployment_target = '6.0'
|
||||
s.osx.deployment_target = '10.8'
|
||||
s.source = { :git => "https://github.com/syedhali/EZAudio.git", :tag => "0.0.2" }
|
||||
s.source = { :git => "https://github.com/syedhali/EZAudio.git", :tag => "0.0.6" }
|
||||
s.source_files = 'EZAudio/*.{h,m,c}'
|
||||
s.exclude_files = 'EZAudio/VERSION'
|
||||
s.ios.frameworks = 'AudioToolbox','GLKit'
|
||||
s.ios.frameworks = 'AudioToolbox','AVFoundation','GLKit'
|
||||
s.osx.frameworks = 'AudioToolbox','AudioUnit','CoreAudio','QuartzCore','OpenGL','GLKit'
|
||||
s.requires_arc = true;
|
||||
end
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
//
|
||||
// AEFloatConverter.h
|
||||
// The Amazing Audio Engine
|
||||
//
|
||||
// Created by Michael Tyson on 25/10/2012.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <AudioToolbox/AudioToolbox.h>
|
||||
|
||||
/*!
|
||||
* Universal converter to float format
|
||||
*
|
||||
* Use this class to easily convert arbitrary audio formats to floating point
|
||||
* for use with utilities like the Accelerate framework.
|
||||
*/
|
||||
@interface AEFloatConverter : NSObject
|
||||
|
||||
/*!
|
||||
* Initialize
|
||||
*
|
||||
* @param sourceFormat The audio format to use
|
||||
*/
|
||||
- (id)initWithSourceFormat:(AudioStreamBasicDescription)sourceFormat;
|
||||
|
||||
/*!
|
||||
* Convert audio to floating-point
|
||||
*
|
||||
* This C function, safe to use in a Core Audio realtime thread context, will take
|
||||
* an audio buffer list of audio in the format you provided at initialisation, and
|
||||
* convert it into a noninterleaved float array.
|
||||
*
|
||||
* @param converter Pointer to the converter object.
|
||||
* @param sourceBuffer An audio buffer list containing the source audio.
|
||||
* @param targetBuffers An array of floating-point arrays to store the converted float audio into.
|
||||
* Note that you must provide the correct number of arrays, to match the number of channels.
|
||||
* @param frames The number of frames to convert.
|
||||
* @return YES on success; NO on failure
|
||||
*/
|
||||
BOOL AEFloatConverterToFloat(AEFloatConverter* converter, AudioBufferList *sourceBuffer, float * const * targetBuffers, UInt32 frames);
|
||||
|
||||
/*!
|
||||
* Convert audio to floating-point, in a buffer list
|
||||
*
|
||||
* This C function, safe to use in a Core Audio realtime thread context, will take
|
||||
* an audio buffer list of audio in the format you provided at initialisation, and
|
||||
* convert it into a noninterleaved float format.
|
||||
*
|
||||
* @param converter Pointer to the converter object.
|
||||
* @param sourceBuffer An audio buffer list containing the source audio.
|
||||
* @param targetBuffer An audio buffer list to store the converted floating-point audio.
|
||||
* @param frames The number of frames to convert.
|
||||
* @return YES on success; NO on failure
|
||||
*/
|
||||
BOOL AEFloatConverterToFloatBufferList(AEFloatConverter* converter, AudioBufferList *sourceBuffer, AudioBufferList *targetBuffer, UInt32 frames);
|
||||
|
||||
/*!
|
||||
* Convert audio from floating-point
|
||||
*
|
||||
* This C function, safe to use in a Core Audio realtime thread context, will take
|
||||
* an audio buffer list of audio in the format you provided at initialisation, and
|
||||
* convert it into a float array.
|
||||
*
|
||||
* @param converter Pointer to the converter object.
|
||||
* @param sourceBuffers An array of floating-point arrays containing the floating-point audio to convert.
|
||||
* Note that you must provide the correct number of arrays, to match the number of channels.
|
||||
* @param targetBuffer An audio buffer list to store the converted audio into.
|
||||
* @param frames The number of frames to convert.
|
||||
* @return YES on success; NO on failure
|
||||
*/
|
||||
BOOL AEFloatConverterFromFloat(AEFloatConverter* converter, float * const * sourceBuffers, AudioBufferList *targetBuffer, UInt32 frames);
|
||||
|
||||
/*!
|
||||
* Convert audio from floating-point, in a buffer list
|
||||
*
|
||||
* This C function, safe to use in a Core Audio realtime thread context, will take
|
||||
* an audio buffer list of audio in the format you provided at initialisation, and
|
||||
* convert it into a float array.
|
||||
*
|
||||
* @param converter Pointer to the converter object.
|
||||
* @param sourceBuffer An audio buffer list containing the source audio.
|
||||
* @param targetBuffer An audio buffer list to store the converted audio into.
|
||||
* @param frames The number of frames to convert.
|
||||
* @return YES on success; NO on failure
|
||||
*/
|
||||
BOOL AEFloatConverterFromFloatBufferList(AEFloatConverter* converter, AudioBufferList *sourceBuffer, AudioBufferList *targetBuffer, UInt32 frames);
|
||||
|
||||
/*!
|
||||
* The AudioStreamBasicDescription representing the converted floating-point format
|
||||
*/
|
||||
@property (nonatomic, readonly) AudioStreamBasicDescription floatingPointAudioDescription;
|
||||
|
||||
/*!
|
||||
* The source audio format set at initialization
|
||||
*/
|
||||
@property (nonatomic, readonly) AudioStreamBasicDescription sourceFormat;
|
||||
|
||||
@end
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,211 +0,0 @@
|
||||
//
|
||||
// AEFloatConverter.m
|
||||
// The Amazing Audio Engine
|
||||
//
|
||||
// Created by Michael Tyson on 25/10/2012.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
#import "AEFloatConverter.h"
|
||||
|
||||
#define checkResult(result,operation) (_checkResult((result),(operation),strrchr(__FILE__, '/')+1,__LINE__))
|
||||
static inline BOOL _checkResult(OSStatus result, const char *operation, const char* file, int line) {
|
||||
if ( result != noErr ) {
|
||||
NSLog(@"%s:%d: %s result %d %08X %4.4s", file, line, operation, (int)result, (int)result, (char*)&result);
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
#define kNoMoreDataErr -2222
|
||||
|
||||
struct complexInputDataProc_t {
|
||||
AudioBufferList *sourceBuffer;
|
||||
};
|
||||
|
||||
@interface AEFloatConverter () {
|
||||
AudioStreamBasicDescription _sourceAudioDescription;
|
||||
AudioStreamBasicDescription _floatAudioDescription;
|
||||
AudioConverterRef _toFloatConverter;
|
||||
AudioConverterRef _fromFloatConverter;
|
||||
AudioBufferList *_scratchFloatBufferList;
|
||||
}
|
||||
|
||||
static OSStatus complexInputDataProc(AudioConverterRef inAudioConverter,
|
||||
UInt32 *ioNumberDataPackets,
|
||||
AudioBufferList *ioData,
|
||||
AudioStreamPacketDescription **outDataPacketDescription,
|
||||
void *inUserData);
|
||||
@end
|
||||
|
||||
@implementation AEFloatConverter
|
||||
@synthesize sourceFormat = _sourceAudioDescription;
|
||||
|
||||
-(id)initWithSourceFormat:(AudioStreamBasicDescription)sourceFormat {
|
||||
if ( !(self = [super init]) ) return nil;
|
||||
|
||||
_floatAudioDescription.mFormatID = kAudioFormatLinearPCM;
|
||||
_floatAudioDescription.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked | kAudioFormatFlagIsNonInterleaved;
|
||||
_floatAudioDescription.mChannelsPerFrame = sourceFormat.mChannelsPerFrame;
|
||||
_floatAudioDescription.mBytesPerPacket = sizeof(float);
|
||||
_floatAudioDescription.mFramesPerPacket = 1;
|
||||
_floatAudioDescription.mBytesPerFrame = sizeof(float);
|
||||
_floatAudioDescription.mBitsPerChannel = 8 * sizeof(float);
|
||||
_floatAudioDescription.mSampleRate = sourceFormat.mSampleRate;
|
||||
|
||||
_sourceAudioDescription = sourceFormat;
|
||||
|
||||
if ( memcmp(&sourceFormat, &_floatAudioDescription, sizeof(AudioStreamBasicDescription)) != 0 ) {
|
||||
checkResult(AudioConverterNew(&sourceFormat, &_floatAudioDescription, &_toFloatConverter), "AudioConverterNew");
|
||||
checkResult(AudioConverterNew(&_floatAudioDescription, &sourceFormat, &_fromFloatConverter), "AudioConverterNew");
|
||||
_scratchFloatBufferList = (AudioBufferList*)malloc(sizeof(AudioBufferList) + (_floatAudioDescription.mChannelsPerFrame-1)*sizeof(AudioBuffer));
|
||||
_scratchFloatBufferList->mNumberBuffers = _floatAudioDescription.mChannelsPerFrame;
|
||||
for ( int i=0; i<_scratchFloatBufferList->mNumberBuffers; i++ ) {
|
||||
_scratchFloatBufferList->mBuffers[i].mNumberChannels = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void)dealloc {
|
||||
if ( _toFloatConverter ) AudioConverterDispose(_toFloatConverter);
|
||||
if ( _fromFloatConverter ) AudioConverterDispose(_fromFloatConverter);
|
||||
if ( _scratchFloatBufferList ) free(_scratchFloatBufferList);
|
||||
// [super dealloc];
|
||||
}
|
||||
|
||||
|
||||
BOOL AEFloatConverterToFloat(AEFloatConverter* THIS, AudioBufferList *sourceBuffer, float * const * targetBuffers, UInt32 frames) {
|
||||
if ( frames == 0 ) return YES;
|
||||
|
||||
if ( THIS->_toFloatConverter ) {
|
||||
UInt32 priorDataByteSize = sourceBuffer->mBuffers[0].mDataByteSize;
|
||||
for ( int i=0; i<sourceBuffer->mNumberBuffers; i++ ) {
|
||||
sourceBuffer->mBuffers[i].mDataByteSize = frames * THIS->_sourceAudioDescription.mBytesPerFrame;
|
||||
}
|
||||
|
||||
for ( int i=0; i<THIS->_scratchFloatBufferList->mNumberBuffers; i++ ) {
|
||||
THIS->_scratchFloatBufferList->mBuffers[i].mData = targetBuffers[i];
|
||||
THIS->_scratchFloatBufferList->mBuffers[i].mDataByteSize = frames * sizeof(float);
|
||||
}
|
||||
|
||||
OSStatus result = AudioConverterFillComplexBuffer(THIS->_toFloatConverter,
|
||||
complexInputDataProc,
|
||||
&(struct complexInputDataProc_t) { .sourceBuffer = sourceBuffer },
|
||||
&frames,
|
||||
THIS->_scratchFloatBufferList,
|
||||
NULL);
|
||||
|
||||
for ( int i=0; i<sourceBuffer->mNumberBuffers; i++ ) {
|
||||
sourceBuffer->mBuffers[i].mDataByteSize = priorDataByteSize;
|
||||
}
|
||||
|
||||
if ( !checkResult(result, "AudioConverterConvertComplexBuffer") ) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
} else {
|
||||
for ( int i=0; i<sourceBuffer->mNumberBuffers; i++ ) {
|
||||
memcpy(targetBuffers[i], sourceBuffer->mBuffers[i].mData, frames * sizeof(float));
|
||||
}
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
BOOL AEFloatConverterToFloatBufferList(AEFloatConverter* converter, AudioBufferList *sourceBuffer, AudioBufferList *targetBuffer, UInt32 frames) {
|
||||
assert(targetBuffer->mNumberBuffers == converter->_floatAudioDescription.mChannelsPerFrame);
|
||||
|
||||
float *targetBuffers[targetBuffer->mNumberBuffers];
|
||||
for ( int i=0; i<targetBuffer->mNumberBuffers; i++ ) {
|
||||
targetBuffers[i] = (float*)targetBuffer->mBuffers[i].mData;
|
||||
}
|
||||
return AEFloatConverterToFloat(converter, sourceBuffer, targetBuffers, frames);
|
||||
}
|
||||
|
||||
BOOL AEFloatConverterFromFloat(AEFloatConverter* THIS, float * const * sourceBuffers, AudioBufferList *targetBuffer, UInt32 frames) {
|
||||
if ( frames == 0 ) return YES;
|
||||
|
||||
if ( THIS->_fromFloatConverter ) {
|
||||
for ( int i=0; i<THIS->_scratchFloatBufferList->mNumberBuffers; i++ ) {
|
||||
THIS->_scratchFloatBufferList->mBuffers[i].mData = sourceBuffers[i];
|
||||
THIS->_scratchFloatBufferList->mBuffers[i].mDataByteSize = frames * sizeof(float);
|
||||
}
|
||||
|
||||
UInt32 priorDataByteSize = targetBuffer->mBuffers[0].mDataByteSize;
|
||||
for ( int i=0; i<targetBuffer->mNumberBuffers; i++ ) {
|
||||
targetBuffer->mBuffers[i].mDataByteSize = frames * THIS->_sourceAudioDescription.mBytesPerFrame;
|
||||
}
|
||||
|
||||
OSStatus result = AudioConverterFillComplexBuffer(THIS->_fromFloatConverter,
|
||||
complexInputDataProc,
|
||||
&(struct complexInputDataProc_t) { .sourceBuffer = THIS->_scratchFloatBufferList },
|
||||
&frames,
|
||||
targetBuffer,
|
||||
NULL);
|
||||
|
||||
for ( int i=0; i<targetBuffer->mNumberBuffers; i++ ) {
|
||||
targetBuffer->mBuffers[i].mDataByteSize = priorDataByteSize;
|
||||
}
|
||||
|
||||
if ( !checkResult(result, "AudioConverterConvertComplexBuffer") ) {
|
||||
return NO;
|
||||
}
|
||||
} else {
|
||||
for ( int i=0; i<targetBuffer->mNumberBuffers; i++ ) {
|
||||
memcpy(targetBuffer->mBuffers[i].mData, sourceBuffers[i], frames * sizeof(float));
|
||||
}
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
BOOL AEFloatConverterFromFloatBufferList(AEFloatConverter* converter, AudioBufferList *sourceBuffer, AudioBufferList *targetBuffer, UInt32 frames) {
|
||||
assert(sourceBuffer->mNumberBuffers == converter->_floatAudioDescription.mChannelsPerFrame);
|
||||
|
||||
float *sourceBuffers[sourceBuffer->mNumberBuffers];
|
||||
for ( int i=0; i<sourceBuffer->mNumberBuffers; i++ ) {
|
||||
sourceBuffers[i] = (float*)sourceBuffer->mBuffers[i].mData;
|
||||
}
|
||||
return AEFloatConverterFromFloat(converter, sourceBuffers, targetBuffer, frames);
|
||||
}
|
||||
|
||||
static OSStatus complexInputDataProc(AudioConverterRef inAudioConverter,
|
||||
UInt32 *ioNumberDataPackets,
|
||||
AudioBufferList *ioData,
|
||||
AudioStreamPacketDescription **outDataPacketDescription,
|
||||
void *inUserData) {
|
||||
struct complexInputDataProc_t *arg = (struct complexInputDataProc_t*)inUserData;
|
||||
if ( !arg->sourceBuffer ) {
|
||||
return kNoMoreDataErr;
|
||||
}
|
||||
|
||||
memcpy(ioData, arg->sourceBuffer, sizeof(AudioBufferList) + (arg->sourceBuffer->mNumberBuffers-1)*sizeof(AudioBuffer));
|
||||
arg->sourceBuffer = NULL;
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
-(AudioStreamBasicDescription)floatingPointAudioDescription {
|
||||
return _floatAudioDescription;
|
||||
}
|
||||
|
||||
@end
|
||||
+86
-19
@@ -25,25 +25,29 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import <AudioToolbox/AudioToolbox.h>
|
||||
|
||||
#pragma mark - 3rd Party Utilties
|
||||
#import "AEFloatConverter.h"
|
||||
#import "TPCircularBuffer.h"
|
||||
|
||||
#pragma mark - Core Components
|
||||
#import "EZAudioFile.h"
|
||||
#import "EZMicrophone.h"
|
||||
#import "EZOutput.h"
|
||||
#import "EZRecorder.h"
|
||||
|
||||
#pragma mark - Interface Components
|
||||
#import "EZPlot.h"
|
||||
#import "EZAudioPlot.h"
|
||||
#import "EZAudioPlotGL.h"
|
||||
#import "EZAudioPlotGLKViewController.h"
|
||||
//#pragma mark - Core Components
|
||||
//#import "EZAudioFile.h"
|
||||
//#import "EZMicrophone.h"
|
||||
//#import "EZOutput.h"
|
||||
//#import "EZRecorder.h"
|
||||
//
|
||||
//#pragma mark - Extended Components
|
||||
//#import "EZAudioPlayer.h"
|
||||
//
|
||||
//#pragma mark - Interface Components
|
||||
//#import "EZPlot.h"
|
||||
//#import "EZAudioPlot.h"
|
||||
//#import "EZAudioPlotGL.h"
|
||||
//#import "EZAudioPlotGLKViewController.h"
|
||||
|
||||
/**
|
||||
EZAudio is a simple, intuitive framework for iOS and OSX. The goal of EZAudio was to provide a modular, cross-platform framework to simplify performing everyday audio operations like getting microphone input, creating audio waveforms, recording/playing audio files, etc. The visualization tools like the EZAudioPlot and EZAudioPlotGL were created to plug right into the framework's various components and provide highly optimized drawing routines that work in harmony with audio callback loops. All components retain the same namespace whether you're on an iOS device or a Mac computer so an EZAudioPlot understands it will subclass an UIView on an iOS device or an NSView on a Mac.
|
||||
|
||||
|
||||
Class methods for EZAudio are provided as utility methods used throughout the other modules within the framework. For instance, these methods help make sense of error codes (checkResult:operation:), map values betwen coordinate systems (MAP:leftMin:leftMax:rightMin:rightMax:), calculate root mean squared values for buffers (RMS:length:), etc.
|
||||
*/
|
||||
@interface EZAudio : NSObject
|
||||
@@ -55,9 +59,14 @@
|
||||
|
||||
/**
|
||||
Allocates an AudioBufferList structure. Make sure to call freeBufferList when done using AudioBufferList or it will leak.
|
||||
@param frames The number of frames that will be stored within each audio buffer
|
||||
@param channels The number of channels (e.g. 2 for stereo, 1 for mono, etc.)
|
||||
@param interleaved Whether the samples will be interleaved (if not it will be assumed to be non-interleaved and each channel will have an AudioBuffer allocated)
|
||||
@return An AudioBufferList struct that has been allocated in memory
|
||||
*/
|
||||
+(AudioBufferList*)audioBufferList;
|
||||
+(AudioBufferList *)audioBufferListWithNumberOfFrames:(UInt32)frames
|
||||
numberOfChannels:(UInt32)channels
|
||||
interleaved:(BOOL)interleaved;
|
||||
|
||||
/**
|
||||
Deallocates an AudioBufferList structure from memory.
|
||||
@@ -71,33 +80,81 @@
|
||||
///-----------------------------------------------------------
|
||||
|
||||
/**
|
||||
|
||||
@param channels The desired number of channels
|
||||
@param sampleRate The desired sample rate
|
||||
@return A new AudioStreamBasicDescription with the specified format.
|
||||
*/
|
||||
+(AudioStreamBasicDescription)AIFFFormatWithNumberOfChannels:(UInt32)channels
|
||||
sampleRate:(float)sampleRate;
|
||||
|
||||
@param sampleRate The desired sample rate for the AudioStreamBasicDescription
|
||||
/**
|
||||
|
||||
@param sampleRate The desired sample rate
|
||||
@return A new AudioStreamBasicDescription with the specified format.
|
||||
*/
|
||||
+(AudioStreamBasicDescription)iLBCFormatWithSampleRate:(float)sampleRate;
|
||||
|
||||
/**
|
||||
Checks an AudioStreamBasicDescription for an interleaved flag, meaning samples are
|
||||
stored in one buffer one after another instead of two (or n channels) parallel buffers
|
||||
@param asbd A valid AudioStreamBasicDescription
|
||||
@return A BOOL indicating whether or not the AudioStreamBasicDescription is interleaved
|
||||
*/
|
||||
+ (BOOL) isInterleaved:(AudioStreamBasicDescription)asbd;
|
||||
|
||||
/**
|
||||
Checks an AudioStreamBasicDescription to see if it is linear PCM, which is an
|
||||
uncompressed, non-variable bit rate type format
|
||||
@param asbd A valid AudioStreamBasicDescription
|
||||
@return A BOOL indicating whether or not the AudioStreamBasicDescription is linear PCM
|
||||
*/
|
||||
+ (BOOL) isLinearPCM:(AudioStreamBasicDescription)asbd;
|
||||
|
||||
/**
|
||||
|
||||
@param channels The desired number of channels
|
||||
@param sampleRate The desired sample rate
|
||||
@return A new AudioStreamBasicDescription with the specified format.
|
||||
*/
|
||||
+(AudioStreamBasicDescription)M4AFormatWithNumberOfChannels:(UInt32)channels
|
||||
sampleRate:(float)sampleRate;
|
||||
|
||||
/**
|
||||
|
||||
@param sampleRate The desired sample rate
|
||||
@return A new AudioStreamBasicDescription with the specified format.
|
||||
*/
|
||||
+(AudioStreamBasicDescription)monoFloatFormatWithSampleRate:(float)sampleRate;
|
||||
|
||||
/**
|
||||
|
||||
@param sampleRate The desired sample rate for the AudioStreamBasicDescription
|
||||
@param sampleRate The desired sample rate
|
||||
@return A new AudioStreamBasicDescription with the specified format.
|
||||
*/
|
||||
+(AudioStreamBasicDescription)monoCanonicalFormatWithSampleRate:(float)sampleRate;
|
||||
|
||||
/**
|
||||
|
||||
@param sampleRate The desired sample rate for the AudioStreamBasicDescription
|
||||
@param sampleRate The desired sample rate
|
||||
@return A new AudioStreamBasicDescription with the specified format.
|
||||
*/
|
||||
+(AudioStreamBasicDescription)stereoCanonicalNonInterleavedFormatWithSampleRate:(float)sampleRate;
|
||||
|
||||
/**
|
||||
|
||||
@param sampleRate The desired sample rate for the AudioStreamBasicDescription
|
||||
@param sampleRate The desired sample rate
|
||||
@return A new AudioStreamBasicDescription with the specified format.
|
||||
*/
|
||||
+(AudioStreamBasicDescription)stereoFloatInterleavedFormatWithSampleRate:(float)sampleRate;
|
||||
|
||||
/**
|
||||
|
||||
@param sampleRate The desired sample rate
|
||||
@return A new AudioStreamBasicDescription with the specified format.
|
||||
*/
|
||||
+(AudioStreamBasicDescription)stereoFloatNonInterleavedFormatWithSampleRate:(float)sameRate;
|
||||
|
||||
///-----------------------------------------------------------
|
||||
/// @name AudioStreamBasicDescription Utilities
|
||||
///-----------------------------------------------------------
|
||||
@@ -114,7 +171,7 @@
|
||||
@param nChannels The number of expected channels on the description
|
||||
@param interleaved A flag indicating whether the stereo samples should be interleaved in the buffer
|
||||
*/
|
||||
+(void)setCanonicalAudioStreamBasicDescription:(AudioStreamBasicDescription)asbd
|
||||
+(void)setCanonicalAudioStreamBasicDescription:(AudioStreamBasicDescription*)asbd
|
||||
numberOfChannels:(UInt32)nChannels
|
||||
interleaved:(BOOL)interleaved;
|
||||
|
||||
@@ -169,6 +226,16 @@
|
||||
+(float)RMS:(float*)buffer
|
||||
length:(int)bufferSize;
|
||||
|
||||
/**
|
||||
Calculate the sign function sgn(x) =
|
||||
{ -1 , x < 0,
|
||||
{ 0 , x = 0,
|
||||
{ 1 , x > 0
|
||||
@param value The float value for which to use as x
|
||||
@return The float sign value
|
||||
*/
|
||||
+(float)SGN:(float)value;
|
||||
|
||||
#pragma mark - OSStatus Utility
|
||||
///-----------------------------------------------------------
|
||||
/// @name OSStatus Utility
|
||||
|
||||
+255
-140
@@ -28,110 +28,220 @@
|
||||
@implementation EZAudio
|
||||
|
||||
#pragma mark - AudioBufferList Utility
|
||||
+(AudioBufferList *)audioBufferList {
|
||||
return (AudioBufferList*)malloc(sizeof(AudioBufferList));
|
||||
+(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);
|
||||
}
|
||||
return audioBufferList;
|
||||
}
|
||||
|
||||
+(void)freeBufferList:(AudioBufferList *)bufferList {
|
||||
if( bufferList ){
|
||||
for( int i = 0; i < bufferList->mNumberBuffers; i++ ){
|
||||
if( bufferList->mBuffers[i].mData ){
|
||||
free(bufferList->mBuffers[i].mData);
|
||||
}
|
||||
+(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);
|
||||
}
|
||||
free(bufferList);
|
||||
}
|
||||
bufferList = NULL;
|
||||
}
|
||||
|
||||
#pragma mark - AudioStreamBasicDescription Utility
|
||||
/// AudioStreamFormatDescription Helpers
|
||||
|
||||
+(AudioStreamBasicDescription)monoFloatFormatWithSampleRate:(float)sampleRate {
|
||||
AudioStreamBasicDescription asbd;
|
||||
UInt32 byteSize = sizeof(AudioUnitSampleType);
|
||||
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)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)monoCanonicalFormatWithSampleRate:(float)sampleRate {
|
||||
AudioStreamBasicDescription asbd;
|
||||
UInt32 byteSize = sizeof(AudioUnitSampleType);
|
||||
asbd.mBitsPerChannel = 8 * byteSize;
|
||||
asbd.mBytesPerFrame = byteSize;
|
||||
asbd.mBytesPerPacket = byteSize;
|
||||
asbd.mChannelsPerFrame = 1;
|
||||
asbd.mFormatFlags = kAudioFormatFlagsCanonical|kAudioFormatFlagIsNonInterleaved;
|
||||
asbd.mFormatID = kAudioFormatLinearPCM;
|
||||
asbd.mFramesPerPacket = 1;
|
||||
asbd.mSampleRate = sampleRate;
|
||||
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);
|
||||
[EZAudio checkResult:AudioFormatGetProperty(kAudioFormatProperty_FormatInfo,
|
||||
0,
|
||||
NULL,
|
||||
&propSize,
|
||||
&asbd)
|
||||
operation:"Failed to fill out the rest of the m4a AudioStreamBasicDescription"];
|
||||
|
||||
return asbd;
|
||||
}
|
||||
|
||||
+(AudioStreamBasicDescription)stereoCanonicalNonInterleavedFormatWithSampleRate:(float)sampleRate {
|
||||
AudioStreamBasicDescription asbd;
|
||||
UInt32 byteSize = sizeof(AudioUnitSampleType);
|
||||
asbd.mBitsPerChannel = 8 * byteSize;
|
||||
asbd.mBytesPerFrame = byteSize;
|
||||
asbd.mBytesPerPacket = byteSize;
|
||||
asbd.mChannelsPerFrame = 2;
|
||||
asbd.mFormatFlags = kAudioFormatFlagsCanonical|kAudioFormatFlagIsNonInterleaved;
|
||||
asbd.mFormatID = kAudioFormatLinearPCM;
|
||||
asbd.mFramesPerPacket = 1;
|
||||
asbd.mSampleRate = sampleRate;
|
||||
return asbd;
|
||||
+ (BOOL)isInterleaved:(AudioStreamBasicDescription)asbd
|
||||
{
|
||||
return !(asbd.mFormatFlags & kAudioFormatFlagIsNonInterleaved);
|
||||
}
|
||||
|
||||
+(AudioStreamBasicDescription)stereoFloatInterleavedFormatWithSampleRate:(float)sampleRate {
|
||||
AudioStreamBasicDescription asbd;
|
||||
UInt32 byteSize = sizeof(AudioUnitSampleType);
|
||||
asbd.mBitsPerChannel = 8 * byteSize;
|
||||
asbd.mBytesPerFrame = 2 * byteSize;
|
||||
asbd.mBytesPerPacket = 2 * byteSize;
|
||||
asbd.mChannelsPerFrame = 2;
|
||||
asbd.mFormatFlags = kAudioFormatFlagIsPacked|kAudioFormatFlagIsFloat;
|
||||
asbd.mFormatID = kAudioFormatLinearPCM;
|
||||
asbd.mFramesPerPacket = 1;
|
||||
asbd.mSampleRate = sampleRate;
|
||||
return asbd;
|
||||
+ (BOOL)isLinearPCM:(AudioStreamBasicDescription)asbd
|
||||
{
|
||||
return asbd.mFormatID == kAudioFormatLinearPCM;
|
||||
}
|
||||
|
||||
+(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);
|
||||
[EZAudio 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.mBytesPerPacket = asbd.mChannelsPerFrame * floatByteSize;
|
||||
asbd.mFormatFlags = kAudioFormatFlagIsPacked|kAudioFormatFlagIsFloat;
|
||||
asbd.mFormatID = kAudioFormatLinearPCM;
|
||||
asbd.mFramesPerPacket = 1;
|
||||
asbd.mSampleRate = sampleRate;
|
||||
return asbd;
|
||||
}
|
||||
|
||||
+(AudioStreamBasicDescription)stereoFloatNonInterleavedFormatWithSampleRate:(float)sampleRate
|
||||
{
|
||||
AudioStreamBasicDescription asbd;
|
||||
UInt32 floatByteSize = sizeof(float);
|
||||
asbd.mBitsPerChannel = 8 * floatByteSize;
|
||||
asbd.mBytesPerFrame = floatByteSize;
|
||||
asbd.mBytesPerPacket = floatByteSize;
|
||||
asbd.mChannelsPerFrame = 2;
|
||||
asbd.mFormatFlags = kAudioFormatFlagIsFloat|kAudioFormatFlagIsNonInterleaved;
|
||||
asbd.mFormatID = kAudioFormatLinearPCM;
|
||||
asbd.mFramesPerPacket = 1;
|
||||
asbd.mSampleRate = sampleRate;
|
||||
return asbd;
|
||||
}
|
||||
|
||||
+(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);
|
||||
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);
|
||||
}
|
||||
|
||||
+(void)setCanonicalAudioStreamBasicDescription:(AudioStreamBasicDescription)asbd
|
||||
+(void)setCanonicalAudioStreamBasicDescription:(AudioStreamBasicDescription*)asbd
|
||||
numberOfChannels:(UInt32)nChannels
|
||||
interleaved:(BOOL)interleaved {
|
||||
asbd.mFormatID = kAudioFormatLinearPCM;
|
||||
int sampleSize = ((UInt32)sizeof(AudioSampleType));
|
||||
asbd.mFormatFlags = kAudioFormatFlagsCanonical;
|
||||
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;
|
||||
}
|
||||
|
||||
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 - OSStatus Utility
|
||||
@@ -156,29 +266,29 @@
|
||||
withBufferSize:(int)bufferLength
|
||||
toScrollHistory:(float*)scrollHistory
|
||||
withScrollHistorySize:(int)scrollHistoryLength {
|
||||
NSAssert(scrollHistoryLength>=bufferLength,@"Scroll history array length must be greater buffer length");
|
||||
NSAssert(scrollHistoryLength>0,@"Scroll history array length must be greater than 0");
|
||||
NSAssert(bufferLength>0,@"Buffer array length must be greater than 0");
|
||||
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);
|
||||
NSAssert(scrollHistoryLength>=bufferLength,@"Scroll history array length must be greater buffer length");
|
||||
NSAssert(scrollHistoryLength>0,@"Scroll history array length must be greater than 0");
|
||||
NSAssert(bufferLength>0,@"Buffer array length must be greater than 0");
|
||||
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 val[1]; val[0] = value;
|
||||
[self appendBufferAndShift:val
|
||||
withBufferSize:1
|
||||
toScrollHistory:scrollHistory
|
||||
withScrollHistorySize:scrollHistoryLength];
|
||||
}
|
||||
|
||||
+(float)MAP:(float)value
|
||||
@@ -186,18 +296,23 @@
|
||||
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 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 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 - Plot Utility
|
||||
@@ -207,48 +322,48 @@
|
||||
withBuffer:(float *)buffer
|
||||
withBufferSize:(int)bufferSize
|
||||
isResolutionChanging:(BOOL*)isChanging {
|
||||
|
||||
//
|
||||
size_t floatByteSize = sizeof(float);
|
||||
|
||||
//
|
||||
if( *scrollHistory == NULL ){
|
||||
// Create the history buffer
|
||||
*scrollHistory = (float*)calloc(kEZAudioPlotMaxHistoryBufferLength,floatByteSize);
|
||||
}
|
||||
|
||||
//
|
||||
if( !*isChanging ){
|
||||
float rms = [EZAudio RMS:buffer length:bufferSize];
|
||||
if( *index < scrollHistoryLength ){
|
||||
float *hist = *scrollHistory;
|
||||
hist[*index] = rms;
|
||||
(*index)++;
|
||||
|
||||
//
|
||||
size_t floatByteSize = sizeof(float);
|
||||
|
||||
//
|
||||
if( *scrollHistory == NULL ){
|
||||
// Create the history buffer
|
||||
*scrollHistory = (float*)calloc(1024,floatByteSize);
|
||||
}
|
||||
else {
|
||||
[EZAudio appendValue:rms
|
||||
toScrollHistory:*scrollHistory
|
||||
withScrollHistorySize:scrollHistoryLength];
|
||||
|
||||
//
|
||||
if( !*isChanging ){
|
||||
float rms = [EZAudio RMS:buffer length:bufferSize];
|
||||
if( *index < scrollHistoryLength ){
|
||||
float *hist = *scrollHistory;
|
||||
hist[*index] = rms;
|
||||
(*index)++;
|
||||
}
|
||||
else {
|
||||
[EZAudio appendValue:rms
|
||||
toScrollHistory:*scrollHistory
|
||||
withScrollHistorySize:scrollHistoryLength];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
#pragma mark - TPCircularBuffer Utility
|
||||
+(void)circularBuffer:(TPCircularBuffer *)circularBuffer withSize:(int)size {
|
||||
TPCircularBufferInit(circularBuffer,size);
|
||||
TPCircularBufferInit(circularBuffer,size);
|
||||
}
|
||||
|
||||
+(void)appendDataToCircularBuffer:(TPCircularBuffer*)circularBuffer
|
||||
fromAudioBufferList:(AudioBufferList*)audioBufferList {
|
||||
TPCircularBufferProduceBytes(circularBuffer,
|
||||
audioBufferList->mBuffers[0].mData,
|
||||
audioBufferList->mBuffers[0].mDataByteSize);
|
||||
TPCircularBufferProduceBytes(circularBuffer,
|
||||
audioBufferList->mBuffers[0].mData,
|
||||
audioBufferList->mBuffers[0].mDataByteSize);
|
||||
}
|
||||
|
||||
+(void)freeCircularBuffer:(TPCircularBuffer *)circularBuffer {
|
||||
TPCircularBufferClear(circularBuffer);
|
||||
TPCircularBufferCleanup(circularBuffer);
|
||||
TPCircularBufferClear(circularBuffer);
|
||||
TPCircularBufferCleanup(circularBuffer);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// EZAudioConverter.h
|
||||
// EZAudioPlayFileExample
|
||||
//
|
||||
// Created by Syed Haris Ali on 2/14/15.
|
||||
// Copyright (c) 2015 Syed Haris Ali. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <AudioToolbox/AudioToolbox.h>
|
||||
|
||||
@interface EZAudioConverter : NSObject
|
||||
|
||||
@property (nonatomic, assign, readonly) AudioStreamBasicDescription inputFormat;
|
||||
@property (nonatomic, assign, readonly) AudioStreamBasicDescription outputFormat;
|
||||
|
||||
+ (instancetype) converterWithInputFormat:(AudioStreamBasicDescription)inputFormat
|
||||
outputFormat:(AudioStreamBasicDescription)outputFormat;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// EZAudioConverter.m
|
||||
// EZAudioPlayFileExample
|
||||
//
|
||||
// Created by Syed Haris Ali on 2/14/15.
|
||||
// Copyright (c) 2015 Syed Haris Ali. All rights reserved.
|
||||
//
|
||||
|
||||
#import "EZAudioConverter.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
AudioConverterRef converterRef;
|
||||
AudioStreamBasicDescription inputFormat;
|
||||
AudioStreamBasicDescription outputFormat;
|
||||
} EZAudioConverterInfo;
|
||||
|
||||
@interface EZAudioConverter ()
|
||||
@property (nonatomic, assign) EZAudioConverterInfo info;
|
||||
@end
|
||||
|
||||
@implementation EZAudioConverter
|
||||
|
||||
+ (instancetype)converterWithInputFormat:(AudioStreamBasicDescription)inputFormat
|
||||
outputFormat:(AudioStreamBasicDescription)outputFormat
|
||||
{
|
||||
id converter = [[self alloc] init];
|
||||
|
||||
EZAudioConverterInfo info;
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.inputFormat = inputFormat;
|
||||
info.outputFormat = outputFormat;
|
||||
((EZAudioConverter *)converter).info = info;
|
||||
|
||||
return converter;
|
||||
}
|
||||
|
||||
@end
|
||||
+227
-69
@@ -25,10 +25,26 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <AudioToolbox/AudioToolbox.h>
|
||||
#import "EZAudioWaveformData.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@class EZAudio;
|
||||
@class EZAudioConverter;
|
||||
@class EZAudioFile;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
typedef NS_ENUM(NSUInteger, EZAudioFilePermission)
|
||||
{
|
||||
EZAudioFilePermissionRead = kAudioFileReadPermission,
|
||||
EZAudioFilePermissionWrite = kAudioFileWritePermission,
|
||||
EZAudioFilePermissionReadWrite = kAudioFileReadWritePermission,
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
#pragma mark - EZAudioFileDelegate
|
||||
//------------------------------------------------------------------------------
|
||||
/**
|
||||
The EZAudioFileDelegate provides event callbacks for the EZAudioFile object. These type of events are triggered by reads and seeks on the file and gives feedback such as the audio data read as a float array for visualizations and the new seek position for UI updating.
|
||||
*/
|
||||
@@ -42,102 +58,192 @@
|
||||
@param bufferSize The length of the buffers float arrays
|
||||
@param numberOfChannels The number of channels. 2 for stereo, 1 for mono.
|
||||
*/
|
||||
-(void) audioFile:(EZAudioFile*)audioFile
|
||||
readAudio:(float**)buffer
|
||||
withBufferSize:(UInt32)bufferSize
|
||||
withNumberOfChannels:(UInt32)numberOfChannels;
|
||||
- (void) audioFile:(EZAudioFile*)audioFile
|
||||
readAudio:(float**)buffer
|
||||
withBufferSize:(UInt32)bufferSize
|
||||
withNumberOfChannels:(UInt32)numberOfChannels;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Occurs when the audio file's internal seek position has been updated by the EZAudioFile functions `readFrames:audioBufferList:bufferSize:eof:` or `audioFile:updatedPosition:`.
|
||||
@param audioFile The instance of the EZAudio in which the change occured
|
||||
@param framePosition The new frame index as a 64-bit signed integer
|
||||
*/
|
||||
-(void)audioFile:(EZAudioFile*)audioFile
|
||||
updatedPosition:(SInt64)framePosition;
|
||||
- (void) audioFile:(EZAudioFile*)audioFile
|
||||
updatedPosition:(SInt64)framePosition;
|
||||
|
||||
@end
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
#pragma mark - EZAudioFile
|
||||
//------------------------------------------------------------------------------
|
||||
/**
|
||||
The EZAudioFile provides a lightweight and intuitive way to asynchronously interact with audio files. These interactions included reading audio data, seeking within an audio file, getting information about the file, and pulling the waveform data for visualizing the contents of the audio file. The EZAudioFileDelegate provides event callbacks for when reads, seeks, and various updates happen within the audio file to allow the caller to interact with the action in meaningful ways. Common use cases here could be to read the audio file's data as AudioBufferList structures for output (see EZOutput) and visualizing the audio file's data as a float array using an audio plot (see EZAudioPlot).
|
||||
*/
|
||||
@interface EZAudioFile : NSObject
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
#pragma mark - Blocks
|
||||
//------------------------------------------------------------------------------
|
||||
/**
|
||||
A block used when returning back the waveform data. The waveform data itself will be an array of float values and the length indicates the total length of the float array.
|
||||
@param waveformData An array of float values representing the amplitude data from the audio waveform
|
||||
@param length The length of the waveform data's float array
|
||||
*/
|
||||
typedef void (^WaveformDataCompletionBlock)(float *waveformData, UInt32 length);
|
||||
typedef void (^WaveformDataCompletionBlock)(EZAudioWaveformData *waveformData);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
#pragma mark - Properties
|
||||
//------------------------------------------------------------------------------
|
||||
/**
|
||||
A EZAudioFileDelegate for the audio file that is used to return events such as new seek positions within the file and the read audio data as a float array.
|
||||
*/
|
||||
@property (nonatomic,assign) id<EZAudioFileDelegate> audioFileDelegate;
|
||||
@property (nonatomic, weak) id<EZAudioFileDelegate> delegate;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
#pragma mark - Initialization
|
||||
//------------------------------------------------------------------------------
|
||||
/**
|
||||
@name Initialization
|
||||
*/
|
||||
|
||||
/**
|
||||
The resolution of the waveform data. This value specifies how the recommendedDrawingFrameRate chooses itself. A low value like 128 will render a waveform containing 128 points representing a low resolution waveform while a high value like 4096 will render a high quality waveform. Higher resolutions provide more detail, but take more work to render in the audio waveform plots (EZAudioPlot or EZAudioPlotGL) while lower resolutions providel less detail, but work better for displaying many at a time (like in a UITableView)
|
||||
Creates a new instance of the EZAudioFile using a file path URL. Read only.
|
||||
@param url The file path reference of the audio file as an NSURL.
|
||||
@return The newly created EZAudioFile instance.
|
||||
*/
|
||||
@property (nonatomic,assign) UInt32 waveformResolution;
|
||||
- (instancetype)initWithURL:(NSURL*)url;
|
||||
|
||||
#pragma mark - Initializers
|
||||
///-----------------------------------------------------------
|
||||
/// @name Initializers
|
||||
///-----------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Creates a new instance of the EZAudioFile using a file path URL.
|
||||
@param url The file path reference of the audio file as an NSURL.
|
||||
@param permission A constant describing what we intend on doing with the audio file (read, write, or both)
|
||||
@param fileFormat An AudioStreamBasicDescription that will be used to create the audio file if it does not exist if the permission argument is set to EZAudioFilePermissionWrite or EZAudioFilePermissionReadWrite. Not used for EZAudioFilePermissionRead permission.
|
||||
@return The newly created EZAudioFile instance.
|
||||
*/
|
||||
-(EZAudioFile*)initWithURL:(NSURL*)url;
|
||||
- (instancetype)initWithURL:(NSURL*)url
|
||||
permission:(EZAudioFilePermission)permission
|
||||
fileFormat:(AudioStreamBasicDescription)fileFormat;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Creates a new instance of the EZAudioFile using a file path URL and allows specifying an EZAudioFileDelegate.
|
||||
@param url The file path reference of the audio file as an NSURL.
|
||||
@param delegate The audio file delegate that receives events specified by the EZAudioFileDelegate protocol
|
||||
@param permission A constant describing what we intend on doing with the audio file (read, write, or both)
|
||||
@param fileFormat An AudioStreamBasicDescription that will be used to create the audio file if it does not exist if the permission argument is set to EZAudioFilePermissionWrite or EZAudioFilePermissionReadWrite. Not used for EZAudioFilePermissionRead permission.
|
||||
@return The newly created EZAudioFile instance.
|
||||
*/
|
||||
-(EZAudioFile*)initWithURL:(NSURL*)url
|
||||
andDelegate:(id<EZAudioFileDelegate>)delegate;
|
||||
- (instancetype)initWithURL:(NSURL*)url
|
||||
delegate:(id<EZAudioFileDelegate>)delegate
|
||||
permission:(EZAudioFilePermission)permission
|
||||
fileFormat:(AudioStreamBasicDescription)fileFormat;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Class method that creates a new instance of the EZAudioFile using a file path URL and allows specifying an EZAudioFileDelegate, a read/write permission, a file format incase a new file is being written, and a client format for a format that will be used when read samples (different from file format).
|
||||
@param url The file path reference of the audio file as an NSURL.
|
||||
@param delegate The audio file delegate that receives events specified by the EZAudioFileDelegate protocol
|
||||
@param permission A constant describing what we intend on doing with the audio file (read, write, or both)
|
||||
@param fileFormat An AudioStreamBasicDescription that will be used to create the audio file if it does not exist if the permission argument is set to EZAudioFilePermissionWrite or EZAudioFilePermissionReadWrite. Not used for EZAudioFilePermissionRead permission.
|
||||
@param clientFormat An AudioStreamBasicDescription that will be used as the client format on the audio file. For instance, the audio file might be in a 22.5 kHz sample rate format in its file format, but your app wants to read the samples at a sample rate of 44.1 kHz so it can iterate with other components (like a audio processing graph) without any weird playback effects. If this initializer is not used then a non-interleaved float format will be assumed.
|
||||
@return The newly created EZAudioFile instance.
|
||||
*/
|
||||
- (instancetype)initWithURL:(NSURL*)url
|
||||
delegate:(id<EZAudioFileDelegate>)delegate
|
||||
permission:(EZAudioFilePermission)permission
|
||||
fileFormat:(AudioStreamBasicDescription)fileFormat
|
||||
clientFormat:(AudioStreamBasicDescription)clientFormat;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
#pragma mark - Class Initializers
|
||||
///-----------------------------------------------------------
|
||||
/// @name Class Initializers
|
||||
///-----------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
/**
|
||||
@name Class Initializers
|
||||
*/
|
||||
|
||||
/**
|
||||
Class method that creates a new instance of the EZAudioFile using a file path URL.
|
||||
@param url The file path reference of the audio file as an NSURL.
|
||||
@return The newly created EZAudioFile instance.
|
||||
*/
|
||||
+(EZAudioFile*)audioFileWithURL:(NSURL*)url;
|
||||
+ (instancetype)audioFileWithURL:(NSURL*)url;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Class method that creates a new instance of the EZAudioFile using a file path URL.
|
||||
@param url The file path reference of the audio file as an NSURL.
|
||||
@param permission A constant describing what we intend on doing with the audio file (read, write, or both)
|
||||
@param fileFormat An AudioStreamBasicDescription that will be used to create the audio file if it does not exist if the permission argument is set to EZAudioFilePermissionWrite or EZAudioFilePermissionReadWrite. Not used for EZAudioFilePermissionRead permission.
|
||||
@return The newly created EZAudioFile instance.
|
||||
*/
|
||||
+ (instancetype)audioFileWithURL:(NSURL*)url
|
||||
permission:(EZAudioFilePermission)permission
|
||||
fileFormat:(AudioStreamBasicDescription)fileFormat;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Class method that creates a new instance of the EZAudioFile using a file path URL and allows specifying an EZAudioFileDelegate.
|
||||
@param url The file path reference of the audio file as an NSURL.
|
||||
@param delegate The audio file delegate that receives events specified by the EZAudioFileDelegate protocol
|
||||
@param permission A constant describing what we intend on doing with the audio file (read, write, or both)
|
||||
@param fileFormat An AudioStreamBasicDescription that will be used to create the audio file if it does not exist if the permission argument is set to EZAudioFilePermissionWrite or EZAudioFilePermissionReadWrite. Not used for EZAudioFilePermissionRead permission.
|
||||
@return The newly created EZAudioFile instance.
|
||||
*/
|
||||
+(EZAudioFile*)audioFileWithURL:(NSURL*)url
|
||||
andDelegate:(id<EZAudioFileDelegate>)delegate;
|
||||
+ (instancetype) audioFileWithURL:(NSURL*)url
|
||||
delegate:(id<EZAudioFileDelegate>)delegate
|
||||
permission:(EZAudioFilePermission)permission
|
||||
fileFormat:(AudioStreamBasicDescription)fileFormat;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Class method that creates a new instance of the EZAudioFile using a file path URL and allows specifying an EZAudioFileDelegate, a read/write permission, a file format incase a new file is being written, and a client format for a format that will be used when read samples (different from file format).
|
||||
@param url The file path reference of the audio file as an NSURL.
|
||||
@param delegate The audio file delegate that receives events specified by the EZAudioFileDelegate protocol
|
||||
@param permission A constant describing what we intend on doing with the audio file (read, write, or both)
|
||||
@param fileFormat An AudioStreamBasicDescription that will be used to create the audio file if it does not exist if the permission argument is set to EZAudioFilePermissionWrite or EZAudioFilePermissionReadWrite. Not used for EZAudioFilePermissionRead permission.
|
||||
@param clientFormat An AudioStreamBasicDescription that will be used as the client format on the audio file. A client format is different from the file format in that it is the format of the other components interacting with this file. For instance, the file on disk could be a 22.5 kHz, float format, but we might have an audio processing graph that has a 44.1 kHz, signed integer format that we'd like to interact with. The client format lets us set that 44.1 kHz format on the audio file to properly read samples from it with any interpolation or format conversion that must take place. If not specified the default value is equal to the class method, 'defaultClientFormat'
|
||||
@return The newly created EZAudioFile instance.
|
||||
*/
|
||||
+ (instancetype)audioFileWithURL:(NSURL*)url
|
||||
delegate:(id<EZAudioFileDelegate>)delegate
|
||||
permission:(EZAudioFilePermission)permission
|
||||
fileFormat:(AudioStreamBasicDescription)fileFormat
|
||||
clientFormat:(AudioStreamBasicDescription)clientFormat;;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
#pragma mark - Class Methods
|
||||
///-----------------------------------------------------------
|
||||
/// @name Class Methods
|
||||
///-----------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
/**
|
||||
@name Class Methods
|
||||
*/
|
||||
|
||||
/**
|
||||
A class method that subclasses can override to specify the default client format that will be used to read audio data from this file. A client format is different from the file format in that it is the format of the other components interacting with this file. For instance, the file on disk could be a 22.5 kHz, float format, but we might have an audio processing graph that has a 44.1 kHz, signed integer format that we'd like to interact with. The client format lets us set that 44.1 kHz format on the audio file to properly read samples from it with any interpolation or format conversion that must take place. Default is stereo, non-interleaved, 44.1 kHz.
|
||||
@return An AudioStreamBasicDescription that serves as the audio file's client format.
|
||||
*/
|
||||
+ (AudioStreamBasicDescription)defaultClientFormat;
|
||||
|
||||
/**
|
||||
Provides an array of the supported audio files types. Each audio file type is provided as a string, i.e. @"caf". Useful for filtering lists of files in an open panel to only the types allowed.
|
||||
@return An array of NSString objects representing the represented file types.
|
||||
*/
|
||||
+(NSArray*)supportedAudioFileTypes;
|
||||
+ (NSArray *)supportedAudioFileTypes;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
#pragma mark - Events
|
||||
///-----------------------------------------------------------
|
||||
/// @name Reading The Audio File
|
||||
///-----------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
/**
|
||||
@name Reading The Audio File
|
||||
*/
|
||||
|
||||
/**
|
||||
Reads a specified number of frames from the audio file. In addition, this will notify the EZAudioFileDelegate (if specified) of the read data as a float array with the audioFile:readAudio:withBufferSize:withNumberOfChannels: event and the new seek position within the file with the audioFile:updatedPosition: event.
|
||||
@@ -147,89 +253,141 @@ typedef void (^WaveformDataCompletionBlock)(float *waveformData, UInt32 length);
|
||||
@param eof A pointer to a BOOL in which to store whether the read operation reached the end of the audio file.
|
||||
*/
|
||||
-(void)readFrames:(UInt32)frames
|
||||
audioBufferList:(AudioBufferList*)audioBufferList
|
||||
bufferSize:(UInt32*)bufferSize
|
||||
eof:(BOOL*)eof;
|
||||
audioBufferList:(AudioBufferList *)audioBufferList
|
||||
bufferSize:(UInt32 *)bufferSize
|
||||
eof:(BOOL *)eof;
|
||||
|
||||
///-----------------------------------------------------------
|
||||
/// @name Seeking Through The Audio File
|
||||
///-----------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
#pragma mark - Seeking Through The Audio File
|
||||
//------------------------------------------------------------------------------
|
||||
/**
|
||||
@name Seeking Through The Audio File
|
||||
*/
|
||||
|
||||
/**
|
||||
Seeks through an audio file to a specified frame. This will notify the EZAudioFileDelegate (if specified) with the audioFile:updatedPosition: function.
|
||||
@param frame The new frame position to seek to as a SInt64.
|
||||
*/
|
||||
-(void)seekToFrame:(SInt64)frame;
|
||||
- (void)seekToFrame:(SInt64)frame;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
#pragma mark - Getters
|
||||
///-----------------------------------------------------------
|
||||
/// @name Getting Information About The Audio File
|
||||
///-----------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
/**
|
||||
@name Getting Information About The Audio File
|
||||
*/
|
||||
|
||||
/**
|
||||
Provides the AudioStreamBasicDescription structure used within the app. The file's format will be converted to this format and then sent back as either a float array or a `AudioBufferList` pointer. Use this when communicating with other EZAudio components.
|
||||
@return An AudioStreamBasicDescription structure describing the format of the audio file.
|
||||
*/
|
||||
-(AudioStreamBasicDescription)clientFormat;
|
||||
- (AudioStreamBasicDescription)clientFormat;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Provides the AudioStreamBasicDescription structure containing the format of the file.
|
||||
@return An AudioStreamBasicDescription structure describing the format of the audio file.
|
||||
*/
|
||||
-(AudioStreamBasicDescription)fileFormat;
|
||||
- (AudioStreamBasicDescription)fileFormat;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Provides the frame index (a.k.a the seek positon) within the audio file as an integer. This can be helpful when seeking through the audio file.
|
||||
@return The current frame index within the audio file as a SInt64.
|
||||
*/
|
||||
-(SInt64)frameIndex;
|
||||
- (SInt64)frameIndex;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Provides a dictionary containing the metadata (ID3) tags that are included in the header for the audio file. Typically this contains stuff like artist, title, release year, etc.
|
||||
@return An NSDictionary containing the metadata for the audio file.
|
||||
*/
|
||||
- (NSDictionary *)metadata;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Provides the total duration of the audio file in seconds.
|
||||
@return The total duration of the audio file as a Float32.
|
||||
*/
|
||||
-(Float32)totalDuration;
|
||||
- (NSTimeInterval)totalDuration;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Provides the total frame count of the audio file.
|
||||
@return The total number of frames in the audio file as a SInt64.
|
||||
Provides the total frame count of the audio file in the client format.
|
||||
@return The total number of frames in the audio file in the AudioStreamBasicDescription representing the client format as a SInt64.
|
||||
*/
|
||||
-(SInt64)totalFrames;
|
||||
- (SInt64)totalClientFrames;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Provides the total frame count of the audio file in the file format.
|
||||
@return The total number of frames in the audio file in the AudioStreamBasicDescription representing the file format as a SInt64.
|
||||
*/
|
||||
- (SInt64)totalFrames;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Provides the NSURL for the audio file.
|
||||
@return An NSURL representing the path of the EZAudioFile instance.
|
||||
*/
|
||||
-(NSURL*)url;
|
||||
- (NSURL*)url;
|
||||
|
||||
#pragma mark - Helpers
|
||||
///-----------------------------------------------------------
|
||||
/// @name Manipulating The Audio Data
|
||||
///-----------------------------------------------------------
|
||||
//------------------------------------------------------------------------------
|
||||
#pragma mark - Setters
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Tells the caller whether the EZAudioFile has cached waveform data that was loaded via the getWaveformDataWithCompletionBlock: function.
|
||||
* @return A BOOL indicating whether there is cached waveform data
|
||||
A client format is different from the file format in that it is the format of the other components interacting with this file. For instance, the file on disk could be a 22.5 kHz, float format, but we might have an audio processing graph that has a 44.1 kHz, signed integer format that we'd like to interact with. The client format lets us set that 44.1 kHz format on the audio file to properly read samples from it with any interpolation or format conversion that must take place. Default is stereo, non-interleaved, 44.1 kHz.
|
||||
@param clientFormat An AudioStreamBasicDescription that should serve as the audio file's client format.
|
||||
*/
|
||||
-(BOOL)hasLoadedAudioData;
|
||||
- (void)setClientFormat:(AudioStreamBasicDescription)clientFormat;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
#pragma mark - Helpers
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Synchronously pulls the waveform amplitude data into a float array for the receiver. This returns a waveform with a default resolution of 1024, meaning there are 1024 data points to plot the waveform.
|
||||
@param numberOfPoints A UInt32 representing the number of data points you need. The higher the number of points the more detailed the waveform will be.
|
||||
@return A EZAudioWaveformData instance containing the audio data for all channels of the audio.
|
||||
*/
|
||||
- (EZAudioWaveformData *)getWaveformData;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Synchronously pulls the waveform amplitude data into a float array for the receiver.
|
||||
@param numberOfPoints A UInt32 representing the number of data points you need. The higher the number of points the more detailed the waveform will be.
|
||||
@return A EZAudioWaveformData instance containing the audio data for all channels of the audio.
|
||||
*/
|
||||
- (EZAudioWaveformData *)getWaveformDataWithNumberOfPoints:(UInt32)numberOfPoints;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Asynchronously pulls the waveform amplitude data into a float array for the receiver. This returns a waveform with a default resolution of 1024, meaning there are 1024 data points to plot the waveform.
|
||||
@param completion A WaveformDataCompletionBlock that executes when the waveform data has been extracted. Provides a `EZAudioWaveformData` instance containing the waveform data for all audio channels.
|
||||
*/
|
||||
- (void)getWaveformDataWithCompletionBlock:(WaveformDataCompletionBlock)completion;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
Asynchronously pulls the waveform amplitude data into a float array for the receiver.
|
||||
@param waveformDataCompletionBlock A WaveformDataCompletionBlock that executes when the waveform data has been extracted. Provides the waveform data as a float array and the length of the array.
|
||||
@param numberOfPoints A UInt32 representing the number of data points you need. The higher the number of points the more detailed the waveform will be.
|
||||
@param completion A WaveformDataCompletionBlock that executes when the waveform data has been extracted. Provides a `EZAudioWaveformData` instance containing the waveform data for all audio channels.
|
||||
*/
|
||||
-(void)getWaveformDataWithCompletionBlock:(WaveformDataCompletionBlock)waveformDataCompletionBlock;
|
||||
- (void)getWaveformDataWithNumberOfPoints:(UInt32)numberOfPoints
|
||||
completion:(WaveformDataCompletionBlock)completion;
|
||||
|
||||
/**
|
||||
Provides the minimum number of buffers that would be required with the constant frames read rate provided.
|
||||
@param frameRate A constant frame rate to use when calculating the number of buffers needed as a UInt32.
|
||||
@return The minimum number of buffers required for the constant frames read rate provided as a UInt32.
|
||||
*/
|
||||
-(UInt32)minBuffersWithFrameRate:(UInt32)frameRate;
|
||||
|
||||
/**
|
||||
Provides a frame rate to use when drawing and averaging a bin of values to create each point in a graph. The ideal amount of end buffers seems to be between 1000-3000 so we determine a frame rate per audio file that can achieve a high degree of detail for the entire waveform.
|
||||
@return A frame rate value as a UInt32 to use when reading frames in a file.
|
||||
*/
|
||||
-(UInt32)recommendedDrawingFrameRate;
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@end
|
||||
|
||||
+540
-250
@@ -25,310 +25,600 @@
|
||||
|
||||
#import "EZAudioFile.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#import "EZAudio.h"
|
||||
#import "EZAudioConverter.h"
|
||||
#import "EZAudioWaveformData.h"
|
||||
#include <pthread.h>
|
||||
|
||||
#define kEZAudioFileWaveformDefaultResolution (1024)
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@interface EZAudioFile (){
|
||||
|
||||
// Reading from the audio file
|
||||
ExtAudioFileRef _audioFile;
|
||||
AudioStreamBasicDescription _clientFormat;
|
||||
AudioStreamBasicDescription _fileFormat;
|
||||
float **_floatBuffers;
|
||||
AEFloatConverter *_floatConverter;
|
||||
SInt64 _frameIndex;
|
||||
CFURLRef _sourceURL;
|
||||
Float32 _totalDuration;
|
||||
SInt64 _totalFrames;
|
||||
|
||||
// Waveform Data
|
||||
float *_waveformData;
|
||||
UInt32 _waveformFrameRate;
|
||||
UInt32 _waveformTotalBuffers;
|
||||
|
||||
}
|
||||
// errors
|
||||
static OSStatus EZAudioFileReadPermissionFileDoesNotExistCode = -88776;
|
||||
|
||||
// constants
|
||||
static UInt32 EZAudioFileWaveformDefaultResolution = 1024;
|
||||
static NSString *EZAudioFileWaveformDataQueueIdentifier = @"com.ezaudio.waveformQueue";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
typedef struct
|
||||
{
|
||||
AudioFileID audioFileID;
|
||||
AudioStreamBasicDescription clientFormat;
|
||||
Float32 duration;
|
||||
ExtAudioFileRef extAudioFileRef;
|
||||
AudioStreamBasicDescription fileFormat;
|
||||
SInt64 frames;
|
||||
EZAudioFilePermission permission;
|
||||
CFURLRef sourceURL;
|
||||
} EZAudioFileInfo;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
#pragma mark - EZAudioFile
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@interface EZAudioFile ()
|
||||
@property (nonatomic) EZAudioFileInfo info;
|
||||
@property (nonatomic) pthread_mutex_t lock;
|
||||
@property (nonatomic) dispatch_queue_t waveformQueue;
|
||||
@end
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@implementation EZAudioFile
|
||||
@synthesize audioFileDelegate = _audioFileDelegate;
|
||||
@synthesize waveformResolution = _waveformResolution;
|
||||
|
||||
#pragma mark - Initializers
|
||||
-(EZAudioFile*)initWithURL:(NSURL*)url {
|
||||
self = [super init];
|
||||
if(self){
|
||||
_sourceURL = (__bridge CFURLRef)url;
|
||||
[self _configureAudioFile];
|
||||
}
|
||||
return self;
|
||||
//------------------------------------------------------------------------------
|
||||
#pragma mark - Initialization
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
{
|
||||
memset(&_info, 0, sizeof(_info));
|
||||
_info.permission = EZAudioFilePermissionRead;
|
||||
pthread_mutex_init(&_lock, NULL);
|
||||
_waveformQueue = dispatch_queue_create(EZAudioFileWaveformDataQueueIdentifier.UTF8String, DISPATCH_QUEUE_PRIORITY_DEFAULT);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
-(EZAudioFile *)initWithURL:(NSURL *)url andDelegate:(id<EZAudioFileDelegate>)delegate {
|
||||
self = [self initWithURL:url];
|
||||
if(self){
|
||||
self.audioFileDelegate = delegate;
|
||||
}
|
||||
return self;
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
- (instancetype)initWithURL:(NSURL *)url
|
||||
{
|
||||
AudioStreamBasicDescription asbd;
|
||||
return [self initWithURL:url
|
||||
permission:EZAudioFilePermissionRead
|
||||
fileFormat:asbd];
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
- (instancetype)initWithURL:(NSURL*)url
|
||||
permission:(EZAudioFilePermission)permission
|
||||
fileFormat:(AudioStreamBasicDescription)fileFormat
|
||||
{
|
||||
return [self initWithURL:url
|
||||
delegate:nil
|
||||
permission:permission
|
||||
fileFormat:fileFormat];
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
- (instancetype)initWithURL:(NSURL*)url
|
||||
delegate:(id<EZAudioFileDelegate>)delegate
|
||||
permission:(EZAudioFilePermission)permission
|
||||
fileFormat:(AudioStreamBasicDescription)fileFormat
|
||||
{
|
||||
return [self initWithURL:url
|
||||
delegate:delegate
|
||||
permission:permission
|
||||
fileFormat:fileFormat
|
||||
clientFormat:[self.class defaultClientFormat]];
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
- (instancetype)initWithURL:(NSURL*)url
|
||||
delegate:(id<EZAudioFileDelegate>)delegate
|
||||
permission:(EZAudioFilePermission)permission
|
||||
fileFormat:(AudioStreamBasicDescription)fileFormat
|
||||
clientFormat:(AudioStreamBasicDescription)clientFormat
|
||||
{
|
||||
self = [self init];
|
||||
if(self)
|
||||
{
|
||||
_info.clientFormat = clientFormat;
|
||||
_info.fileFormat = fileFormat;
|
||||
_info.permission = permission;
|
||||
_info.sourceURL = (__bridge CFURLRef)url;
|
||||
self.delegate = delegate;
|
||||
[self setup];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
#pragma mark - Class Initializers
|
||||
+(EZAudioFile*)audioFileWithURL:(NSURL*)url {
|
||||
return [[EZAudioFile alloc] initWithURL:url];
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
+ (instancetype)audioFileWithURL:(NSURL*)url
|
||||
{
|
||||
return [[self alloc] initWithURL:url];
|
||||
}
|
||||
|
||||
+(EZAudioFile *)audioFileWithURL:(NSURL *)url andDelegate:(id<EZAudioFileDelegate>)delegate {
|
||||
return [[EZAudioFile alloc] initWithURL:url andDelegate:delegate];
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
+ (instancetype)audioFileWithURL:(NSURL*)url
|
||||
permission:(EZAudioFilePermission)permission
|
||||
fileFormat:(AudioStreamBasicDescription)fileFormat
|
||||
{
|
||||
return [[self alloc] initWithURL:url
|
||||
permission:permission
|
||||
fileFormat:fileFormat];
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
+ (instancetype)audioFileWithURL:(NSURL*)url
|
||||
delegate:(id<EZAudioFileDelegate>)delegate
|
||||
permission:(EZAudioFilePermission)permission
|
||||
fileFormat:(AudioStreamBasicDescription)fileFormat
|
||||
{
|
||||
return [[self alloc] initWithURL:url
|
||||
delegate:delegate
|
||||
permission:permission
|
||||
fileFormat:fileFormat];
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
+ (instancetype)audioFileWithURL:(NSURL*)url
|
||||
delegate:(id<EZAudioFileDelegate>)delegate
|
||||
permission:(EZAudioFilePermission)permission
|
||||
fileFormat:(AudioStreamBasicDescription)fileFormat
|
||||
clientFormat:(AudioStreamBasicDescription)clientFormat
|
||||
{
|
||||
return [[self alloc] initWithURL:url
|
||||
delegate:delegate
|
||||
permission:permission
|
||||
fileFormat:fileFormat
|
||||
clientFormat:clientFormat];
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
#pragma mark - Class Methods
|
||||
+(NSArray *)supportedAudioFileTypes {
|
||||
return @[ @"aac",
|
||||
@"caf",
|
||||
@"aif",
|
||||
@"aiff",
|
||||
@"aifc",
|
||||
@"mp3",
|
||||
@"mp4",
|
||||
@"m4a",
|
||||
@"snd",
|
||||
@"au",
|
||||
@"sd2",
|
||||
@"wav" ];
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
+ (AudioStreamBasicDescription)defaultClientFormat
|
||||
{
|
||||
return [EZAudio stereoFloatInterleavedFormatWithSampleRate:44100];
|
||||
}
|
||||
|
||||
#pragma mark - Private Configuation
|
||||
-(void)_configureAudioFile {
|
||||
|
||||
// Source URL should not be nil
|
||||
NSAssert(_sourceURL,@"Source URL was not specified correctly.");
|
||||
|
||||
// Try to open the file for reading
|
||||
[EZAudio checkResult:ExtAudioFileOpenURL(_sourceURL,&_audioFile)
|
||||
operation:"Failed to open audio file for reading"];
|
||||
|
||||
// Try pulling the stream description
|
||||
UInt32 size = sizeof(_fileFormat);
|
||||
[EZAudio checkResult:ExtAudioFileGetProperty(_audioFile,kExtAudioFileProperty_FileDataFormat, &size, &_fileFormat)
|
||||
operation:"Failed to get audio stream basic description of input file"];
|
||||
|
||||
// Try pulling the total frame size
|
||||
size = sizeof(_totalFrames);
|
||||
[EZAudio checkResult:ExtAudioFileGetProperty(_audioFile,kExtAudioFileProperty_FileLengthFrames, &size, &_totalFrames)
|
||||
operation:"Failed to get total frames of input file"];
|
||||
|
||||
// Total duration
|
||||
_totalDuration = _totalFrames / _fileFormat.mSampleRate;
|
||||
|
||||
// Set the client format on the stream
|
||||
switch (_fileFormat.mChannelsPerFrame) {
|
||||
case 1:
|
||||
_clientFormat = [EZAudio monoFloatFormatWithSampleRate:_fileFormat.mSampleRate];
|
||||
break;
|
||||
case 2:
|
||||
_clientFormat = [EZAudio stereoFloatInterleavedFormatWithSampleRate:_fileFormat.mSampleRate];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
+ (NSArray *)supportedAudioFileTypes
|
||||
{
|
||||
return @[
|
||||
@"aac",
|
||||
@"caf",
|
||||
@"aif",
|
||||
@"aiff",
|
||||
@"aifc",
|
||||
@"mp3",
|
||||
@"mp4",
|
||||
@"m4a",
|
||||
@"snd",
|
||||
@"au",
|
||||
@"sd2",
|
||||
@"wav"
|
||||
];
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
#pragma mark - Setup
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
- (void)setup
|
||||
{
|
||||
// we open the file differently depending on the permissions specified
|
||||
[EZAudio checkResult:[self openAudioFile]
|
||||
operation:"Failed to create/open audio file"];
|
||||
|
||||
[EZAudio checkResult:ExtAudioFileSetProperty(_audioFile,
|
||||
kExtAudioFileProperty_ClientDataFormat,
|
||||
sizeof (AudioStreamBasicDescription),
|
||||
&_clientFormat)
|
||||
operation:"Couldn't set client data format on input ext file"];
|
||||
|
||||
// Allocate the float buffers
|
||||
_floatConverter = [[AEFloatConverter alloc] initWithSourceFormat:_clientFormat];
|
||||
_floatBuffers = (float**)malloc( sizeof(float*) * _clientFormat.mChannelsPerFrame );
|
||||
UInt32 outputBufferSize = 32 * 1024; // 32 KB
|
||||
for ( int i=0; i< _clientFormat.mChannelsPerFrame; i++ ) {
|
||||
_floatBuffers[i] = (float*)malloc(outputBufferSize);
|
||||
}
|
||||
|
||||
// There's no waveform data yet
|
||||
_waveformData = NULL;
|
||||
|
||||
// Set the default resolution for the waveform data
|
||||
_waveformResolution = kEZAudioFileWaveformDefaultResolution;
|
||||
|
||||
// set the client format
|
||||
self.clientFormat = self.info.clientFormat;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
#pragma mark - Creating/Opening Audio File
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
- (OSStatus)openAudioFile
|
||||
{
|
||||
// need a source url
|
||||
NSAssert(_info.sourceURL, @"EZAudioFile cannot be created without a source url!");
|
||||
|
||||
// determine if the file actually exists
|
||||
CFURLRef url = self.info.sourceURL;
|
||||
NSURL *fileURL = (__bridge NSURL *)(url);
|
||||
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:fileURL.path];
|
||||
|
||||
// create the file wrapper slightly differently depending what we are
|
||||
// trying to do with it
|
||||
OSStatus result = noErr;
|
||||
EZAudioFilePermission permission = self.info.permission;
|
||||
UInt32 propSize;
|
||||
if (fileExists)
|
||||
{
|
||||
result = AudioFileOpenURL(url,
|
||||
permission,
|
||||
0,
|
||||
&_info.audioFileID);
|
||||
[EZAudio checkResult:result
|
||||
operation:"failed to open audio file"];
|
||||
}
|
||||
else
|
||||
{
|
||||
// read permission is not applicable because the file does not exist
|
||||
if (permission == EZAudioFilePermissionRead)
|
||||
{
|
||||
result = EZAudioFileReadPermissionFileDoesNotExistCode;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = AudioFileCreateWithURL(url,
|
||||
0,
|
||||
&_info.fileFormat,
|
||||
kAudioFileFlags_EraseFile,
|
||||
&_info.audioFileID);
|
||||
}
|
||||
}
|
||||
|
||||
// get the ExtAudioFile wrapper
|
||||
if (result == noErr)
|
||||
{
|
||||
[EZAudio checkResult:ExtAudioFileWrapAudioFileID(self.info.audioFileID,
|
||||
false,
|
||||
&_info.extAudioFileRef)
|
||||
operation:"Failed to wrap audio file ID in ext audio file ref"];
|
||||
}
|
||||
|
||||
// store the file format if we opened an existing file
|
||||
if (fileExists)
|
||||
{
|
||||
propSize = sizeof(self.info.fileFormat);
|
||||
[EZAudio checkResult:ExtAudioFileGetProperty(self.info.extAudioFileRef,
|
||||
kExtAudioFileProperty_FileDataFormat,
|
||||
&propSize,
|
||||
&_info.fileFormat)
|
||||
operation:"Failed to get file audio format on existing audio file"];
|
||||
}
|
||||
|
||||
NSLog(@"file format......................");
|
||||
[EZAudio printASBD:self.info.fileFormat];
|
||||
NSLog(@"client format....................");
|
||||
[EZAudio printASBD:self.info.clientFormat];
|
||||
|
||||
// done
|
||||
return result;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
#pragma mark - Events
|
||||
-(void)readFrames:(UInt32)frames
|
||||
audioBufferList:(AudioBufferList *)audioBufferList
|
||||
bufferSize:(UInt32 *)bufferSize
|
||||
eof:(BOOL *)eof {
|
||||
@autoreleasepool {
|
||||
// Setup the buffers
|
||||
UInt32 outputBufferSize = 32 * frames; // 32 KB
|
||||
audioBufferList->mNumberBuffers = 1;
|
||||
audioBufferList->mBuffers[0].mNumberChannels = _clientFormat.mChannelsPerFrame;
|
||||
audioBufferList->mBuffers[0].mDataByteSize = _clientFormat.mChannelsPerFrame*outputBufferSize;
|
||||
audioBufferList->mBuffers[0].mData = (AudioUnitSampleType*)malloc(_clientFormat.mChannelsPerFrame*sizeof(AudioUnitSampleType*)*outputBufferSize);
|
||||
[EZAudio checkResult:ExtAudioFileRead(_audioFile,
|
||||
&frames,
|
||||
audioBufferList)
|
||||
operation:"Failed to read audio data from audio file"];
|
||||
*bufferSize = audioBufferList->mBuffers[0].mDataByteSize/sizeof(AudioUnitSampleType);
|
||||
*eof = frames == 0;
|
||||
_frameIndex += frames;
|
||||
if( self.audioFileDelegate ){
|
||||
if( [self.audioFileDelegate respondsToSelector:@selector(audioFile:updatedPosition:)] ){
|
||||
[self.audioFileDelegate audioFile:self
|
||||
updatedPosition:_frameIndex];
|
||||
}
|
||||
if( [self.audioFileDelegate respondsToSelector:@selector(audioFile:readAudio:withBufferSize:withNumberOfChannels:)] ){
|
||||
AEFloatConverterToFloat(_floatConverter,audioBufferList,_floatBuffers,frames);
|
||||
[self.audioFileDelegate audioFile:self
|
||||
readAudio:_floatBuffers
|
||||
withBufferSize:frames
|
||||
withNumberOfChannels:_clientFormat.mChannelsPerFrame];
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
- (void)readFrames:(UInt32)frames
|
||||
audioBufferList:(AudioBufferList *)audioBufferList
|
||||
bufferSize:(UInt32 *)bufferSize
|
||||
eof:(BOOL *)eof
|
||||
{
|
||||
if (pthread_mutex_trylock(&_lock) == 0)
|
||||
{
|
||||
// perform read
|
||||
[EZAudio checkResult:ExtAudioFileRead(self.info.extAudioFileRef,
|
||||
&frames,
|
||||
audioBufferList)
|
||||
operation:"Failed to read audio data from file"];
|
||||
*bufferSize = frames;
|
||||
*eof = frames == 0;
|
||||
pthread_mutex_unlock(&_lock);
|
||||
|
||||
// notify delegate
|
||||
if ([self.delegate respondsToSelector:@selector(audioFile:updatedPosition:)])
|
||||
{
|
||||
[self.delegate audioFile:self
|
||||
updatedPosition:self.frameIndex];
|
||||
}
|
||||
if ([self.delegate respondsToSelector:@selector(audioFile:readAudio:withBufferSize:withNumberOfChannels:)])
|
||||
{
|
||||
[self.delegate audioFile:self
|
||||
readAudio:nil
|
||||
withBufferSize:*bufferSize
|
||||
withNumberOfChannels:self.info.clientFormat.mChannelsPerFrame];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-(void)seekToFrame:(SInt64)frame {
|
||||
[EZAudio checkResult:ExtAudioFileSeek(_audioFile,frame)
|
||||
operation:"Failed to seek frame position within audio file"];
|
||||
_frameIndex = frame;
|
||||
if( self.audioFileDelegate ){
|
||||
if( [self.audioFileDelegate respondsToSelector:@selector(audioFile:updatedPosition:)] ){
|
||||
[self.audioFileDelegate audioFile:self updatedPosition:_frameIndex];
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
- (void)seekToFrame:(SInt64)frame
|
||||
{
|
||||
if (pthread_mutex_trylock(&_lock) == 0)
|
||||
{
|
||||
[EZAudio checkResult:ExtAudioFileSeek(self.info.extAudioFileRef,
|
||||
frame)
|
||||
operation:"Failed to seek frame position within audio file"];
|
||||
pthread_mutex_unlock(&_lock);
|
||||
|
||||
// notify delegate
|
||||
if ([self.delegate respondsToSelector:@selector(audioFile:updatedPosition:)])
|
||||
{
|
||||
[self.delegate audioFile:self
|
||||
updatedPosition:self.frameIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
#pragma mark - Getters
|
||||
-(BOOL)hasLoadedAudioData {
|
||||
return _waveformData != NULL;
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
- (EZAudioWaveformData *)getWaveformData
|
||||
{
|
||||
return [self getWaveformDataWithNumberOfPoints:EZAudioFileWaveformDefaultResolution];
|
||||
}
|
||||
|
||||
-(void)getWaveformDataWithCompletionBlock:(WaveformDataCompletionBlock)waveformDataCompletionBlock {
|
||||
|
||||
SInt64 currentFramePosition = _frameIndex;
|
||||
|
||||
if( _waveformData != NULL ){
|
||||
|
||||
waveformDataCompletionBlock( _waveformData, _waveformTotalBuffers );
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
_waveformFrameRate = [self recommendedDrawingFrameRate];
|
||||
_waveformTotalBuffers = [self minBuffersWithFrameRate:_waveformFrameRate];
|
||||
_waveformData = (float*)malloc(sizeof(float)*_waveformTotalBuffers);
|
||||
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0ul), ^{
|
||||
|
||||
for( int i = 0; i < _waveformTotalBuffers; i++ ){
|
||||
|
||||
// Take a snapshot of each buffer through the audio file to form the waveform
|
||||
AudioBufferList *bufferList = [EZAudio audioBufferList];
|
||||
UInt32 bufferSize;
|
||||
BOOL eof;
|
||||
|
||||
// Setup the buffers
|
||||
UInt32 outputBufferSize = 32 * _waveformFrameRate; // 32 KB
|
||||
bufferList->mNumberBuffers = 1;
|
||||
bufferList->mBuffers[0].mNumberChannels = _clientFormat.mChannelsPerFrame;
|
||||
bufferList->mBuffers[0].mDataByteSize = _clientFormat.mChannelsPerFrame*outputBufferSize;
|
||||
bufferList->mBuffers[0].mData = (AudioUnitSampleType*)malloc(_clientFormat.mChannelsPerFrame*sizeof(AudioUnitSampleType*)*outputBufferSize);
|
||||
|
||||
// Read in the specified number of frames
|
||||
[EZAudio checkResult:ExtAudioFileRead(_audioFile,
|
||||
&_waveformFrameRate,
|
||||
bufferList)
|
||||
operation:"Failed to read audio data from audio file"];
|
||||
bufferSize = bufferList->mBuffers[0].mDataByteSize/sizeof(AudioUnitSampleType);
|
||||
eof = _waveformFrameRate == 0;
|
||||
_frameIndex += _waveformFrameRate;
|
||||
|
||||
// Calculate RMS of each buffer
|
||||
float rms = [EZAudio RMS:bufferList->mBuffers[0].mData
|
||||
length:bufferSize];
|
||||
_waveformData[i] = rms;
|
||||
|
||||
// Since we malloc'ed, we should cleanup
|
||||
[EZAudio freeBufferList:bufferList];
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
- (EZAudioWaveformData *)getWaveformDataWithNumberOfPoints:(UInt32)numberOfPoints
|
||||
{
|
||||
EZAudioWaveformData *waveformData;
|
||||
if (pthread_mutex_trylock(&_lock) == 0)
|
||||
{
|
||||
// store current frame
|
||||
SInt64 currentFrame = self.frameIndex;
|
||||
UInt32 channels = self.clientFormat.mChannelsPerFrame;
|
||||
BOOL interleaved = [EZAudio isInterleaved:self.clientFormat];
|
||||
SInt64 totalFrames = self.totalClientFrames;
|
||||
SInt64 framesPerBuffer = ((SInt64) totalFrames / numberOfPoints);
|
||||
SInt64 framesPerChannel = framesPerBuffer / channels;
|
||||
float **data = (float **)malloc( sizeof(float*) * channels );
|
||||
for (int i = 0; i < channels; i++)
|
||||
{
|
||||
data[i] = (float *)malloc( sizeof(float) * numberOfPoints );
|
||||
}
|
||||
|
||||
// seek to 0
|
||||
[EZAudio checkResult:ExtAudioFileSeek(self.info.extAudioFileRef,
|
||||
0)
|
||||
operation:"Failed to seek frame position within audio file"];
|
||||
|
||||
// allocate an audio buffer list
|
||||
AudioBufferList *audioBufferList = [EZAudio audioBufferListWithNumberOfFrames:(UInt32)totalFrames
|
||||
numberOfChannels:self.info.clientFormat.mChannelsPerFrame
|
||||
interleaved:interleaved];
|
||||
|
||||
UInt32 bufferSize = (UInt32)totalFrames;
|
||||
[EZAudio checkResult:ExtAudioFileRead(self.info.extAudioFileRef,
|
||||
&bufferSize,
|
||||
audioBufferList)
|
||||
operation:"Failed to read audio data from file waveform"];
|
||||
|
||||
// read through file and calculate rms at each point
|
||||
SInt64 offset = 0;
|
||||
for (SInt64 i = 0; i < numberOfPoints; i++)
|
||||
{
|
||||
float buffer[framesPerBuffer];
|
||||
if (interleaved)
|
||||
{
|
||||
float *samples = (float *)audioBufferList->mBuffers[0].mData;
|
||||
memcpy(buffer, &samples[offset], framesPerBuffer * sizeof(float));
|
||||
for (int channel = 0; channel < channels; channel++)
|
||||
{
|
||||
float channelData[framesPerChannel];
|
||||
for (int frame = 0; frame < framesPerChannel; frame++)
|
||||
{
|
||||
channelData[frame] = buffer[frame * channels + channel];
|
||||
}
|
||||
float rms = [EZAudio RMS:channelData length:(UInt32)framesPerChannel];
|
||||
data[channel][i] = rms;
|
||||
}
|
||||
offset += channels * framesPerBuffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int channel = 0; channel < channels; channel++)
|
||||
{
|
||||
float *samples = (float *)audioBufferList->mBuffers[channel].mData;
|
||||
memcpy(buffer, &samples[offset], framesPerBuffer * sizeof(float));
|
||||
float rms = [EZAudio RMS:buffer length:(UInt32)framesPerBuffer];
|
||||
data[channel][i] = rms;
|
||||
}
|
||||
offset += framesPerBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
// clean up
|
||||
[EZAudio freeBufferList:audioBufferList];
|
||||
|
||||
// seek back to previous position
|
||||
[EZAudio checkResult:ExtAudioFileSeek(self.info.extAudioFileRef,
|
||||
currentFrame)
|
||||
operation:"Failed to seek frame position within audio file"];
|
||||
|
||||
pthread_mutex_unlock(&_lock);
|
||||
|
||||
waveformData = [EZAudioWaveformData dataWithNumberOfChannels:channels
|
||||
buffers:(float **)data
|
||||
bufferSize:numberOfPoints];
|
||||
|
||||
// cleanup
|
||||
for (int i = 0; i < channels; i++)
|
||||
{
|
||||
free(data[i]);
|
||||
}
|
||||
free(data);
|
||||
}
|
||||
|
||||
// Seek the audio file back to the beginning
|
||||
[EZAudio checkResult:ExtAudioFileSeek(_audioFile,currentFramePosition)
|
||||
operation:"Failed to seek frame position within audio file"];
|
||||
_frameIndex = currentFramePosition;
|
||||
|
||||
// Once we're done send off the waveform data
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
waveformDataCompletionBlock( _waveformData, _waveformTotalBuffers );
|
||||
return waveformData;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
- (void)getWaveformDataWithCompletionBlock:(WaveformDataCompletionBlock)waveformDataCompletionBlock
|
||||
{
|
||||
[self getWaveformDataWithNumberOfPoints:EZAudioFileWaveformDefaultResolution
|
||||
completion:waveformDataCompletionBlock];
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
- (void)getWaveformDataWithNumberOfPoints:(UInt32)numberOfPoints
|
||||
completion:(WaveformDataCompletionBlock)completion
|
||||
{
|
||||
if (!completion)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// async get waveform data
|
||||
dispatch_async(self.waveformQueue, ^{
|
||||
EZAudioWaveformData *waveformData = [self getWaveformDataWithNumberOfPoints:numberOfPoints];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
completion(waveformData);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
-(AudioStreamBasicDescription)clientFormat {
|
||||
return _clientFormat;
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
- (AudioStreamBasicDescription)clientFormat
|
||||
{
|
||||
return self.info.clientFormat;
|
||||
}
|
||||
|
||||
-(AudioStreamBasicDescription)fileFormat {
|
||||
return _fileFormat;
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
- (AudioStreamBasicDescription)fileFormat
|
||||
{
|
||||
return self.info.fileFormat;
|
||||
}
|
||||
|
||||
-(SInt64)frameIndex {
|
||||
return _frameIndex;
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
- (SInt64)frameIndex
|
||||
{
|
||||
SInt64 frameIndex;
|
||||
[EZAudio checkResult:ExtAudioFileTell(self.info.extAudioFileRef, &frameIndex)
|
||||
operation:"Failed to get frame index"];
|
||||
return frameIndex;
|
||||
}
|
||||
|
||||
-(Float32)totalDuration {
|
||||
return _totalDuration;
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
- (NSDictionary *)metadata
|
||||
{
|
||||
// get size of metadata property (dictionary)
|
||||
UInt32 propSize = sizeof(_info.audioFileID);
|
||||
CFDictionaryRef metadata;
|
||||
UInt32 writable;
|
||||
[EZAudio checkResult:AudioFileGetPropertyInfo(self.info.audioFileID,
|
||||
kAudioFilePropertyInfoDictionary,
|
||||
&propSize,
|
||||
&writable)
|
||||
operation:"Failed to get the size of the metadata dictionary"];
|
||||
|
||||
// pull metadata
|
||||
[EZAudio checkResult:AudioFileGetProperty(self.info.audioFileID,
|
||||
kAudioFilePropertyInfoDictionary,
|
||||
&propSize,
|
||||
&metadata)
|
||||
operation:"Failed to get metadata dictionary"];
|
||||
|
||||
// cast to NSDictionary
|
||||
return (__bridge NSDictionary*)metadata;
|
||||
}
|
||||
|
||||
-(SInt64)totalFrames {
|
||||
return _totalFrames;
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
- (NSTimeInterval)totalDuration
|
||||
{
|
||||
SInt64 totalFrames = [self totalFrames];
|
||||
return (NSTimeInterval) totalFrames / self.info.fileFormat.mSampleRate;
|
||||
}
|
||||
|
||||
-(NSURL *)url {
|
||||
return (__bridge NSURL*)_sourceURL;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#pragma mark - Setters
|
||||
-(void)setWaveformResolution:(UInt32)waveformResolution {
|
||||
if( _waveformResolution != waveformResolution ){
|
||||
_waveformResolution = waveformResolution;
|
||||
if( _waveformData ){
|
||||
free(_waveformData);
|
||||
_waveformData = NULL;
|
||||
- (SInt64)totalClientFrames
|
||||
{
|
||||
SInt64 totalFrames = [self totalFrames];
|
||||
|
||||
// check sample rate of client vs file format
|
||||
AudioStreamBasicDescription clientFormat = self.info.clientFormat;
|
||||
AudioStreamBasicDescription fileFormat = self.info.fileFormat;
|
||||
BOOL sameSampleRate = clientFormat.mSampleRate == fileFormat.mSampleRate;
|
||||
if (!sameSampleRate)
|
||||
{
|
||||
NSTimeInterval duration = [self totalDuration];
|
||||
totalFrames = duration * clientFormat.mSampleRate;
|
||||
}
|
||||
}
|
||||
|
||||
return totalFrames;
|
||||
}
|
||||
|
||||
#pragma mark - Helpers
|
||||
-(UInt32)minBuffersWithFrameRate:(UInt32)frameRate {
|
||||
frameRate = frameRate > 0 ? frameRate : 1;
|
||||
return (UInt32) _totalFrames / frameRate + 1;
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
- (SInt64)totalFrames
|
||||
{
|
||||
SInt64 totalFrames;
|
||||
UInt32 size = sizeof(SInt64);
|
||||
[EZAudio checkResult:ExtAudioFileGetProperty(self.info.extAudioFileRef,
|
||||
kExtAudioFileProperty_FileLengthFrames,
|
||||
&size,
|
||||
&totalFrames)
|
||||
operation:"Failed to get total frames"];
|
||||
return totalFrames;
|
||||
}
|
||||
|
||||
-(UInt32)recommendedDrawingFrameRate {
|
||||
return (UInt32) _totalFrames / _waveformResolution - 1;
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
- (NSURL*)url
|
||||
{
|
||||
return (__bridge NSURL*)self.info.sourceURL;
|
||||
}
|
||||
|
||||
#pragma mark - Cleanup
|
||||
-(void)dealloc {
|
||||
if( _waveformData ){
|
||||
free(_waveformData);
|
||||
_waveformData = NULL;
|
||||
}
|
||||
// if( _floatBuffers ){
|
||||
// free(_floatBuffers);
|
||||
// _floatBuffers = NULL;
|
||||
// }
|
||||
_frameIndex = 0;
|
||||
_waveformFrameRate = 0;
|
||||
_waveformTotalBuffers = 0;
|
||||
[EZAudio checkResult:ExtAudioFileDispose(_audioFile)
|
||||
operation:"Failed to dispose of extended audio file."];
|
||||
//------------------------------------------------------------------------------
|
||||
#pragma mark - Setters
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
- (void)setClientFormat:(AudioStreamBasicDescription)clientFormat
|
||||
{
|
||||
NSAssert([EZAudio isLinearPCM:clientFormat], @"Client format must be linear PCM");
|
||||
|
||||
// store the client format
|
||||
_info.clientFormat = clientFormat;
|
||||
|
||||
// set the client format on the extended audio file ref
|
||||
[EZAudio checkResult:ExtAudioFileSetProperty(self.info.extAudioFileRef,
|
||||
kExtAudioFileProperty_ClientDataFormat,
|
||||
sizeof(clientFormat),
|
||||
&clientFormat)
|
||||
operation:"Couldn't set client data format on file"];
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
-(void)dealloc
|
||||
{
|
||||
pthread_mutex_destroy(&_lock);
|
||||
[EZAudio checkResult:AudioFileClose(self.info.audioFileID) operation:"Failed to close audio file"];
|
||||
[EZAudio checkResult:ExtAudioFileDispose(self.info.extAudioFileRef) operation:"Failed to dispose of ext audio file"];
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,300 @@
|
||||
//
|
||||
// EZAudioPlayer.h
|
||||
// EZAudio
|
||||
//
|
||||
// Created by Syed Haris Ali on 1/16/14.
|
||||
// Copyright (c) 2014 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 <Foundation/Foundation.h>
|
||||
#import "TargetConditionals.h"
|
||||
|
||||
#import "EZAudio.h"
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#elif TARGET_OS_MAC
|
||||
#endif
|
||||
|
||||
@class EZAudioPlayer;
|
||||
|
||||
/**
|
||||
The EZAudioPlayerDelegate provides event callbacks for the EZAudioPlayer. These type of events are triggered by changes in the EZAudioPlayer's state and allow someone implementing the EZAudioPlayer to more easily update their user interface. Events are triggered anytime the EZAudioPlayer resumes/pauses playback, reaches the end of the file, reads audio data and converts it to float data visualizations (using the EZAudioFile), and updates its cursor position within the audio file during playback (use this for the play position on a slider on the user interface).
|
||||
@warning These callbacks don't necessarily occur on the main thread so make sure you wrap any UI code in a GCD block like: dispatch_async(dispatch_get_main_queue(), ^{ // Update UI });
|
||||
*/
|
||||
@protocol EZAudioPlayerDelegate <NSObject>
|
||||
|
||||
@optional
|
||||
/**
|
||||
Triggered by the EZAudioPlayer when the playback has been resumed or started.
|
||||
@param audioPlayer The instance of the EZAudioPlayer that triggered the event
|
||||
@param audioFile The instance of the EZAudioFile that the event was triggered from
|
||||
*/
|
||||
-(void)audioPlayer:(EZAudioPlayer*)audioPlayer didResumePlaybackOnAudioFile:(EZAudioFile*)audioFile;
|
||||
|
||||
/**
|
||||
Triggered by the EZAudioPlayer when the playback has been paused.
|
||||
@param audioPlayer The instance of the EZAudioPlayer that triggered the event
|
||||
@param audioFile The instance of the EZAudioFile that the event was triggered from
|
||||
*/
|
||||
-(void)audioPlayer:(EZAudioPlayer*)audioPlayer didPausePlaybackOnAudioFile:(EZAudioFile*)audioFile;
|
||||
|
||||
/**
|
||||
Triggered by the EZAudioPlayer when the output has reached the end of the EZAudioFile it's playing. If the EZAudioPlayer has its `shouldLoop` property set to true this will trigger, but playback will continue to loop once its hit the end of the audio file.
|
||||
@param audioPlayer The instance of the EZAudioPlayer that triggered the event
|
||||
@param audioFile The instance of the EZAudioFile that the event was triggered from
|
||||
*/
|
||||
-(void)audioPlayer:(EZAudioPlayer*)audioPlayer reachedEndOfAudioFile:(EZAudioFile*)audioFile;
|
||||
|
||||
/**
|
||||
Triggered by the EZAudioPlayer's internal EZAudioFile's EZAudioFileDelegate callback and notifies the delegate of the read audio data as a float array instead of a buffer list. Common use case of this would be to visualize the float data using an audio plot or audio data dependent OpenGL sketch.
|
||||
@param audioPlayer The instance of the EZAudioPlayer that triggered the event
|
||||
@param buffer A float array of float arrays holding the audio data. buffer[0] would be the left channel's float array while buffer[1] would be the right channel's float array in a stereo file.
|
||||
@param bufferSize The length of the buffers float arrays
|
||||
@param numberOfChannels The number of channels. 2 for stereo, 1 for mono.
|
||||
@param audioFile The instance of the EZAudioFile that the event was triggered from
|
||||
*/
|
||||
-(void) audioPlayer:(EZAudioPlayer*)audioPlayer
|
||||
readAudio:(float**)buffer
|
||||
withBufferSize:(UInt32)bufferSize
|
||||
withNumberOfChannels:(UInt32)numberOfChannels
|
||||
inAudioFile:(EZAudioFile*)audioFile;;
|
||||
|
||||
/**
|
||||
Triggered by EZAudioPlayer's internal EZAudioFile's EZAudioFileDelegate callback and notifies the delegate of the current playback position. The framePosition provides the current frame position and can be calculated against the EZAudioPlayer's total frames using the `totalFrames` function from the EZAudioPlayer.
|
||||
@param audioPlayer The instance of the EZAudioPlayer that triggered the event
|
||||
@param framePosition The new frame index as a 64-bit signed integer
|
||||
@param audioFile The instance of the EZAudioFile that the event was triggered from
|
||||
*/
|
||||
-(void)audioPlayer:(EZAudioPlayer*)audioPlayer
|
||||
updatedPosition:(SInt64)framePosition
|
||||
inAudioFile:(EZAudioFile*)audioFile;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
The EZAudioPlayer acts as the master delegate (the EZAudioFileDelegate) over whatever EZAudioFile it is using for playback. Classes that want to get the EZAudioFileDelegate callbacks should implement the EZAudioPlayer's EZAudioPlayerDelegate on the EZAudioPlayer instance.
|
||||
*/
|
||||
@interface EZAudioPlayer : NSObject
|
||||
|
||||
#pragma mark - Properties
|
||||
///-----------------------------------------------------------
|
||||
/// @name Properties
|
||||
///-----------------------------------------------------------
|
||||
|
||||
/**
|
||||
The EZAudioPlayerDelegate that will handle the audio player callbacks
|
||||
*/
|
||||
@property (nonatomic,assign) id<EZAudioPlayerDelegate> audioPlayerDelegate;
|
||||
|
||||
/**
|
||||
A BOOL indicating whether the player should loop the file
|
||||
*/
|
||||
@property (nonatomic,assign) BOOL shouldLoop;
|
||||
|
||||
#pragma mark - Initializers
|
||||
///-----------------------------------------------------------
|
||||
/// @name Initializers
|
||||
///-----------------------------------------------------------
|
||||
|
||||
/**
|
||||
Initializes the EZAudioPlayer with an EZAudioFile instance. This does not use the EZAudioFile by reference, but instead creates a separate EZAudioFile instance with the same file at the given file path provided by the internal NSURL to use for internal seeking so it doesn't cause any locking between the caller's instance of the EZAudioFile.
|
||||
@param audioFile The instance of the EZAudioFile to use for initializing the EZAudioPlayer
|
||||
@return The newly created instance of the EZAudioPlayer
|
||||
*/
|
||||
-(EZAudioPlayer*)initWithEZAudioFile:(EZAudioFile*)audioFile;
|
||||
|
||||
/**
|
||||
Initializes the EZAudioPlayer with an EZAudioFile instance and provides a way to assign the EZAudioPlayerDelegate on instantiation. This does not use the EZAudioFile by reference, but instead creates a separate EZAudioFile instance with the same file at the given file path provided by the internal NSURL to use for internal seeking so it doesn't cause any locking between the caller's instance of the EZAudioFile.
|
||||
@param audioFile The instance of the EZAudioFile to use for initializing the EZAudioPlayer
|
||||
@param audioPlayerDelegate The receiver that will act as the EZAudioPlayerDelegate. Set to nil if it should have no delegate or use the initWithEZAudioFile: function instead.
|
||||
@return The newly created instance of the EZAudioPlayer
|
||||
*/
|
||||
-(EZAudioPlayer*)initWithEZAudioFile:(EZAudioFile*)audioFile
|
||||
withDelegate:(id<EZAudioPlayerDelegate>)audioPlayerDelegate;
|
||||
|
||||
/**
|
||||
Initializes the EZAudioPlayer with an NSURL instance representing the file path of the audio file.
|
||||
@param url The NSURL instance representing the file path of the audio file.
|
||||
@return The newly created instance of the EZAudioPlayer
|
||||
*/
|
||||
-(EZAudioPlayer*)initWithURL:(NSURL*)url;
|
||||
|
||||
/**
|
||||
Initializes the EZAudioPlayer with an NSURL instance representing the file path of the audio file and a caller to assign as the EZAudioPlayerDelegate on instantiation.
|
||||
@param url The NSURL instance representing the file path of the audio file.
|
||||
@param audioPlayerDelegate The receiver that will act as the EZAudioPlayerDelegate. Set to nil if it should have no delegate or use the initWithEZAudioFile: function instead.
|
||||
@return The newly created instance of the EZAudioPlayer
|
||||
*/
|
||||
-(EZAudioPlayer*)initWithURL:(NSURL*)url
|
||||
withDelegate:(id<EZAudioPlayerDelegate>)audioPlayerDelegate;
|
||||
|
||||
|
||||
#pragma mark - Class Initializers
|
||||
///-----------------------------------------------------------
|
||||
/// @name Class Initializers
|
||||
///-----------------------------------------------------------
|
||||
|
||||
/**
|
||||
Class initializer that initializes the EZAudioPlayer with an EZAudioFile instance. This does not use the EZAudioFile by reference, but instead creates a separate EZAudioFile instance with the same file at the given file path provided by the internal NSURL to use for internal seeking so it doesn't cause any locking between the caller's instance of the EZAudioFile.
|
||||
@param audioFile The instance of the EZAudioFile to use for initializing the EZAudioPlayer
|
||||
@return The newly created instance of the EZAudioPlayer
|
||||
*/
|
||||
+(EZAudioPlayer*)audioPlayerWithEZAudioFile:(EZAudioFile*)audioFile;
|
||||
|
||||
/**
|
||||
Class initializer that initializes the EZAudioPlayer with an EZAudioFile instance and provides a way to assign the EZAudioPlayerDelegate on instantiation. This does not use the EZAudioFile by reference, but instead creates a separate EZAudioFile instance with the same file at the given file path provided by the internal NSURL to use for internal seeking so it doesn't cause any locking between the caller's instance of the EZAudioFile.
|
||||
@param audioFile The instance of the EZAudioFile to use for initializing the EZAudioPlayer
|
||||
@param audioPlayerDelegate The receiver that will act as the EZAudioPlayerDelegate. Set to nil if it should have no delegate or use the audioPlayerWithEZAudioFile: function instead.
|
||||
@return The newly created instance of the EZAudioPlayer
|
||||
*/
|
||||
+(EZAudioPlayer*)audioPlayerWithEZAudioFile:(EZAudioFile*)audioFile
|
||||
withDelegate:(id<EZAudioPlayerDelegate>)audioPlayerDelegate;
|
||||
|
||||
/**
|
||||
Class initializer that initializes the EZAudioPlayer with an NSURL instance representing the file path of the audio file.
|
||||
@param url The NSURL instance representing the file path of the audio file.
|
||||
@return The newly created instance of the EZAudioPlayer
|
||||
*/
|
||||
+(EZAudioPlayer*)audioPlayerWithURL:(NSURL*)url;
|
||||
|
||||
/**
|
||||
Class initializer that initializes the EZAudioPlayer with an NSURL instance representing the file path of the audio file and a caller to assign as the EZAudioPlayerDelegate on instantiation.
|
||||
@param url The NSURL instance representing the file path of the audio file.
|
||||
@param audioPlayerDelegate The receiver that will act as the EZAudioPlayerDelegate. Set to nil if it should have no delegate or use the audioPlayerWithURL: function instead.
|
||||
@return The newly created instance of the EZAudioPlayer
|
||||
*/
|
||||
+(EZAudioPlayer*)audioPlayerWithURL:(NSURL*)url
|
||||
withDelegate:(id<EZAudioPlayerDelegate>)audioPlayerDelegate;
|
||||
|
||||
#pragma mark - Singleton
|
||||
///-----------------------------------------------------------
|
||||
/// @name Shared Instance
|
||||
///-----------------------------------------------------------
|
||||
|
||||
/**
|
||||
The shared instance (singleton) of the audio player. Most applications will only have one instance of the EZAudioPlayer that can be reused with multiple different audio files.
|
||||
* @return The shared instance of the EZAudioPlayer.
|
||||
*/
|
||||
+(EZAudioPlayer*)sharedAudioPlayer;
|
||||
|
||||
#pragma mark - Getters
|
||||
///-----------------------------------------------------------
|
||||
/// @name Getting The Audio Player's Properties
|
||||
///-----------------------------------------------------------
|
||||
|
||||
/**
|
||||
Provides the EZAudioFile instance that is being used as the datasource for playback.
|
||||
@return The EZAudioFile instance that is currently being used for playback.
|
||||
*/
|
||||
-(EZAudioFile*)audioFile;
|
||||
|
||||
/**
|
||||
Provides the current time (a.k.a. the seek position) in seconds within the audio file that's being used for playback. This can be helpful when displaying the audio player's current time over duration.
|
||||
@return A float representing the current time within the audio file used for playback.
|
||||
*/
|
||||
-(float)currentTime;
|
||||
|
||||
/**
|
||||
Provides a flag indicating whether the EZAudioPlayer has reached the end of the audio file used for playback.
|
||||
@return A BOOL indicating whether or not the EZAudioPlayer has reached the end of the file it is using for playback.
|
||||
*/
|
||||
-(BOOL)endOfFile;
|
||||
|
||||
/**
|
||||
Provides the frame index (a.k.a the seek positon) within the audio file being used for playback. This can be helpful when seeking through the audio file.
|
||||
@return An SInt64 representing the current frame index within the audio file used for playback.
|
||||
*/
|
||||
-(SInt64)frameIndex;
|
||||
|
||||
/**
|
||||
Provides a flag indicating whether the EZAudioPlayer is currently playing back any audio.
|
||||
@return A BOOL indicating whether or not the EZAudioPlayer is performing playback,
|
||||
*/
|
||||
-(BOOL)isPlaying;
|
||||
|
||||
/**
|
||||
Provides the EZOutput instance that is being used to provide playback to the system output.
|
||||
@return The EZOutput instance that is currently being used for output playback.
|
||||
*/
|
||||
-(EZOutput*)output;
|
||||
|
||||
/**
|
||||
Provides the total duration of the current audio file being used for playback (in seconds).
|
||||
@return A float representing the total duration of the current audio file being used for playback in seconds.
|
||||
*/
|
||||
-(float)totalDuration;
|
||||
|
||||
/**
|
||||
Provides the total amount of frames in the current audio file being used for playback.
|
||||
@return A SInt64 representing the total amount of frames in the current audio file being used for playback.
|
||||
*/
|
||||
-(SInt64)totalFrames;
|
||||
|
||||
/**
|
||||
Provides the file path that's currently being used by the player for playback.
|
||||
@return The NSURL representing the file path of the audio file being used for playback.
|
||||
*/
|
||||
-(NSURL*)url;
|
||||
|
||||
#pragma mark - Setters
|
||||
///-----------------------------------------------------------
|
||||
/// @name Setting The File/Output
|
||||
///-----------------------------------------------------------
|
||||
|
||||
/**
|
||||
Sets the EZAudioFile to use for playback. This does not use the EZAudioFile by reference, but instead creates a separate EZAudioFile instance with the same file at the given file path provided by the internal NSURL to use for internal seeking so it doesn't cause any locking between the caller's instance of the EZAudioFile.
|
||||
@param audioFile The new EZAudioFile instance that should be used for playback
|
||||
*/
|
||||
-(void)setAudioFile:(EZAudioFile*)audioFile;
|
||||
|
||||
/**
|
||||
Sets the EZOutput to route playback. By default this uses the [EZOutput sharedOutput] singleton.
|
||||
@param output The new EZOutput instance that should be used for playback
|
||||
*/
|
||||
-(void)setOutput:(EZOutput*)output;
|
||||
|
||||
#pragma mark - Methods
|
||||
///-----------------------------------------------------------
|
||||
/// @name Play/Pause/Seeking the Player
|
||||
///-----------------------------------------------------------
|
||||
|
||||
/**
|
||||
Starts or resumes playback.
|
||||
*/
|
||||
-(void)play;
|
||||
|
||||
/**
|
||||
Pauses playback.
|
||||
*/
|
||||
-(void)pause;
|
||||
|
||||
/**
|
||||
Stops playback.
|
||||
*/
|
||||
-(void)stop;
|
||||
|
||||
/**
|
||||
Seeks playback to a specified frame within the internal EZAudioFile. This will notify the EZAudioFileDelegate (if specified) with the audioPlayer:updatedPosition:inAudioFile: function.
|
||||
@param frame The new frame position to seek to as a SInt64.
|
||||
*/
|
||||
-(void)seekToFrame:(SInt64)frame;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,296 @@
|
||||
//
|
||||
// EZAudioPlayer.m
|
||||
// EZAudio
|
||||
//
|
||||
// Created by Syed Haris Ali on 1/16/14.
|
||||
// Copyright (c) 2014 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 "EZAudioPlayer.h"
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
#elif TARGET_OS_MAC
|
||||
#endif
|
||||
@interface EZAudioPlayer () <EZAudioFileDelegate,EZOutputDataSource>
|
||||
{
|
||||
BOOL _eof;
|
||||
}
|
||||
@property (nonatomic,strong,setter=setAudioFile:) EZAudioFile *audioFile;
|
||||
@property (nonatomic,strong,setter=setOutput:) EZOutput *output;
|
||||
@end
|
||||
|
||||
@implementation EZAudioPlayer
|
||||
@synthesize audioFile = _audioFile;
|
||||
@synthesize audioPlayerDelegate = _audioPlayerDelegate;
|
||||
@synthesize output = _output;
|
||||
@synthesize shouldLoop = _shouldLoop;
|
||||
|
||||
#pragma mark - Initializers
|
||||
-(id)init {
|
||||
self = [super init];
|
||||
if(self){
|
||||
[self _configureAudioPlayer];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
-(EZAudioPlayer*)initWithEZAudioFile:(EZAudioFile *)audioFile {
|
||||
return [self initWithEZAudioFile:audioFile withDelegate:nil];
|
||||
}
|
||||
|
||||
-(EZAudioPlayer *)initWithEZAudioFile:(EZAudioFile *)audioFile
|
||||
withDelegate:(id<EZAudioPlayerDelegate>)audioPlayerDelegate {
|
||||
self = [super init];
|
||||
if(self){
|
||||
// This should make a separate reference to the audio file
|
||||
[self _configureAudioPlayer];
|
||||
self.audioFile = audioFile;
|
||||
self.audioPlayerDelegate = audioPlayerDelegate;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
-(EZAudioPlayer *)initWithURL:(NSURL *)url {
|
||||
return [self initWithURL:url withDelegate:nil];
|
||||
}
|
||||
|
||||
-(EZAudioPlayer *)initWithURL:(NSURL *)url
|
||||
withDelegate:(id<EZAudioPlayerDelegate>)audioPlayerDelegate {
|
||||
self = [super init];
|
||||
if(self){
|
||||
[self _configureAudioPlayer];
|
||||
self.audioFile = [EZAudioFile audioFileWithURL:url andDelegate:self];
|
||||
self.audioPlayerDelegate = audioPlayerDelegate;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Class Initializers
|
||||
+(EZAudioPlayer *)audioPlayerWithEZAudioFile:(EZAudioFile *)audioFile {
|
||||
return [[EZAudioPlayer alloc] initWithEZAudioFile:audioFile];
|
||||
}
|
||||
|
||||
+(EZAudioPlayer *)audioPlayerWithEZAudioFile:(EZAudioFile *)audioFile
|
||||
withDelegate:(id<EZAudioPlayerDelegate>)audioPlayerDelegate {
|
||||
return [[EZAudioPlayer alloc] initWithEZAudioFile:audioFile
|
||||
withDelegate:audioPlayerDelegate];
|
||||
}
|
||||
|
||||
+(EZAudioPlayer *)audioPlayerWithURL:(NSURL *)url {
|
||||
return [[EZAudioPlayer alloc] initWithURL:url];
|
||||
}
|
||||
|
||||
+(EZAudioPlayer *)audioPlayerWithURL:(NSURL *)url
|
||||
withDelegate:(id<EZAudioPlayerDelegate>)audioPlayerDelegate {
|
||||
return [[EZAudioPlayer alloc] initWithURL:url
|
||||
withDelegate:audioPlayerDelegate];
|
||||
}
|
||||
|
||||
#pragma mark - Singleton
|
||||
+(EZAudioPlayer *)sharedAudioPlayer {
|
||||
static EZAudioPlayer *_sharedAudioPlayer = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
_sharedAudioPlayer = [[EZAudioPlayer alloc] init];
|
||||
});
|
||||
return _sharedAudioPlayer;
|
||||
}
|
||||
|
||||
#pragma mark - Private Configuration
|
||||
-(void)_configureAudioPlayer {
|
||||
|
||||
// Defaults
|
||||
self.output = [EZOutput sharedOutput];
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
// Configure the AVSession
|
||||
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
|
||||
NSError *err = NULL;
|
||||
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:&err];
|
||||
if( err ){
|
||||
NSLog(@"There was an error creating the audio session");
|
||||
}
|
||||
[audioSession overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:NULL];
|
||||
if( err ){
|
||||
NSLog(@"There was an error sending the audio to the speakers");
|
||||
}
|
||||
#elif TARGET_OS_MAC
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#pragma mark - Getters
|
||||
-(EZAudioFile*)audioFile {
|
||||
return _audioFile;
|
||||
}
|
||||
|
||||
-(float)currentTime {
|
||||
NSAssert(_audioFile,@"No audio file to perform the seek on, check that EZAudioFile is not nil");
|
||||
return [EZAudio MAP:self.audioFile.frameIndex
|
||||
leftMin:0
|
||||
leftMax:self.audioFile.totalFrames
|
||||
rightMin:0
|
||||
rightMax:self.audioFile.totalDuration];
|
||||
}
|
||||
|
||||
-(BOOL)endOfFile {
|
||||
return _eof;
|
||||
}
|
||||
|
||||
-(SInt64)frameIndex {
|
||||
NSAssert(_audioFile,@"No audio file to perform the seek on, check that EZAudioFile is not nil");
|
||||
return _audioFile.frameIndex;
|
||||
}
|
||||
|
||||
-(BOOL)isPlaying {
|
||||
return self.output.isPlaying;
|
||||
}
|
||||
|
||||
-(EZOutput*)output {
|
||||
NSAssert(_output,@"No output was found, this should by default be the EZOutput shared instance");
|
||||
return _output;
|
||||
}
|
||||
|
||||
-(float)totalDuration {
|
||||
NSAssert(_audioFile,@"No audio file to perform the seek on, check that EZAudioFile is not nil");
|
||||
return _audioFile.totalDuration;
|
||||
}
|
||||
|
||||
-(SInt64)totalFrames {
|
||||
NSAssert(_audioFile,@"No audio file to perform the seek on, check that EZAudioFile is not nil");
|
||||
return _audioFile.totalFrames;
|
||||
}
|
||||
|
||||
-(NSURL *)url {
|
||||
NSAssert(_audioFile,@"No audio file to perform the seek on, check that EZAudioFile is not nil");
|
||||
return _audioFile.url;
|
||||
}
|
||||
|
||||
#pragma mark - Setters
|
||||
-(void)setAudioFile:(EZAudioFile *)audioFile {
|
||||
if( _audioFile ){
|
||||
_audioFile.audioFileDelegate = nil;
|
||||
}
|
||||
_eof = NO;
|
||||
_audioFile = [EZAudioFile audioFileWithURL:audioFile.url andDelegate:self];
|
||||
NSAssert(_output,@"No output was found, this should by default be the EZOutput shared instance");
|
||||
[_output setAudioStreamBasicDescription:self.audioFile.clientFormat];
|
||||
}
|
||||
|
||||
-(void)setOutput:(EZOutput*)output {
|
||||
_output = output;
|
||||
_output.outputDataSource = self;
|
||||
}
|
||||
|
||||
#pragma mark - Methods
|
||||
-(void)play {
|
||||
NSAssert(_audioFile,@"No audio file to perform the seek on, check that EZAudioFile is not nil");
|
||||
if( _audioFile ){
|
||||
[_output startPlayback];
|
||||
if( self.frameIndex != self.totalFrames ){
|
||||
_eof = NO;
|
||||
}
|
||||
if( self.audioPlayerDelegate ){
|
||||
if( [self.audioPlayerDelegate respondsToSelector:@selector(audioPlayer:didResumePlaybackOnAudioFile:)] ){
|
||||
// Notify the delegate we're starting playback
|
||||
[self.audioPlayerDelegate audioPlayer:self didResumePlaybackOnAudioFile:_audioFile];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-(void)pause {
|
||||
NSAssert(self.audioFile,@"No audio file to perform the seek on, check that EZAudioFile is not nil");
|
||||
if( _audioFile ){
|
||||
[_output stopPlayback];
|
||||
if( self.audioPlayerDelegate ){
|
||||
if( [self.audioPlayerDelegate respondsToSelector:@selector(audioPlayer:didPausePlaybackOnAudioFile:)] ){
|
||||
// Notify the delegate we're pausing playback
|
||||
[self.audioPlayerDelegate audioPlayer:self didPausePlaybackOnAudioFile:_audioFile];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-(void)seekToFrame:(SInt64)frame {
|
||||
NSAssert(_audioFile,@"No audio file to perform the seek on, check that EZAudioFile is not nil");
|
||||
if( _audioFile ){
|
||||
[_audioFile seekToFrame:frame];
|
||||
}
|
||||
if( self.frameIndex != self.totalFrames ){
|
||||
_eof = NO;
|
||||
}
|
||||
}
|
||||
|
||||
-(void)stop {
|
||||
NSAssert(_audioFile,@"No audio file to perform the seek on, check that EZAudioFile is not nil");
|
||||
if( _audioFile ){
|
||||
[_output stopPlayback];
|
||||
[_audioFile seekToFrame:0];
|
||||
_eof = NO;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - EZAudioFileDelegate
|
||||
-(void)audioFile:(EZAudioFile *)audioFile
|
||||
readAudio:(float **)buffer
|
||||
withBufferSize:(UInt32)bufferSize
|
||||
withNumberOfChannels:(UInt32)numberOfChannels {
|
||||
if( self.audioPlayerDelegate ){
|
||||
if( [self.audioPlayerDelegate respondsToSelector:@selector(audioPlayer:readAudio:withBufferSize:withNumberOfChannels:inAudioFile:)] ){
|
||||
[self.audioPlayerDelegate audioPlayer:self
|
||||
readAudio:buffer
|
||||
withBufferSize:bufferSize
|
||||
withNumberOfChannels:numberOfChannels
|
||||
inAudioFile:audioFile];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-(void)audioFile:(EZAudioFile *)audioFile updatedPosition:(SInt64)framePosition {
|
||||
if( self.audioPlayerDelegate ){
|
||||
if( [self.audioPlayerDelegate respondsToSelector:@selector(audioPlayer:updatedPosition:inAudioFile:)] ){
|
||||
[self.audioPlayerDelegate audioPlayer:self
|
||||
updatedPosition:framePosition
|
||||
inAudioFile:audioFile];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - EZOutputDataSource
|
||||
-(void) output:(EZOutput *)output
|
||||
shouldFillAudioBufferList:(AudioBufferList *)audioBufferList
|
||||
withNumberOfFrames:(UInt32)frames
|
||||
{
|
||||
if( self.audioFile )
|
||||
{
|
||||
UInt32 bufferSize;
|
||||
[self.audioFile readFrames:frames
|
||||
audioBufferList:audioBufferList
|
||||
bufferSize:&bufferSize
|
||||
eof:&_eof];
|
||||
if( _eof && self.shouldLoop )
|
||||
{
|
||||
[self seekToFrame:0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -46,6 +46,10 @@
|
||||
|
||||
*/
|
||||
@interface EZAudioPlot : EZPlot
|
||||
{
|
||||
CGPoint *plotData;
|
||||
UInt32 plotLength;
|
||||
}
|
||||
|
||||
#pragma mark - Adjust Resolution
|
||||
///-----------------------------------------------------------
|
||||
@@ -59,4 +63,20 @@
|
||||
*/
|
||||
-(int)setRollingHistoryLength:(int)historyLength;
|
||||
|
||||
/**
|
||||
Provides the length of the rolling history buffer
|
||||
* @return An int representing the length of the rolling history buffer
|
||||
*/
|
||||
-(int)rollingHistoryLength;
|
||||
|
||||
#pragma mark - Subclass Methods
|
||||
|
||||
/**
|
||||
<#Description#>
|
||||
@param data <#theplotData description#>
|
||||
@param length <#length description#>
|
||||
*/
|
||||
-(void)setSampleData:(float *)data
|
||||
length:(int)length;
|
||||
|
||||
@end
|
||||
|
||||
+26
-24
@@ -37,9 +37,6 @@
|
||||
int _scrollHistoryIndex;
|
||||
UInt32 _scrollHistoryLength;
|
||||
BOOL _changingHistorySize;
|
||||
|
||||
CGPoint *_sampleData;
|
||||
UInt32 _sampleLength;
|
||||
}
|
||||
@end
|
||||
|
||||
@@ -92,6 +89,7 @@
|
||||
self.plotType = EZPlotTypeRolling;
|
||||
self.shouldMirror = NO;
|
||||
self.shouldFill = NO;
|
||||
plotData = NULL;
|
||||
_scrollHistory = NULL;
|
||||
_scrollHistoryLength = kEZAudioPlotDefaultHistoryBufferLength;
|
||||
}
|
||||
@@ -136,18 +134,18 @@
|
||||
}
|
||||
|
||||
#pragma mark - Get Data
|
||||
- (void)_setSampleData:(float *)the_sampleData
|
||||
length:(int)length {
|
||||
if( _sampleData != nil ){
|
||||
free(_sampleData);
|
||||
-(void)setSampleData:(float *)data
|
||||
length:(int)length {
|
||||
if( plotData != nil ){
|
||||
free(plotData);
|
||||
}
|
||||
|
||||
_sampleData = (CGPoint *)calloc(sizeof(CGPoint),length);
|
||||
_sampleLength = length;
|
||||
plotData = (CGPoint *)calloc(sizeof(CGPoint),length);
|
||||
plotLength = length;
|
||||
|
||||
for(int i = 0; i < length; i++) {
|
||||
the_sampleData[i] = i == 0 ? 0 : the_sampleData[i];
|
||||
_sampleData[i] = CGPointMake(i,the_sampleData[i] * _gain);
|
||||
data[i] = i == 0 ? 0 : data[i];
|
||||
plotData[i] = CGPointMake(i,data[i] * _gain);
|
||||
}
|
||||
|
||||
[self _refreshDisplay];
|
||||
@@ -166,15 +164,15 @@
|
||||
isResolutionChanging:&_changingHistorySize];
|
||||
|
||||
//
|
||||
[self _setSampleData:_scrollHistory
|
||||
length:(!_setMaxLength?kEZAudioPlotMaxHistoryBufferLength:_scrollHistoryLength)];
|
||||
[self setSampleData:_scrollHistory
|
||||
length:(!_setMaxLength?kEZAudioPlotMaxHistoryBufferLength:_scrollHistoryLength)];
|
||||
_setMaxLength = YES;
|
||||
|
||||
}
|
||||
else if( _plotType == EZPlotTypeBuffer ){
|
||||
|
||||
[self _setSampleData:buffer
|
||||
length:bufferSize];
|
||||
[self setSampleData:buffer
|
||||
length:bufferSize];
|
||||
|
||||
}
|
||||
else {
|
||||
@@ -183,9 +181,7 @@
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Drawing
|
||||
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
- (void)drawRect:(CGRect)rect
|
||||
{
|
||||
@@ -213,18 +209,18 @@
|
||||
[(NSColor*)self.color set];
|
||||
#endif
|
||||
|
||||
if(_sampleLength > 0) {
|
||||
if(plotLength > 0) {
|
||||
|
||||
_sampleData[_sampleLength-1] = CGPointMake(_sampleLength-1,0.0f);
|
||||
plotData[plotLength-1] = CGPointMake(plotLength-1,0.0f);
|
||||
|
||||
CGMutablePathRef halfPath = CGPathCreateMutable();
|
||||
CGPathAddLines(halfPath,
|
||||
NULL,
|
||||
_sampleData,
|
||||
_sampleLength);
|
||||
plotData,
|
||||
plotLength);
|
||||
CGMutablePathRef path = CGPathCreateMutable();
|
||||
|
||||
double xscale = (frame.size.width) / (float)_sampleLength;
|
||||
double xscale = (frame.size.width) / (float)plotLength;
|
||||
double halfHeight = floor( frame.size.height / 2.0 );
|
||||
|
||||
// iOS drawing origin is flipped by default so make sure we account for that
|
||||
@@ -288,9 +284,15 @@
|
||||
_changingHistorySize = NO;
|
||||
return historyLength;
|
||||
}
|
||||
|
||||
-(int)rollingHistoryLength {
|
||||
return _scrollHistoryLength;
|
||||
}
|
||||
|
||||
-(void)dealloc {
|
||||
free(_sampleData);
|
||||
if( plotData ){
|
||||
free(plotData);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -136,7 +136,22 @@ typedef struct {
|
||||
*/
|
||||
-(int)setRollingHistoryLength:(int)historyLength;
|
||||
|
||||
/**
|
||||
Provides the length of the rolling history buffer
|
||||
* @return An int representing the length of the rolling history buffer
|
||||
*/
|
||||
-(int)rollingHistoryLength;
|
||||
|
||||
#pragma mark - Shared Methods
|
||||
///-----------------------------------------------------------
|
||||
/// @name Clearing The Plot
|
||||
///-----------------------------------------------------------
|
||||
|
||||
/**
|
||||
Clears all data from the audio plot (includes both EZPlotTypeBuffer and EZPlotTypeRolling)
|
||||
*/
|
||||
-(void)clear;
|
||||
|
||||
///-----------------------------------------------------------
|
||||
/// @name Shared OpenGL Methods
|
||||
///-----------------------------------------------------------
|
||||
|
||||
+41
-16
@@ -26,6 +26,7 @@
|
||||
#import "EZAudioPlotGL.h"
|
||||
|
||||
#import "EZAudio.h"
|
||||
#import "EZAudioPlot.h"
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
#import "EZAudioPlotGLKViewController.h"
|
||||
@@ -207,6 +208,9 @@
|
||||
[self.glViewController updateBuffer:buffer
|
||||
withBufferSize:bufferSize];
|
||||
#elif TARGET_OS_MAC
|
||||
|
||||
|
||||
|
||||
if( _copiedBuffer == NULL ){
|
||||
_copiedBuffer = (float*)malloc(bufferSize*sizeof(float));
|
||||
}
|
||||
@@ -262,12 +266,12 @@
|
||||
NSOpenGLPixelFormatAttribute attrs[] =
|
||||
{
|
||||
NSOpenGLPFADoubleBuffer,
|
||||
NSOpenGLPFAMultisample,
|
||||
NSOpenGLPFASampleBuffers, 1,
|
||||
NSOpenGLPFASamples, 4,
|
||||
NSOpenGLPFAMultisample,
|
||||
NSOpenGLPFASampleBuffers, 1,
|
||||
NSOpenGLPFASamples, 4,
|
||||
NSOpenGLPFADepthSize, 24,
|
||||
NSOpenGLPFAOpenGLProfile,
|
||||
NSOpenGLProfileVersion3_2Core, 0,
|
||||
NSOpenGLProfileVersion3_2Core, 0
|
||||
};
|
||||
|
||||
NSOpenGLPixelFormat *pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
|
||||
@@ -277,10 +281,10 @@
|
||||
NSLog(@"No OpenGL pixel format");
|
||||
}
|
||||
|
||||
NSOpenGLContext* context = [[NSOpenGLContext alloc] initWithFormat:pf shareContext:nil];
|
||||
NSOpenGLContext* context = [[NSOpenGLContext alloc] initWithFormat:pf shareContext:nil];
|
||||
|
||||
// Debug only
|
||||
CGLEnable([context CGLContextObj], kCGLCECrashOnRemovedFunctions);
|
||||
CGLEnable([context CGLContextObj], kCGLCECrashOnRemovedFunctions);
|
||||
|
||||
self.pixelFormat = pf;
|
||||
self.openGLContext = context;
|
||||
@@ -288,7 +292,7 @@
|
||||
|
||||
-(void)_setupView {
|
||||
self.backgroundColor = [NSColor colorWithCalibratedRed: 0.796 green: 0.749 blue: 0.663 alpha: 1];
|
||||
self.color = [NSColor colorWithCalibratedRed: 0.481 green: 0.548 blue: 0.637 alpha: 1];
|
||||
self.color = [NSColor colorWithCalibratedRed: 0.481 green: 0.548 blue: 0.637 alpha: 1];
|
||||
}
|
||||
|
||||
#pragma mark - Prepare
|
||||
@@ -296,7 +300,7 @@
|
||||
[super prepareOpenGL];
|
||||
|
||||
GLint swapInt = 1;
|
||||
[self.openGLContext setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
|
||||
[self.openGLContext setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Setup VABs and VBOs //
|
||||
@@ -318,13 +322,17 @@
|
||||
glBindBuffer(GL_ARRAY_BUFFER,_rollingPlotVBO);
|
||||
}
|
||||
|
||||
// Enable anti-aliasing
|
||||
glEnable(GL_MULTISAMPLE);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glClearColor(0, 0, 0, 0);
|
||||
self.layer = nil;
|
||||
|
||||
// Set the background color
|
||||
[self _refreshWithBackgroundColor:self.backgroundColor];
|
||||
[self _refreshWithColor:self.color];
|
||||
|
||||
// Enable anti-aliasing
|
||||
glEnable(GL_MULTISAMPLE);
|
||||
|
||||
// Setup the display link (rendering loop)
|
||||
[self _setupDisplayLink];
|
||||
|
||||
@@ -445,7 +453,7 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink,
|
||||
|
||||
// If starting with a VBO of half of our max size make sure we initialize it to anticipate
|
||||
// a filled graph (which needs 2 * bufferSize) to allocate its resources properly
|
||||
if( !_hasRollingPlotData && _drawingType == EZAudioPlotGLDrawTypeLineStrip ){
|
||||
if( !_hasRollingPlotData ){
|
||||
EZAudioPlotGLPoint maxGraph[2*kEZAudioPlotMaxHistoryBufferLength];
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(maxGraph), maxGraph, GL_STREAM_DRAW );
|
||||
_hasRollingPlotData = YES;
|
||||
@@ -500,7 +508,7 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink,
|
||||
CGLLockContext([[self openGLContext] CGLContextObj]);
|
||||
|
||||
// Draw frame
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
|
||||
|
||||
if( _hasBufferPlotData || _hasRollingPlotData ){
|
||||
// Plot either a buffer plot or a rolling plot
|
||||
@@ -561,8 +569,8 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink,
|
||||
self.baseEffect.transform.modelviewMatrix = GLKMatrix4MakeXRotation(0);
|
||||
|
||||
// Enable the vertex data
|
||||
glEnableVertexAttribArray(GLKVertexAttribPosition);
|
||||
// Define the vertex data size & layout
|
||||
glEnableVertexAttribArray(GLKVertexAttribPosition);
|
||||
// Define the vertex data size & layout
|
||||
glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, sizeof(EZAudioPlotGLPoint), NULL);
|
||||
// Draw the triangle
|
||||
glDrawArrays(_drawingType, 0,_rollingPlotGraphSize);
|
||||
@@ -651,7 +659,8 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink,
|
||||
#pragma mark - Adjust Resolution
|
||||
-(int)setRollingHistoryLength:(int)historyLength {
|
||||
#if TARGET_OS_IPHONE
|
||||
return [self.glViewController setRollingHistoryLength:historyLength];
|
||||
int result = [self.glViewController setRollingHistoryLength:historyLength];
|
||||
return result;
|
||||
#elif TARGET_OS_MAC
|
||||
historyLength = MIN(historyLength,kEZAudioPlotMaxHistoryBufferLength);
|
||||
size_t floatByteSize = sizeof(float);
|
||||
@@ -674,6 +683,22 @@ static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink,
|
||||
return kEZAudioPlotDefaultHistoryBufferLength;
|
||||
}
|
||||
|
||||
-(int)rollingHistoryLength {
|
||||
#if TARGET_OS_IPHONE
|
||||
return self.glViewController.rollingHistoryLength;
|
||||
#elif TARGET_OS_MAC
|
||||
return _scrollHistoryLength;
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma mark - Clearing
|
||||
-(void)clear {
|
||||
#if TARGET_OS_IPHONE
|
||||
[self.glViewController clear];
|
||||
#elif TARGET_OS_MAC
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma mark - Graph Methods
|
||||
+(void)fillGraph:(EZAudioPlotGLPoint*)graph
|
||||
withGraphSize:(UInt32)graphSize
|
||||
|
||||
@@ -92,6 +92,22 @@
|
||||
*/
|
||||
-(int)setRollingHistoryLength:(int)historyLength;
|
||||
|
||||
/**
|
||||
Provides the length of the rolling history buffer
|
||||
* @return An int representing the length of the rolling history buffer
|
||||
*/
|
||||
-(int)rollingHistoryLength;
|
||||
|
||||
#pragma mark - Clearing
|
||||
///-----------------------------------------------------------
|
||||
/// @name Clearing The Plot
|
||||
///-----------------------------------------------------------
|
||||
|
||||
/**
|
||||
Clears all data from the audio plot (includes both EZPlotTypeBuffer and EZPlotTypeRolling)
|
||||
*/
|
||||
-(void)clear;
|
||||
|
||||
#pragma mark - Get Samples
|
||||
///-----------------------------------------------------------
|
||||
/// @name Updating The Plot
|
||||
|
||||
@@ -100,7 +100,15 @@
|
||||
[super viewDidLoad];
|
||||
|
||||
// Setup the context
|
||||
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
|
||||
if( ![EAGLContext currentContext] )
|
||||
{
|
||||
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
|
||||
}
|
||||
else
|
||||
{
|
||||
self.context = [EAGLContext currentContext];
|
||||
}
|
||||
|
||||
if (!self.context) {
|
||||
NSLog(@"Failed to create ES context");
|
||||
}
|
||||
@@ -128,9 +136,9 @@
|
||||
|
||||
#pragma mark - Adjust Resolution
|
||||
-(int)setRollingHistoryLength:(int)historyLength {
|
||||
_changingHistorySize = YES;
|
||||
historyLength = MIN(historyLength,kEZAudioPlotMaxHistoryBufferLength);
|
||||
size_t floatByteSize = sizeof(float);
|
||||
_changingHistorySize = YES;
|
||||
if( _scrollHistoryLength != historyLength ){
|
||||
_scrollHistoryLength = historyLength;
|
||||
}
|
||||
@@ -143,10 +151,68 @@
|
||||
else {
|
||||
_scrollHistoryIndex = _scrollHistoryLength;
|
||||
}
|
||||
[self _updateRollingPlotDisplay];
|
||||
_changingHistorySize = NO;
|
||||
return historyLength;
|
||||
}
|
||||
|
||||
-(int)rollingHistoryLength {
|
||||
return _scrollHistoryLength;
|
||||
}
|
||||
|
||||
#pragma mark - Clearing
|
||||
-(void)clear
|
||||
{
|
||||
_scrollHistoryIndex = 0;
|
||||
[self _clearBufferPlot];
|
||||
[self _clearRollingPlot];
|
||||
}
|
||||
|
||||
-(void)_clearBufferPlot
|
||||
{
|
||||
if( _hasBufferPlotData )
|
||||
{
|
||||
float empty[_bufferPlotGraphSize];
|
||||
memset( empty, 0.0f, sizeof(float) );
|
||||
[self _updateBufferPlotBufferWithAudioReceived:empty
|
||||
withBufferSize:_bufferPlotGraphSize];
|
||||
}
|
||||
}
|
||||
|
||||
-(void)_clearRollingPlot
|
||||
{
|
||||
if( _hasRollingPlotData )
|
||||
{
|
||||
float empty[_rollingPlotGraphSize];
|
||||
EZAudioPlotGLPoint graph[_rollingPlotGraphSize];
|
||||
// Figure out better way to do this
|
||||
for(int i = 0; i < _rollingPlotGraphSize; i++ )
|
||||
{
|
||||
empty[i] = 0.0f;
|
||||
}
|
||||
for(int i = 0; i < _scrollHistoryLength; i++)
|
||||
{
|
||||
_scrollHistory[i] = 0.0f;
|
||||
}
|
||||
// Update the scroll history datasource
|
||||
[EZAudio updateScrollHistory:&_scrollHistory
|
||||
withLength:_scrollHistoryLength
|
||||
atIndex:&_scrollHistoryIndex
|
||||
withBuffer:empty
|
||||
withBufferSize:_rollingPlotGraphSize
|
||||
isResolutionChanging:&_changingHistorySize];
|
||||
// Fill in graph data
|
||||
[EZAudioPlotGL fillGraph:graph
|
||||
withGraphSize:_rollingPlotGraphSize
|
||||
forDrawingType:_drawingType
|
||||
withBuffer:_scrollHistory
|
||||
withBufferSize:_scrollHistoryLength
|
||||
withGain:self.gain];
|
||||
// Update the drawing
|
||||
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(graph), graph);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Get Samples
|
||||
-(void)updateBuffer:(float *)buffer
|
||||
withBufferSize:(UInt32)bufferSize {
|
||||
@@ -154,6 +220,9 @@
|
||||
// Make sure the update render loop is active
|
||||
if( self.paused ) self.paused = NO;
|
||||
|
||||
// Make sure we are updating the buffers on the correct gl context.
|
||||
EAGLContext.currentContext = self.context;
|
||||
|
||||
// Draw based on plot type
|
||||
switch(_plotType) {
|
||||
case EZPlotTypeBuffer:
|
||||
@@ -207,6 +276,8 @@
|
||||
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(graph), graph);
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
}
|
||||
|
||||
-(void)_updateRollingPlotBufferWithAudioReceived:(float*)buffer
|
||||
@@ -214,11 +285,13 @@
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, _rollingPlotVBO);
|
||||
|
||||
|
||||
|
||||
// If starting with a VBO of half of our max size make sure we initialize it to anticipate
|
||||
// a filled graph (which needs 2 * bufferSize) to allocate its resources properly
|
||||
if( !_hasRollingPlotData && _drawingType == EZAudioPlotGLDrawTypeLineStrip ){
|
||||
if( !_hasRollingPlotData ){
|
||||
EZAudioPlotGLPoint maxGraph[2*kEZAudioPlotMaxHistoryBufferLength];
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(maxGraph), maxGraph, GL_STREAM_DRAW );
|
||||
glBufferData( GL_ARRAY_BUFFER, sizeof(maxGraph), maxGraph, GL_STREAM_DRAW );
|
||||
_hasRollingPlotData = YES;
|
||||
}
|
||||
|
||||
@@ -229,8 +302,6 @@
|
||||
// Fill the graph with data
|
||||
EZAudioPlotGLPoint graph[_rollingPlotGraphSize];
|
||||
|
||||
|
||||
|
||||
// Update the scroll history datasource
|
||||
[EZAudio updateScrollHistory:&_scrollHistory
|
||||
withLength:_scrollHistoryLength
|
||||
@@ -256,10 +327,36 @@
|
||||
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(graph), graph);
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
|
||||
}
|
||||
|
||||
-(void)_updateRollingPlotDisplay {
|
||||
// Setup the plot
|
||||
_rollingPlotGraphSize = [EZAudioPlotGL graphSizeForDrawingType:_drawingType
|
||||
withBufferSize:_scrollHistoryLength];
|
||||
|
||||
// Fill the graph with data
|
||||
EZAudioPlotGLPoint graph[_rollingPlotGraphSize];
|
||||
// Fill in graph data
|
||||
[EZAudioPlotGL fillGraph:graph
|
||||
withGraphSize:_rollingPlotGraphSize
|
||||
forDrawingType:_drawingType
|
||||
withBuffer:_scrollHistory
|
||||
withBufferSize:_scrollHistoryLength
|
||||
withGain:self.gain];
|
||||
|
||||
// Update the drawing
|
||||
if( _hasRollingPlotData ){
|
||||
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(graph), graph);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Drawing
|
||||
-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
|
||||
|
||||
EAGLContext.currentContext = self.context;
|
||||
|
||||
// Clear the context
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
@@ -293,20 +390,23 @@
|
||||
glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, sizeof(EZAudioPlotGLPoint), NULL);
|
||||
|
||||
// Normal plot
|
||||
glPushMatrix();
|
||||
|
||||
self.baseEffect.transform.modelviewMatrix = GLKMatrix4MakeXRotation(0);
|
||||
glDrawArrays(_drawingType, 0, _bufferPlotGraphSize);
|
||||
glPopMatrix();
|
||||
|
||||
|
||||
if( self.shouldMirror ){
|
||||
// Mirrored plot
|
||||
[self.baseEffect prepareToDraw];
|
||||
glPushMatrix();
|
||||
|
||||
self.baseEffect.transform.modelviewMatrix = GLKMatrix4MakeXRotation(M_PI);
|
||||
glDrawArrays(_drawingType, 0, _bufferPlotGraphSize);
|
||||
glPopMatrix();
|
||||
|
||||
}
|
||||
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER,0);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,20 +419,22 @@
|
||||
glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, sizeof(EZAudioPlotGLPoint), NULL);
|
||||
|
||||
// Normal plot
|
||||
glPushMatrix();
|
||||
|
||||
self.baseEffect.transform.modelviewMatrix = GLKMatrix4MakeXRotation(0);
|
||||
glDrawArrays(_drawingType, 0, _rollingPlotGraphSize);
|
||||
glPopMatrix();
|
||||
|
||||
|
||||
if( self.shouldMirror ){
|
||||
// Mirrored plot
|
||||
[self.baseEffect prepareToDraw];
|
||||
glPushMatrix();
|
||||
|
||||
self.baseEffect.transform.modelviewMatrix = GLKMatrix4MakeXRotation(3.14159265359);
|
||||
glDrawArrays(_drawingType, 0, _rollingPlotGraphSize);
|
||||
glPopMatrix();
|
||||
|
||||
}
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER,0);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// EZAudioWaveformData.h
|
||||
// EZAudioPlayFileExample
|
||||
//
|
||||
// Created by Syed Haris Ali on 2/14/15.
|
||||
// Copyright (c) 2015 Syed Haris Ali. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
#pragma mark - EZAudioWaveformData
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@interface EZAudioWaveformData : NSObject
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
+ (instancetype) dataWithNumberOfChannels:(int)numberOfChannels
|
||||
buffers:(float **)buffers
|
||||
bufferSize:(UInt32)bufferSize;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@property (nonatomic, assign, readonly) int numberOfChannels;
|
||||
@property (nonatomic, assign, readonly) float **buffers;
|
||||
@property (nonatomic, assign, readonly) UInt32 bufferSize;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
- (float *) bufferForChannel:(int)channel;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,74 @@
|
||||
//
|
||||
// EZAudioWaveformData.m
|
||||
// EZAudioPlayFileExample
|
||||
//
|
||||
// Created by Syed Haris Ali on 2/14/15.
|
||||
// Copyright (c) 2015 Syed Haris Ali. All rights reserved.
|
||||
//
|
||||
|
||||
#import "EZAudioWaveformData.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
#pragma mark - EZAudioWaveformData
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@interface EZAudioWaveformData ()
|
||||
@property (nonatomic, assign, readwrite) int numberOfChannels;
|
||||
@property (nonatomic, assign, readwrite) float **buffers;
|
||||
@property (nonatomic, assign, readwrite) UInt32 bufferSize;
|
||||
@end
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@implementation EZAudioWaveformData
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
for (int i = 0; i < self.numberOfChannels; i++)
|
||||
{
|
||||
free(self.buffers[i]);
|
||||
}
|
||||
free(self.buffers);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
+ (instancetype)dataWithNumberOfChannels:(int)numberOfChannels
|
||||
buffers:(float **)buffers
|
||||
bufferSize:(UInt32)bufferSize
|
||||
{
|
||||
id waveformData = [[self alloc] init];
|
||||
|
||||
size_t size = sizeof(float *) * numberOfChannels;
|
||||
float **buffersCopy = (float **)malloc(size);
|
||||
for (int i = 0; i < numberOfChannels; i++)
|
||||
{
|
||||
size = sizeof(float) * bufferSize;
|
||||
buffersCopy[i] = (float *)malloc(size);
|
||||
memcpy(buffersCopy[i], buffers[i], size);
|
||||
}
|
||||
|
||||
((EZAudioWaveformData *)waveformData).buffers = buffersCopy;
|
||||
((EZAudioWaveformData *)waveformData).bufferSize = bufferSize;
|
||||
((EZAudioWaveformData *)waveformData).numberOfChannels = numberOfChannels;
|
||||
|
||||
return waveformData;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
- (float *)bufferForChannel:(int)channel
|
||||
{
|
||||
float *buffer = NULL;
|
||||
if (channel < self.numberOfChannels)
|
||||
{
|
||||
buffer = self.buffers[channel];
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@end
|
||||
@@ -25,7 +25,6 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <AudioToolbox/AudioToolbox.h>
|
||||
#import "AEFloatConverter.h"
|
||||
#import "TargetConditionals.h"
|
||||
|
||||
@class EZAudio;
|
||||
|
||||
+54
-53
@@ -51,7 +51,6 @@ static const UInt32 kEZAudioMicrophoneEnableFlag = 1;
|
||||
BOOL _isFetching;
|
||||
|
||||
/// Stream Description
|
||||
AEFloatConverter *converter;
|
||||
AudioStreamBasicDescription streamFormat;
|
||||
|
||||
/// Audio Graph and Input/Output Units
|
||||
@@ -86,7 +85,7 @@ static OSStatus inputCallback(void *inRefCon,
|
||||
UInt32 inNumberFrames,
|
||||
AudioBufferList *ioData ) {
|
||||
EZMicrophone *microphone = (__bridge EZMicrophone*)inRefCon;
|
||||
OSStatus result = noErr;
|
||||
OSStatus result = noErr;
|
||||
// Render audio into buffer
|
||||
result = AudioUnitRender(microphone->microphoneInput,
|
||||
ioActionFlags,
|
||||
@@ -95,30 +94,28 @@ static OSStatus inputCallback(void *inRefCon,
|
||||
inNumberFrames,
|
||||
microphone->microphoneInputBuffer);
|
||||
if( !result ){
|
||||
// Notify delegate (OF-style)
|
||||
@autoreleasepool {
|
||||
// Audio Received (float array)
|
||||
if( microphone.microphoneDelegate ){
|
||||
// THIS IS NOT OCCURING ON THE MAIN THREAD
|
||||
if( [microphone.microphoneDelegate respondsToSelector:@selector(microphone:hasAudioReceived:withBufferSize:withNumberOfChannels:)] ){
|
||||
AEFloatConverterToFloat(microphone->converter,
|
||||
microphone->microphoneInputBuffer,
|
||||
microphone->floatBuffers,
|
||||
inNumberFrames);
|
||||
}
|
||||
// ----- Notify delegate (OF-style) -----
|
||||
// Audio Received (float array)
|
||||
if( microphone.microphoneDelegate ){
|
||||
// THIS IS NOT OCCURING ON THE MAIN THREAD
|
||||
if( [microphone.microphoneDelegate respondsToSelector:@selector(microphone:hasAudioReceived:withBufferSize:withNumberOfChannels:)] ){
|
||||
// AEFloatConverterToFloat(microphone->converter,
|
||||
// microphone->microphoneInputBuffer,
|
||||
// microphone->floatBuffers,
|
||||
// inNumberFrames);
|
||||
[microphone.microphoneDelegate microphone:microphone
|
||||
hasAudioReceived:microphone->floatBuffers
|
||||
withBufferSize:inNumberFrames
|
||||
withNumberOfChannels:microphone->streamFormat.mChannelsPerFrame];
|
||||
}
|
||||
// Audio Received (buffer list)
|
||||
if( microphone.microphoneDelegate ){
|
||||
if( [microphone.microphoneDelegate respondsToSelector:@selector(microphone:hasBufferList:withBufferSize:withNumberOfChannels:)] ){
|
||||
[microphone.microphoneDelegate microphone:microphone
|
||||
hasBufferList:microphone->microphoneInputBuffer
|
||||
withBufferSize:inNumberFrames
|
||||
withNumberOfChannels:microphone->streamFormat.mChannelsPerFrame];
|
||||
}
|
||||
}
|
||||
// Audio Received (buffer list)
|
||||
if( microphone.microphoneDelegate ){
|
||||
if( [microphone.microphoneDelegate respondsToSelector:@selector(microphone:hasBufferList:withBufferSize:withNumberOfChannels:)] ){
|
||||
[microphone.microphoneDelegate microphone:microphone
|
||||
hasBufferList:microphone->microphoneInputBuffer
|
||||
withBufferSize:inNumberFrames
|
||||
withNumberOfChannels:microphone->streamFormat.mChannelsPerFrame];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -134,14 +131,31 @@ static OSStatus inputCallback(void *inRefCon,
|
||||
// We're not fetching anything yet
|
||||
_isConfigured = NO;
|
||||
_isFetching = NO;
|
||||
if( !_isConfigured ){
|
||||
// Create the input audio graph
|
||||
[self _createInputUnit];
|
||||
// We're configured meow
|
||||
_isConfigured = YES;
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
-(EZMicrophone *)initWithMicrophoneDelegate:(id<EZMicrophoneDelegate>)microphoneDelegate {
|
||||
self = [self init];
|
||||
self = [super init];
|
||||
if(self){
|
||||
self.microphoneDelegate = microphoneDelegate;
|
||||
// Clear the float buffer
|
||||
floatBuffers = NULL;
|
||||
// We're not fetching anything yet
|
||||
_isConfigured = NO;
|
||||
_isFetching = NO;
|
||||
if( !_isConfigured ){
|
||||
// Create the input audio graph
|
||||
[self _createInputUnit];
|
||||
// We're configured meow
|
||||
_isConfigured = YES;
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -213,12 +227,6 @@ static OSStatus inputCallback(void *inRefCon,
|
||||
#pragma mark - Events
|
||||
-(void)startFetchingAudio {
|
||||
if( !_isFetching ){
|
||||
if( !_isConfigured ){
|
||||
// Create the input audio graph
|
||||
[self _createInputUnit];
|
||||
// We're configured meow
|
||||
_isConfigured = YES;
|
||||
}
|
||||
// Start fetching input
|
||||
[EZAudio checkResult:AudioOutputUnitStart(self->microphoneInput)
|
||||
operation:"Microphone failed to start fetching audio"];
|
||||
@@ -448,11 +456,7 @@ static OSStatus inputCallback(void *inRefCon,
|
||||
// Use approximations for simulator and pull from real device if connected
|
||||
#if !(TARGET_IPHONE_SIMULATOR)
|
||||
// Sample Rate
|
||||
UInt32 propSize = sizeof(Float64);
|
||||
[EZAudio checkResult:AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareSampleRate,
|
||||
&propSize,
|
||||
&hardwareSampleRate)
|
||||
operation:"Could not get hardware sample rate from device"];
|
||||
hardwareSampleRate = [[AVAudioSession sharedInstance] sampleRate];
|
||||
#endif
|
||||
#elif TARGET_OS_MAC
|
||||
hardwareSampleRate = inputScopeSampleRate;
|
||||
@@ -466,17 +470,14 @@ static OSStatus inputCallback(void *inRefCon,
|
||||
#if TARGET_OS_IPHONE
|
||||
// Use approximations for simulator and pull from real device if connected
|
||||
#if !(TARGET_IPHONE_SIMULATOR)
|
||||
UInt32 propSize = sizeof(Float32);
|
||||
[EZAudio checkResult:AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareIOBufferDuration,
|
||||
propSize,
|
||||
&bufferDuration)
|
||||
operation:"Couldn't set the preferred buffer duration from device"];
|
||||
// Buffer Size
|
||||
propSize = sizeof(bufferDuration);
|
||||
[EZAudio checkResult:AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareIOBufferDuration,
|
||||
&propSize,
|
||||
&bufferDuration)
|
||||
operation:"Could not get preferred buffer size from device"];
|
||||
NSError *err;
|
||||
[[AVAudioSession sharedInstance] setPreferredIOBufferDuration:bufferDuration error:&err];
|
||||
if (err) {
|
||||
NSLog(@"Error setting preferredIOBufferDuration for audio session: %@", err.localizedDescription);
|
||||
}
|
||||
|
||||
// Buffer Size
|
||||
bufferDuration = [[AVAudioSession sharedInstance] IOBufferDuration];
|
||||
#endif
|
||||
#elif TARGET_OS_MAC
|
||||
|
||||
@@ -547,7 +548,7 @@ static OSStatus inputCallback(void *inRefCon,
|
||||
microphoneInputBuffer = (AudioBufferList*)malloc(propSize);
|
||||
microphoneInputBuffer->mNumberBuffers = streamFormat.mChannelsPerFrame;
|
||||
for( UInt32 i = 0; i < microphoneInputBuffer->mNumberBuffers; i++ ){
|
||||
microphoneInputBuffer->mBuffers[i].mNumberChannels = 1;
|
||||
microphoneInputBuffer->mBuffers[i].mNumberChannels = streamFormat.mChannelsPerFrame;
|
||||
microphoneInputBuffer->mBuffers[i].mDataByteSize = bufferSizeBytes;
|
||||
microphoneInputBuffer->mBuffers[i].mData = malloc(bufferSizeBytes);
|
||||
}
|
||||
@@ -555,14 +556,14 @@ static OSStatus inputCallback(void *inRefCon,
|
||||
|
||||
#pragma mark - Float Converter Initialization
|
||||
-(void)_configureFloatConverterWithFrameSize:(UInt32)bufferFrameSize {
|
||||
UInt32 bufferSizeBytes = bufferFrameSize * streamFormat.mBytesPerFrame;
|
||||
converter = [[AEFloatConverter alloc] initWithSourceFormat:streamFormat];
|
||||
floatBuffers = (float**)malloc(sizeof(float*)*streamFormat.mChannelsPerFrame);
|
||||
assert(floatBuffers);
|
||||
for ( int i=0; i<streamFormat.mChannelsPerFrame; i++ ) {
|
||||
floatBuffers[i] = (float*)malloc(bufferSizeBytes);
|
||||
assert(floatBuffers[i]);
|
||||
}
|
||||
// UInt32 bufferSizeBytes = bufferFrameSize * streamFormat.mBytesPerFrame;
|
||||
// converter = [[AEFloatConverter alloc] initWithSourceFormat:streamFormat];
|
||||
// floatBuffers = (float**)malloc(sizeof(float*)*streamFormat.mChannelsPerFrame);
|
||||
// assert(floatBuffers);
|
||||
// for ( int i=0; i<streamFormat.mChannelsPerFrame; i++ ) {
|
||||
// floatBuffers[i] = (float*)malloc(bufferSizeBytes);
|
||||
// assert(floatBuffers[i]);
|
||||
// }
|
||||
}
|
||||
|
||||
#pragma mark - Input Callback Initialization
|
||||
|
||||
+16
-5
@@ -70,16 +70,17 @@ inNumberFrames:(UInt32)inNumberFrames
|
||||
*/
|
||||
-(TPCircularBuffer*)outputShouldUseCircularBuffer:(EZOutput *)output;
|
||||
|
||||
|
||||
/**
|
||||
Alternate way to provide output with data anytime the EZOutput needs audio data to play. This function expects you to allocate a chunk of memory for an AudioBufferList (see EZAudio function `audioBufferList`) and will try to free it on a seperate thread (see EZAudio function `freeBufferList:`) when it is done to prevent leaking since this is expected to be the end of the road for the audio signal. If the EZOutputDataSource receives a nil or NULL AudioBufferList then the EZOutput component will output silence.
|
||||
Provides a way to provide output with data anytime the EZOutput needs audio data to play. This function provides an already allocated AudioBufferList to use for providing audio data into the output buffer.
|
||||
@param output The instance of the EZOutput that asked for the data.
|
||||
@param audioBufferList The AudioBufferList structure pointer that needs to be filled with audio data
|
||||
@param frames The amount of frames as a UInt32 that output will need to properly fill its output buffer.
|
||||
@param bufferSize The pointer to the bufferSize the dataSource is expected to set. For instance, if the bufferSize ended up being 512 you'd say *bufferSize = 512.
|
||||
@return A pointer to the AudioBufferList structure holding the audio data. If nil or NULL, will output silence.
|
||||
*/
|
||||
-(AudioBufferList*) output:(EZOutput*)output
|
||||
needsBufferListWithFrames:(UInt32)frames
|
||||
withBufferSize:(UInt32*)bufferSize;
|
||||
-(void) output:(EZOutput *)output
|
||||
shouldFillAudioBufferList:(AudioBufferList*)audioBufferList
|
||||
withNumberOfFrames:(UInt32)frames;
|
||||
|
||||
@end
|
||||
|
||||
@@ -165,6 +166,16 @@ inNumberFrames:(UInt32)inNumberFrames
|
||||
-(void)stopPlayback;
|
||||
|
||||
#pragma mark - Getters
|
||||
///-----------------------------------------------------------
|
||||
/// @name Getting The Output Audio Format
|
||||
///-----------------------------------------------------------
|
||||
|
||||
/**
|
||||
Provides the AudioStreamBasicDescription structure containing the format of the microphone's audio.
|
||||
@return An AudioStreamBasicDescription structure describing the format of the microphone's audio.
|
||||
*/
|
||||
-(AudioStreamBasicDescription)audioStreamBasicDescription;
|
||||
|
||||
///-----------------------------------------------------------
|
||||
/// @name Getting The State Of The Output
|
||||
///-----------------------------------------------------------
|
||||
|
||||
+35
-67
@@ -27,13 +27,6 @@
|
||||
|
||||
#import "EZAudio.h"
|
||||
|
||||
/// Buses
|
||||
static const AudioUnitScope kEZAudioMicrophoneOutputBus = 0;
|
||||
|
||||
/// Flags
|
||||
static const UInt32 kEZAudioMicrophoneEnableFlag = 1;
|
||||
static const UInt32 kEZAudioMicrophoneDisableFlag = 0;
|
||||
|
||||
@interface EZOutput (){
|
||||
BOOL _customASBD;
|
||||
BOOL _isPlaying;
|
||||
@@ -52,6 +45,8 @@ static OSStatus OutputRenderCallback(void *inRefCon,
|
||||
UInt32 inNumberFrames,
|
||||
AudioBufferList *ioData){
|
||||
|
||||
// NSLog(@"output something");
|
||||
|
||||
EZOutput *output = (__bridge EZOutput*)inRefCon;
|
||||
// Manual override
|
||||
if( [output.outputDataSource respondsToSelector:@selector(output:callbackWithActionFlags:inTimeStamp:inBusNumber:inNumberFrames:ioData:)] ){
|
||||
@@ -66,8 +61,8 @@ static OSStatus OutputRenderCallback(void *inRefCon,
|
||||
|
||||
TPCircularBuffer *circularBuffer = [output.outputDataSource outputShouldUseCircularBuffer:output];
|
||||
if( !circularBuffer ){
|
||||
AudioUnitSampleType *left = (AudioUnitSampleType*)ioData->mBuffers[0].mData;
|
||||
AudioUnitSampleType *right = (AudioUnitSampleType*)ioData->mBuffers[1].mData;
|
||||
float *left = (float*)ioData->mBuffers[0].mData;
|
||||
float *right = (float*)ioData->mBuffers[1].mData;
|
||||
for(int i = 0; i < inNumberFrames; i++ ){
|
||||
left[ i ] = 0.0f;
|
||||
right[ i ] = 0.0f;
|
||||
@@ -81,58 +76,27 @@ static OSStatus OutputRenderCallback(void *inRefCon,
|
||||
|
||||
// Get the desired amount of bytes to copy
|
||||
int32_t bytesToCopy = ioData->mBuffers[0].mDataByteSize;
|
||||
AudioSampleType *left = (AudioSampleType*)ioData->mBuffers[0].mData;
|
||||
AudioSampleType *right = (AudioSampleType*)ioData->mBuffers[1].mData;
|
||||
float *left = (float*)ioData->mBuffers[0].mData;
|
||||
float *right = (float*)ioData->mBuffers[1].mData;
|
||||
|
||||
// Get the available bytes in the circular buffer
|
||||
int32_t availableBytes;
|
||||
AudioSampleType *buffer = TPCircularBufferTail(circularBuffer,&availableBytes);
|
||||
float *buffer = TPCircularBufferTail(circularBuffer,&availableBytes);
|
||||
|
||||
// Ideally we'd have all the bytes to be copied, but compare it against the available bytes (get min)
|
||||
int32_t amount = MIN(bytesToCopy,availableBytes);
|
||||
memcpy(left,buffer,amount);
|
||||
memcpy(right,buffer,amount);
|
||||
memcpy( left, buffer, amount );
|
||||
memcpy( right, buffer, amount );
|
||||
|
||||
// Consume those bytes ( this will internally push the head of the circular buffer )
|
||||
TPCircularBufferConsume(circularBuffer,amount);
|
||||
|
||||
}
|
||||
// Provided an AudioBufferList (defaults to silence)
|
||||
else {
|
||||
UInt32 bufferSize;
|
||||
AudioBufferList *bufferList = [output.outputDataSource output:output
|
||||
needsBufferListWithFrames:inNumberFrames
|
||||
withBufferSize:&bufferSize];
|
||||
if( !bufferList ){
|
||||
return noErr;
|
||||
};
|
||||
// Interleaved
|
||||
if( !(ioData->mNumberBuffers == 1) ){
|
||||
AudioUnitSampleType *left = (AudioUnitSampleType*)ioData->mBuffers[0].mData;
|
||||
AudioUnitSampleType *right = (AudioUnitSampleType*)ioData->mBuffers[1].mData;
|
||||
AudioUnitSampleType *interleaved = (AudioUnitSampleType*)bufferList->mBuffers[0].mData;
|
||||
for(int i = 0; i < inNumberFrames; i++ ){
|
||||
if( bufferList ){
|
||||
*left++ = *interleaved++;
|
||||
*right++ = *interleaved++;
|
||||
}
|
||||
else {
|
||||
*left++ = 0.0f;
|
||||
*right++ = 0.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Non-interleaved
|
||||
else {
|
||||
memcpy(ioData,
|
||||
bufferList,
|
||||
sizeof(AudioBufferList)+(bufferList->mNumberBuffers-1)*sizeof(AudioBuffer));
|
||||
}
|
||||
if( bufferList ){
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0ul),^{
|
||||
[EZAudio freeBufferList:bufferList];
|
||||
});
|
||||
}
|
||||
else if( [output.outputDataSource respondsToSelector:@selector(output:shouldFillAudioBufferList:withNumberOfFrames:)] ) {
|
||||
[output.outputDataSource output:output
|
||||
shouldFillAudioBufferList:ioData
|
||||
withNumberOfFrames:inNumberFrames];
|
||||
}
|
||||
|
||||
return noErr;
|
||||
@@ -262,11 +226,7 @@ static OSStatus OutputRenderCallback(void *inRefCon,
|
||||
// Get the hardware sample rate
|
||||
Float64 hardwareSampleRate = 44100;
|
||||
#if !(TARGET_IPHONE_SIMULATOR)
|
||||
UInt32 propSize = sizeof(hardwareSampleRate);
|
||||
[EZAudio checkResult:AudioSessionGetProperty(kAudioSessionProperty_CurrentHardwareSampleRate,
|
||||
&propSize,
|
||||
&hardwareSampleRate)
|
||||
operation:"Could not get hardware sample rate"];
|
||||
hardwareSampleRate = [[AVAudioSession sharedInstance] sampleRate];
|
||||
#endif
|
||||
|
||||
// Setup an ASBD in canonical format by default
|
||||
@@ -322,7 +282,7 @@ static OSStatus OutputRenderCallback(void *inRefCon,
|
||||
|
||||
// Setup an ASBD in canonical format by default
|
||||
if( !_customASBD ){
|
||||
_outputASBD = [EZAudio stereoCanonicalNonInterleavedFormatWithSampleRate:44100];
|
||||
_outputASBD = [EZAudio stereoFloatNonInterleavedFormatWithSampleRate:44100];
|
||||
}
|
||||
|
||||
// Set the format for output
|
||||
@@ -371,26 +331,34 @@ static OSStatus OutputRenderCallback(void *inRefCon,
|
||||
}
|
||||
|
||||
#pragma mark - Getters
|
||||
-(AudioStreamBasicDescription)audioStreamBasicDescription {
|
||||
return _outputASBD;
|
||||
}
|
||||
|
||||
-(BOOL)isPlaying {
|
||||
return _isPlaying;
|
||||
}
|
||||
|
||||
#pragma mark - Setters
|
||||
-(void)setAudioStreamBasicDescription:(AudioStreamBasicDescription)asbd {
|
||||
BOOL wasPlaying = NO;
|
||||
if( self.isPlaying ){
|
||||
NSAssert(self.isPlaying,@"Cannot set the AudioStreamBasicDescription while output is performing playback");
|
||||
[self stopPlayback];
|
||||
wasPlaying = YES;
|
||||
}
|
||||
else {
|
||||
_customASBD = YES;
|
||||
_outputASBD = asbd;
|
||||
// Set the format for output
|
||||
[EZAudio checkResult:AudioUnitSetProperty(_outputUnit,
|
||||
kAudioUnitProperty_StreamFormat,
|
||||
kAudioUnitScope_Input,
|
||||
0,
|
||||
&_outputASBD,
|
||||
sizeof(_outputASBD))
|
||||
operation:"Couldn't set the ASBD for input scope/bos 0"];
|
||||
_customASBD = YES;
|
||||
_outputASBD = asbd;
|
||||
// Set the format for output
|
||||
[EZAudio checkResult:AudioUnitSetProperty(_outputUnit,
|
||||
kAudioUnitProperty_StreamFormat,
|
||||
kAudioUnitScope_Input,
|
||||
0,
|
||||
&_outputASBD,
|
||||
sizeof(_outputASBD))
|
||||
operation:"Couldn't set the ASBD for input scope/bos 0"];
|
||||
if( wasPlaying )
|
||||
{
|
||||
[self startPlayback];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+12
-2
@@ -33,11 +33,11 @@
|
||||
*/
|
||||
typedef NS_ENUM(NSInteger,EZPlotType){
|
||||
/**
|
||||
* Plot that displays only the samples of the current buffer
|
||||
Plot that displays only the samples of the current buffer
|
||||
*/
|
||||
EZPlotTypeBuffer,
|
||||
/**
|
||||
* Plot that displays a rolling history of values using the RMS calculated for each incoming buffer
|
||||
Plot that displays a rolling history of values using the RMS calculated for each incoming buffer
|
||||
*/
|
||||
EZPlotTypeRolling
|
||||
};
|
||||
@@ -91,6 +91,16 @@ typedef NS_ENUM(NSInteger,EZPlotType){
|
||||
*/
|
||||
@property (nonatomic,assign,setter=setShouldMirror:) BOOL shouldMirror;
|
||||
|
||||
#pragma mark - Clearing
|
||||
///-----------------------------------------------------------
|
||||
/// @name Clearing The Plot
|
||||
///-----------------------------------------------------------
|
||||
|
||||
/**
|
||||
Clears all data from the audio plot (includes both EZPlotTypeBuffer and EZPlotTypeRolling)
|
||||
*/
|
||||
-(void)clear;
|
||||
|
||||
#pragma mark - Get Samples
|
||||
///-----------------------------------------------------------
|
||||
/// @name Updating The Plot
|
||||
|
||||
+8
-1
@@ -31,9 +31,16 @@
|
||||
|
||||
@implementation EZPlot
|
||||
|
||||
#pragma mark - Clearing
|
||||
-(void)clear
|
||||
{
|
||||
// Override in subclass
|
||||
}
|
||||
|
||||
#pragma mark - Get Samples
|
||||
-(void)updateBuffer:(float *)buffer
|
||||
withBufferSize:(UInt32)bufferSize {
|
||||
withBufferSize:(UInt32)bufferSize
|
||||
{
|
||||
// Override in subclass
|
||||
}
|
||||
|
||||
|
||||
+45
-19
@@ -26,12 +26,32 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <AudioToolbox/AudioToolbox.h>
|
||||
|
||||
/**
|
||||
To ensure valid recording formats are used when recording to a file the EZRecorderFileType describes the most common file types that a file can be encoded in. Each of these types can be used to output recordings as such:
|
||||
|
||||
EZRecorderFileTypeAIFF - .aif, .aiff, .aifc, .aac
|
||||
EZRecorderFileTypeM4A - .m4a, .mp4
|
||||
EZRecorderFileTypeWAV - .wav
|
||||
|
||||
*/
|
||||
typedef NS_ENUM(NSInteger, EZRecorderFileType)
|
||||
{
|
||||
/**
|
||||
Recording format that describes AIFF file types. These are uncompressed, LPCM files that are completely lossless, but are large in file size.
|
||||
*/
|
||||
EZRecorderFileTypeAIFF,
|
||||
/**
|
||||
Recording format that describes M4A file types. These are compressed, but yield great results especially when file size is an issue.
|
||||
*/
|
||||
EZRecorderFileTypeM4A,
|
||||
/**
|
||||
Recording format that describes WAV file types. These are uncompressed, LPCM files that are completely lossless, but are large in file size.
|
||||
*/
|
||||
EZRecorderFileTypeWAV
|
||||
};
|
||||
|
||||
/**
|
||||
The EZRecorder provides a flexible way to create an audio file and append raw audio data to it. The EZRecorder will convert the incoming audio on the fly to the destination format so no conversion is needed between this and any other component. Right now the only supported output format is 'caf'. Each output file should have its own EZRecorder instance (think 1 EZRecorder = 1 audio file).
|
||||
|
||||
#Future Plans#
|
||||
Extend EZRecorder to allow any destination AudioStreamBasicDescription and any file extension.
|
||||
|
||||
*/
|
||||
@interface EZRecorder : NSObject
|
||||
|
||||
@@ -44,10 +64,12 @@
|
||||
Creates a new instance of an EZRecorder using a destination file path URL and the source format of the incoming audio.
|
||||
@param url An NSURL specifying the file path location of where the audio file should be written to.
|
||||
@param sourceFormat The AudioStreamBasicDescription for the incoming audio that will be written to the file.
|
||||
@param destinationFileType A constant described by the EZRecorderFileType that corresponds to the type of destination file that should be written. For instance, an AAC file written using an '.m4a' extension would correspond to EZRecorderFileTypeM4A. See EZRecorderFileType for all the constants and mapping combinations.
|
||||
@return The newly created EZRecorder instance.
|
||||
*/
|
||||
-(EZRecorder*)initWithDestinationURL:(NSURL*)url
|
||||
andSourceFormat:(AudioStreamBasicDescription)sourceFormat;
|
||||
sourceFormat:(AudioStreamBasicDescription)sourceFormat
|
||||
destinationFileType:(EZRecorderFileType)destinationFileType;
|
||||
|
||||
|
||||
#pragma mark - Class Initializers
|
||||
@@ -59,27 +81,22 @@
|
||||
Class method to create a new instance of an EZRecorder using a destination file path URL and the source format of the incoming audio.
|
||||
@param url An NSURL specifying the file path location of where the audio file should be written to.
|
||||
@param sourceFormat The AudioStreamBasicDescription for the incoming audio that will be written to the file.
|
||||
@param destinationFileType A constant described by the EZRecorderFileType that corresponds to the type of destination file that should be written. For instance, an AAC file written using an '.m4a' extension would correspond to EZRecorderFileTypeM4A. See EZRecorderFileType for all the constants and mapping combinations.
|
||||
@return The newly created EZRecorder instance.
|
||||
*/
|
||||
+(EZRecorder*)recorderWithDestinationURL:(NSURL*)url
|
||||
andSourceFormat:(AudioStreamBasicDescription)sourceFormat;
|
||||
sourceFormat:(AudioStreamBasicDescription)sourceFormat
|
||||
destinationFileType:(EZRecorderFileType)destinationFileType;
|
||||
|
||||
#pragma mark - Class Methods
|
||||
#pragma mark - Getters
|
||||
///-----------------------------------------------------------
|
||||
/// @name Class Methods
|
||||
/// @name Getting The Recorder's Properties
|
||||
///-----------------------------------------------------------
|
||||
|
||||
/**
|
||||
Class method returning the format used for the output file.
|
||||
@return An AudioStreamBasicDescription describing the output file's format.
|
||||
Provides the file path that's currently being used by the recorder.
|
||||
@return The NSURL representing the file path of the audio file path being used for recording.
|
||||
*/
|
||||
+(AudioStreamBasicDescription)defaultDestinationFormat;
|
||||
|
||||
/**
|
||||
Class method returning the default format extension to use for output audio file (caf).
|
||||
@return An NSString representing the default output audio file's extension @"caf"
|
||||
*/
|
||||
+(NSString*)defaultDestinationFormatExtension;
|
||||
-(NSURL*)url;
|
||||
|
||||
#pragma mark - Events
|
||||
///-----------------------------------------------------------
|
||||
@@ -94,4 +111,13 @@
|
||||
-(void)appendDataFromBufferList:(AudioBufferList*)bufferList
|
||||
withBufferSize:(UInt32)bufferSize;
|
||||
|
||||
@end
|
||||
///-----------------------------------------------------------
|
||||
/// @name Closing The Audio File
|
||||
///-----------------------------------------------------------
|
||||
|
||||
/**
|
||||
Finishes writes to the audio file and closes it.
|
||||
*/
|
||||
-(void)closeAudioFile;
|
||||
|
||||
@end
|
||||
+136
-120
@@ -28,150 +28,166 @@
|
||||
#import "EZAudio.h"
|
||||
|
||||
@interface EZRecorder (){
|
||||
AudioConverterRef _audioConverter;
|
||||
AudioStreamBasicDescription _clientFormat;
|
||||
ExtAudioFileRef _destinationFile;
|
||||
CFURLRef _destinationFileURL;
|
||||
AudioStreamBasicDescription _destinationFormat;
|
||||
AudioStreamBasicDescription _sourceFormat;
|
||||
ExtAudioFileRef _destinationFile;
|
||||
AudioFileTypeID _destinationFileTypeID;
|
||||
CFURLRef _destinationFileURL;
|
||||
AudioStreamBasicDescription _destinationFormat;
|
||||
AudioStreamBasicDescription _sourceFormat;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
AudioBufferList *sourceBuffer;
|
||||
} EZRecorderConverterStruct;
|
||||
|
||||
@end
|
||||
|
||||
@implementation EZRecorder
|
||||
|
||||
#pragma mark - Initializers
|
||||
-(EZRecorder*)initWithDestinationURL:(NSURL*)url
|
||||
andSourceFormat:(AudioStreamBasicDescription)sourceFormat {
|
||||
self = [super init];
|
||||
if(self){
|
||||
_destinationFileURL = (__bridge CFURLRef)url;
|
||||
_sourceFormat = sourceFormat;
|
||||
_destinationFormat = [EZRecorder defaultDestinationFormat];
|
||||
[self _configureRecorder];
|
||||
}
|
||||
return self;
|
||||
sourceFormat:(AudioStreamBasicDescription)sourceFormat
|
||||
destinationFileType:(EZRecorderFileType)destinationFileType
|
||||
{
|
||||
self = [super init];
|
||||
if( self )
|
||||
{
|
||||
// Set defaults
|
||||
_destinationFile = NULL;
|
||||
_destinationFileURL = (__bridge CFURLRef)url;
|
||||
_sourceFormat = sourceFormat;
|
||||
_destinationFormat = [EZRecorder recorderFormatForFileType:destinationFileType
|
||||
withSourceFormat:_sourceFormat];
|
||||
_destinationFileTypeID = [EZRecorder recorderFileTypeIdForFileType:destinationFileType
|
||||
withSourceFormat:_sourceFormat];
|
||||
|
||||
// Initializer the recorder instance
|
||||
[self _initializeRecorder];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Class Initializers
|
||||
+(EZRecorder*)recorderWithDestinationURL:(NSURL*)url
|
||||
andSourceFormat:(AudioStreamBasicDescription)sourceFormat {
|
||||
return [[EZRecorder alloc] initWithDestinationURL:url
|
||||
andSourceFormat:sourceFormat];
|
||||
sourceFormat:(AudioStreamBasicDescription)sourceFormat
|
||||
destinationFileType:(EZRecorderFileType)destinationFileType
|
||||
{
|
||||
return [[EZRecorder alloc] initWithDestinationURL:url
|
||||
sourceFormat:sourceFormat
|
||||
destinationFileType:destinationFileType];
|
||||
}
|
||||
|
||||
#pragma mark - Class Format Helper
|
||||
+(AudioStreamBasicDescription)defaultDestinationFormat {
|
||||
AudioStreamBasicDescription destinationFormat = [EZAudio stereoFloatInterleavedFormatWithSampleRate:44100.0];
|
||||
return destinationFormat;
|
||||
#pragma mark - Private Configuration
|
||||
+(AudioStreamBasicDescription)recorderFormatForFileType:(EZRecorderFileType)fileType
|
||||
withSourceFormat:(AudioStreamBasicDescription)sourceFormat
|
||||
{
|
||||
AudioStreamBasicDescription asbd;
|
||||
switch ( fileType )
|
||||
{
|
||||
case EZRecorderFileTypeAIFF:
|
||||
asbd = [EZAudio AIFFFormatWithNumberOfChannels:sourceFormat.mChannelsPerFrame
|
||||
sampleRate:sourceFormat.mSampleRate];
|
||||
break;
|
||||
case EZRecorderFileTypeM4A:
|
||||
asbd = [EZAudio M4AFormatWithNumberOfChannels:sourceFormat.mChannelsPerFrame
|
||||
sampleRate:sourceFormat.mSampleRate];
|
||||
break;
|
||||
|
||||
case EZRecorderFileTypeWAV:
|
||||
asbd = [EZAudio stereoFloatInterleavedFormatWithSampleRate:sourceFormat.mSampleRate];
|
||||
break;
|
||||
|
||||
default:
|
||||
asbd = [EZAudio stereoCanonicalNonInterleavedFormatWithSampleRate:sourceFormat.mSampleRate];
|
||||
break;
|
||||
}
|
||||
return asbd;
|
||||
}
|
||||
|
||||
+(NSString *)defaultDestinationFormatExtension {
|
||||
return @"caf";
|
||||
+(AudioFileTypeID)recorderFileTypeIdForFileType:(EZRecorderFileType)fileType
|
||||
withSourceFormat:(AudioStreamBasicDescription)sourceFormat
|
||||
{
|
||||
AudioFileTypeID audioFileTypeID;
|
||||
switch ( fileType )
|
||||
{
|
||||
case EZRecorderFileTypeAIFF:
|
||||
audioFileTypeID = kAudioFileAIFFType;
|
||||
break;
|
||||
|
||||
case EZRecorderFileTypeM4A:
|
||||
audioFileTypeID = kAudioFileM4AType;
|
||||
break;
|
||||
|
||||
case EZRecorderFileTypeWAV:
|
||||
audioFileTypeID = kAudioFileWAVEType;
|
||||
break;
|
||||
|
||||
default:
|
||||
audioFileTypeID = kAudioFileWAVEType;
|
||||
break;
|
||||
}
|
||||
return audioFileTypeID;
|
||||
}
|
||||
|
||||
#pragma mark - Private Configuation
|
||||
-(void)_configureRecorderForExistingFile {
|
||||
|
||||
}
|
||||
|
||||
-(void)_configureRecorderForNewFile {
|
||||
|
||||
}
|
||||
|
||||
-(void)_configureRecorder {
|
||||
|
||||
// Create the extended audio file
|
||||
[EZAudio checkResult:ExtAudioFileCreateWithURL(_destinationFileURL,
|
||||
kAudioFileCAFType,
|
||||
&_destinationFormat,
|
||||
NULL,
|
||||
kAudioFileFlags_EraseFile,
|
||||
&_destinationFile)
|
||||
operation:"Could not open audio file"];
|
||||
|
||||
// Set the client format
|
||||
_clientFormat = _destinationFormat;
|
||||
if( _destinationFormat.mFormatID != kAudioFormatLinearPCM ){
|
||||
[EZAudio setCanonicalAudioStreamBasicDescription:_destinationFormat
|
||||
numberOfChannels:_destinationFormat.mChannelsPerFrame
|
||||
interleaved:YES];
|
||||
}
|
||||
UInt32 propertySize = sizeof(_clientFormat);
|
||||
[EZAudio checkResult:ExtAudioFileSetProperty(_destinationFile,
|
||||
kExtAudioFileProperty_ClientDataFormat,
|
||||
propertySize,
|
||||
&_clientFormat)
|
||||
operation:"Failed to set client data format on destination file"];
|
||||
|
||||
// Instantiate the writer
|
||||
[EZAudio checkResult:ExtAudioFileWriteAsync(_destinationFile, 0, NULL)
|
||||
operation:"Failed to initialize with ExtAudioFileWriteAsync"];
|
||||
|
||||
// Setup the audio converter
|
||||
[EZAudio checkResult:AudioConverterNew(&_sourceFormat, &_destinationFormat, &_audioConverter)
|
||||
operation:"Failed to create new audio converter"];
|
||||
|
||||
-(void)_initializeRecorder
|
||||
{
|
||||
// Finish filling out the destination format description
|
||||
UInt32 propSize = sizeof(_destinationFormat);
|
||||
[EZAudio checkResult:AudioFormatGetProperty(kAudioFormatProperty_FormatInfo,
|
||||
0,
|
||||
NULL,
|
||||
&propSize,
|
||||
&_destinationFormat)
|
||||
operation:"Failed to fill out rest of destination format"];
|
||||
|
||||
// Create the audio file
|
||||
[EZAudio checkResult:ExtAudioFileCreateWithURL(_destinationFileURL,
|
||||
_destinationFileTypeID,
|
||||
&_destinationFormat,
|
||||
NULL,
|
||||
kAudioFileFlags_EraseFile,
|
||||
&_destinationFile)
|
||||
operation:"Failed to create audio file"];
|
||||
|
||||
// Set the client format (which should be equal to the source format)
|
||||
[EZAudio checkResult:ExtAudioFileSetProperty(_destinationFile,
|
||||
kExtAudioFileProperty_ClientDataFormat,
|
||||
sizeof(_sourceFormat),
|
||||
&_sourceFormat)
|
||||
operation:"Failed to set client format on recorded audio file"];
|
||||
|
||||
}
|
||||
|
||||
#pragma mark - Events
|
||||
-(void)appendDataFromBufferList:(AudioBufferList*)bufferList
|
||||
withBufferSize:(UInt32)bufferSize {
|
||||
|
||||
// Setup output buffers
|
||||
UInt32 outputBufferSize = 32 * 1024; // 32 KB
|
||||
AudioBufferList *convertedData = [EZAudio audioBufferList];
|
||||
convertedData->mNumberBuffers = 1;
|
||||
convertedData->mBuffers[0].mNumberChannels = _clientFormat.mChannelsPerFrame;
|
||||
convertedData->mBuffers[0].mDataByteSize = outputBufferSize*_clientFormat.mChannelsPerFrame;
|
||||
convertedData->mBuffers[0].mData = (AudioUnitSampleType*)malloc(sizeof(AudioUnitSampleType)*outputBufferSize*_clientFormat.mChannelsPerFrame);
|
||||
|
||||
[EZAudio checkResult:AudioConverterFillComplexBuffer(_audioConverter,
|
||||
complexInputDataProc,
|
||||
&(EZRecorderConverterStruct){ .sourceBuffer = bufferList },
|
||||
&bufferSize,
|
||||
convertedData,
|
||||
NULL)
|
||||
operation:"Failed while converting buffers"];
|
||||
|
||||
// Write the destination audio buffer list into t
|
||||
[EZAudio checkResult:ExtAudioFileWriteAsync(_destinationFile,bufferSize,convertedData)
|
||||
operation:"Failed to write audio data to file"];
|
||||
|
||||
// Free resources
|
||||
[EZAudio freeBufferList:convertedData];
|
||||
|
||||
-(void)appendDataFromBufferList:(AudioBufferList *)bufferList
|
||||
withBufferSize:(UInt32)bufferSize
|
||||
{
|
||||
if( _destinationFile )
|
||||
{
|
||||
[EZAudio checkResult:ExtAudioFileWriteAsync(_destinationFile,
|
||||
bufferSize,
|
||||
bufferList)
|
||||
operation:"Failed to write audio data to recorded audio file"];
|
||||
}
|
||||
}
|
||||
|
||||
static OSStatus complexInputDataProc(AudioConverterRef inAudioConverter,
|
||||
UInt32 *ioNumberDataPackets,
|
||||
AudioBufferList *ioData,
|
||||
AudioStreamPacketDescription **outDataPacketDescription,
|
||||
void *inUserData) {
|
||||
EZRecorderConverterStruct *recorderStruct = (EZRecorderConverterStruct*)inUserData;
|
||||
|
||||
if ( !recorderStruct->sourceBuffer ) {
|
||||
return -2222; // No More Data
|
||||
}
|
||||
|
||||
memcpy(ioData,
|
||||
recorderStruct->sourceBuffer,
|
||||
sizeof(AudioBufferList)+(recorderStruct->sourceBuffer->mNumberBuffers-1)*sizeof(AudioBuffer));
|
||||
recorderStruct->sourceBuffer = NULL;
|
||||
|
||||
return noErr;
|
||||
-(void)closeAudioFile
|
||||
{
|
||||
if( _destinationFile )
|
||||
{
|
||||
// Dispose of the audio file reference
|
||||
[EZAudio checkResult:ExtAudioFileDispose(_destinationFile)
|
||||
operation:"Failed to close audio file"];
|
||||
|
||||
// Null out the file reference
|
||||
_destinationFile = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Cleanup
|
||||
-(void)dealloc {
|
||||
[EZAudio checkResult:AudioConverterDispose(_audioConverter)
|
||||
operation:"Failed to dispose audio converter in recorder"];
|
||||
[EZAudio checkResult:ExtAudioFileDispose(_destinationFile)
|
||||
operation:"Failed to dispose extended audio file in recorder"];
|
||||
-(NSURL *)url
|
||||
{
|
||||
return (__bridge NSURL*)_destinationFileURL;
|
||||
}
|
||||
|
||||
@end
|
||||
#pragma mark - Dealloc
|
||||
-(void)dealloc
|
||||
{
|
||||
[self closeAudioFile];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -46,7 +46,7 @@ bool TPCircularBufferInit(TPCircularBuffer *buffer, int length) {
|
||||
int retries = 3;
|
||||
while ( true ) {
|
||||
|
||||
buffer->length = round_page(length); // We need whole page sizes
|
||||
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
|
||||
|
||||
@@ -40,3 +40,12 @@ Added more memory cleanup for EZAudioFile
|
||||
Added adjustable rolling length for EZAudioPlot and EZAudioPlotGL so those rolling graphs can now range from 128 to 8192 whereas before it was fixed at 1024
|
||||
Added adjustable resolution for waveform data coming from the EZAudioFile so output from the getWaveformDataWithCompletionBlock: function can literally be of any size. Try 128 for a low resolution waveform or 8192 for a much higher resolution waveform.
|
||||
Added quick fix for EZOutput to properly route stereo data coming from a circular buffer datasource. Next version (0.0.4) needs to add EZConverter to allow quick conversions between non-interleaved and interleaved formats.
|
||||
|
||||
0.0.4
|
||||
Added ‘closeAudioFile’ to EZRecorder to properly dispose of internal audio file prior to trying to reload it using the EZAudioFile.
|
||||
Added new EZOutputDataSource method that provides pre-allocated AudioBufferList to fill instead of caller allocating and disposing on AudioBufferList. Much less errors.
|
||||
Added EZAudioPlayer for playback and visualization of local audio files (no network streaming yet).
|
||||
Merged bug fixes from community for EZAudio file.
|
||||
|
||||
0.0.5
|
||||
Added multiple destination recording formats to the EZRecorder (EZRecorderFileType)
|
||||
|
||||
@@ -23,4 +23,4 @@ THE SOFTWARE.
|
||||
|
||||
==========================================================================================
|
||||
|
||||
0.0.2
|
||||
0.0.5
|
||||
|
||||
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
+53
@@ -6,6 +6,20 @@
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXAggregateTarget section */
|
||||
94F8DF4A18C84203005C4CBD /* Generate Documentation */ = {
|
||||
isa = PBXAggregateTarget;
|
||||
buildConfigurationList = 94F8DF4D18C84204005C4CBD /* Build configuration list for PBXAggregateTarget "Generate Documentation" */;
|
||||
buildPhases = (
|
||||
94F8DF4E18C8420C005C4CBD /* ShellScript */,
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = "Generate Documentation";
|
||||
productName = "Generate Documentation";
|
||||
};
|
||||
/* End PBXAggregateTarget section */
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
94056D88185B97E300EB94BA /* CoreGraphicsWaveformViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 94056D86185B97E300EB94BA /* CoreGraphicsWaveformViewController.m */; };
|
||||
94056D89185B97E300EB94BA /* CoreGraphicsWaveformViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 94056D87185B97E300EB94BA /* CoreGraphicsWaveformViewController.xib */; };
|
||||
@@ -331,6 +345,7 @@
|
||||
targets = (
|
||||
94373020185B931C00F315F0 /* EZAudioCoreGraphicsWaveformExample */,
|
||||
94373041185B931C00F315F0 /* EZAudioCoreGraphicsWaveformExampleTests */,
|
||||
94F8DF4A18C84203005C4CBD /* Generate Documentation */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
@@ -360,6 +375,22 @@
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
94F8DF4E18C8420C005C4CBD /* ShellScript */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "/usr/local/bin/appledoc \\\n--project-name \"EZAudio\" \\\n--project-company \"Syed Haris Ali\" \\\n--company-id \"com.sha\" \\\n--output documentation \\\n--create-docset \\\n--install-docset \\\n--create-html \\\n--exit-threshold 2 \\\n--no-repeat-first-par \\\n/../EZAudio";
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
9437301D185B931C00F315F0 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
@@ -570,6 +601,20 @@
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
94F8DF4B18C84204005C4CBD /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
94F8DF4C18C84204005C4CBD /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
@@ -600,6 +645,14 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
94F8DF4D18C84204005C4CBD /* Build configuration list for PBXAggregateTarget "Generate Documentation" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
94F8DF4B18C84204005C4CBD /* Debug */,
|
||||
94F8DF4C18C84204005C4CBD /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 94373019185B931C00F315F0 /* Project object */;
|
||||
|
||||
+5
-5
@@ -10,29 +10,29 @@
|
||||
<string>EZAudioExamplesOSX</string>
|
||||
<key>IDESourceControlProjectOriginsDictionary</key>
|
||||
<dict>
|
||||
<key>F77EC8D9-F815-4829-9274-5DA08EA98D6B</key>
|
||||
<key>9D6FF97A89F512CD81EAE1A971A1D2EB03E03F7C</key>
|
||||
<string>https://github.com/syedhali/EZAudio.git</string>
|
||||
</dict>
|
||||
<key>IDESourceControlProjectPath</key>
|
||||
<string>EZAudioExamples/OSX/EZAudioExamplesOSX.xcworkspace</string>
|
||||
<key>IDESourceControlProjectRelativeInstallPathDictionary</key>
|
||||
<dict>
|
||||
<key>F77EC8D9-F815-4829-9274-5DA08EA98D6B</key>
|
||||
<key>9D6FF97A89F512CD81EAE1A971A1D2EB03E03F7C</key>
|
||||
<string>../../..</string>
|
||||
</dict>
|
||||
<key>IDESourceControlProjectURL</key>
|
||||
<string>https://github.com/syedhali/EZAudio.git</string>
|
||||
<key>IDESourceControlProjectVersion</key>
|
||||
<integer>110</integer>
|
||||
<integer>111</integer>
|
||||
<key>IDESourceControlProjectWCCIdentifier</key>
|
||||
<string>F77EC8D9-F815-4829-9274-5DA08EA98D6B</string>
|
||||
<string>9D6FF97A89F512CD81EAE1A971A1D2EB03E03F7C</string>
|
||||
<key>IDESourceControlProjectWCConfigurations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>F77EC8D9-F815-4829-9274-5DA08EA98D6B</string>
|
||||
<string>9D6FF97A89F512CD81EAE1A971A1D2EB03E03F7C</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string>EZAudio</string>
|
||||
</dict>
|
||||
|
||||
BIN
Binary file not shown.
-1
@@ -30,7 +30,6 @@
|
||||
|
||||
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
|
||||
{
|
||||
|
||||
// Swap in our view controller in the window's content view
|
||||
self.openGLWaveformViewController = [[OpenGLWaveformViewController alloc] init];
|
||||
// Resize view controller to content view's current size
|
||||
|
||||
+5
-8
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="4514" systemVersion="13B42" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6185.11" systemVersion="13E28" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<dependencies>
|
||||
<deployment version="1070" identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="4514"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6185.11"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="OpenGLWaveformViewController">
|
||||
@@ -12,18 +12,16 @@
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<customView id="Zy4-iU-8jm">
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="272"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<customView translatesAutoresizingMaskIntoConstraints="NO" id="sjT-Ri-IOJ" customClass="EZAudioPlotGL">
|
||||
<rect key="frame" x="0.0" y="42" width="480" height="230"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
</customView>
|
||||
<button translatesAutoresizingMaskIntoConstraints="NO" id="0kM-1N-88d">
|
||||
<rect key="frame" x="18" y="14" width="119" height="18"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="14" id="Da9-ZF-OaJ"/>
|
||||
</constraints>
|
||||
@@ -37,11 +35,10 @@
|
||||
</button>
|
||||
<segmentedControl verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="M6h-53-a8o">
|
||||
<rect key="frame" x="333" y="11" width="129" height="24"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="21" id="a5H-w4-lUa"/>
|
||||
</constraints>
|
||||
<segmentedCell key="cell" alignment="left" style="rounded" trackingMode="selectOne" id="NWl-cZ-1Qx">
|
||||
<segmentedCell key="cell" borderStyle="border" alignment="left" style="rounded" trackingMode="selectOne" id="NWl-cZ-1Qx">
|
||||
<font key="font" metaFont="system"/>
|
||||
<segments>
|
||||
<segment label="Buffer" selected="YES"/>
|
||||
@@ -66,4 +63,4 @@
|
||||
</constraints>
|
||||
</customView>
|
||||
</objects>
|
||||
</document>
|
||||
</document>
|
||||
|
||||
Binary file not shown.
+18
-12
@@ -7,6 +7,9 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
668E4F821A905DAF00F4B814 /* EZAudioConverter.m in Sources */ = {isa = PBXBuildFile; fileRef = 668E4F811A905DAF00F4B814 /* EZAudioConverter.m */; };
|
||||
668E4F851A905F8700F4B814 /* EZAudioWaveformData.m in Sources */ = {isa = PBXBuildFile; fileRef = 668E4F841A905F8700F4B814 /* EZAudioWaveformData.m */; };
|
||||
668E4F881A90607500F4B814 /* EZMicrophone.m in Sources */ = {isa = PBXBuildFile; fileRef = 668E4F871A90607500F4B814 /* EZMicrophone.m */; };
|
||||
94056EFB185BD83400EB94BA /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 94056EFA185BD83400EB94BA /* Cocoa.framework */; };
|
||||
94056F05185BD83400EB94BA /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 94056F03185BD83400EB94BA /* InfoPlist.strings */; };
|
||||
94056F07185BD83400EB94BA /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 94056F06185BD83400EB94BA /* main.m */; };
|
||||
@@ -27,13 +30,11 @@
|
||||
94056F66185BDB4700EB94BA /* AudioUnit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 94056F63185BDB4700EB94BA /* AudioUnit.framework */; };
|
||||
94056F67185BDB4700EB94BA /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 94056F64185BDB4700EB94BA /* CoreAudio.framework */; };
|
||||
9417A6D51865928C00D9D37B /* simple-drum-beat.wav in Resources */ = {isa = PBXBuildFile; fileRef = 9417A6D41865928C00D9D37B /* simple-drum-beat.wav */; };
|
||||
9417A73E1867DD3400D9D37B /* AEFloatConverter.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A7261867DD3400D9D37B /* AEFloatConverter.m */; };
|
||||
9417A73F1867DD3400D9D37B /* EZAudio.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A7281867DD3400D9D37B /* EZAudio.m */; };
|
||||
9417A7401867DD3400D9D37B /* EZAudioFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A72A1867DD3400D9D37B /* EZAudioFile.m */; };
|
||||
9417A7411867DD3400D9D37B /* EZAudioPlot.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A72C1867DD3400D9D37B /* EZAudioPlot.m */; };
|
||||
9417A7421867DD3400D9D37B /* EZAudioPlotGL.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A72E1867DD3400D9D37B /* EZAudioPlotGL.m */; };
|
||||
9417A7431867DD3400D9D37B /* EZAudioPlotGLKViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A7301867DD3400D9D37B /* EZAudioPlotGLKViewController.m */; };
|
||||
9417A7441867DD3400D9D37B /* EZMicrophone.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A7321867DD3400D9D37B /* EZMicrophone.m */; };
|
||||
9417A7451867DD3400D9D37B /* EZOutput.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A7341867DD3400D9D37B /* EZOutput.m */; };
|
||||
9417A7461867DD3400D9D37B /* EZPlot.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A7361867DD3400D9D37B /* EZPlot.m */; };
|
||||
9417A7471867DD3400D9D37B /* EZRecorder.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A7381867DD3400D9D37B /* EZRecorder.m */; };
|
||||
@@ -53,6 +54,12 @@
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
668E4F801A905DAF00F4B814 /* EZAudioConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioConverter.h; sourceTree = "<group>"; };
|
||||
668E4F811A905DAF00F4B814 /* EZAudioConverter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioConverter.m; sourceTree = "<group>"; };
|
||||
668E4F831A905F8700F4B814 /* EZAudioWaveformData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioWaveformData.h; sourceTree = "<group>"; };
|
||||
668E4F841A905F8700F4B814 /* EZAudioWaveformData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioWaveformData.m; sourceTree = "<group>"; };
|
||||
668E4F861A90607500F4B814 /* EZMicrophone.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZMicrophone.h; sourceTree = "<group>"; };
|
||||
668E4F871A90607500F4B814 /* EZMicrophone.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZMicrophone.m; sourceTree = "<group>"; };
|
||||
94056EF7185BD83400EB94BA /* EZAudioPlayFileExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = EZAudioPlayFileExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
94056EFA185BD83400EB94BA /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
|
||||
94056EFD185BD83400EB94BA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
|
||||
@@ -82,8 +89,6 @@
|
||||
94056F63185BDB4700EB94BA /* AudioUnit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioUnit.framework; path = System/Library/Frameworks/AudioUnit.framework; sourceTree = SDKROOT; };
|
||||
94056F64185BDB4700EB94BA /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; };
|
||||
9417A6D41865928C00D9D37B /* simple-drum-beat.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; name = "simple-drum-beat.wav"; path = "../../../simple-drum-beat.wav"; sourceTree = "<group>"; };
|
||||
9417A7251867DD3400D9D37B /* AEFloatConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEFloatConverter.h; sourceTree = "<group>"; };
|
||||
9417A7261867DD3400D9D37B /* AEFloatConverter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AEFloatConverter.m; sourceTree = "<group>"; };
|
||||
9417A7271867DD3400D9D37B /* EZAudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudio.h; sourceTree = "<group>"; };
|
||||
9417A7281867DD3400D9D37B /* EZAudio.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudio.m; sourceTree = "<group>"; };
|
||||
9417A7291867DD3400D9D37B /* EZAudioFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioFile.h; sourceTree = "<group>"; };
|
||||
@@ -94,8 +99,6 @@
|
||||
9417A72E1867DD3400D9D37B /* EZAudioPlotGL.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioPlotGL.m; sourceTree = "<group>"; };
|
||||
9417A72F1867DD3400D9D37B /* EZAudioPlotGLKViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioPlotGLKViewController.h; sourceTree = "<group>"; };
|
||||
9417A7301867DD3400D9D37B /* EZAudioPlotGLKViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioPlotGLKViewController.m; sourceTree = "<group>"; };
|
||||
9417A7311867DD3400D9D37B /* EZMicrophone.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZMicrophone.h; sourceTree = "<group>"; };
|
||||
9417A7321867DD3400D9D37B /* EZMicrophone.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZMicrophone.m; sourceTree = "<group>"; };
|
||||
9417A7331867DD3400D9D37B /* EZOutput.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZOutput.h; sourceTree = "<group>"; };
|
||||
9417A7341867DD3400D9D37B /* EZOutput.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZOutput.m; sourceTree = "<group>"; };
|
||||
9417A7351867DD3400D9D37B /* EZPlot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZPlot.h; sourceTree = "<group>"; };
|
||||
@@ -230,10 +233,12 @@
|
||||
9417A7241867DD3400D9D37B /* EZAudio */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9417A7251867DD3400D9D37B /* AEFloatConverter.h */,
|
||||
9417A7261867DD3400D9D37B /* AEFloatConverter.m */,
|
||||
9417A7271867DD3400D9D37B /* EZAudio.h */,
|
||||
9417A7281867DD3400D9D37B /* EZAudio.m */,
|
||||
668E4F801A905DAF00F4B814 /* EZAudioConverter.h */,
|
||||
668E4F811A905DAF00F4B814 /* EZAudioConverter.m */,
|
||||
668E4F831A905F8700F4B814 /* EZAudioWaveformData.h */,
|
||||
668E4F841A905F8700F4B814 /* EZAudioWaveformData.m */,
|
||||
9417A7291867DD3400D9D37B /* EZAudioFile.h */,
|
||||
9417A72A1867DD3400D9D37B /* EZAudioFile.m */,
|
||||
9417A72B1867DD3400D9D37B /* EZAudioPlot.h */,
|
||||
@@ -242,8 +247,8 @@
|
||||
9417A72E1867DD3400D9D37B /* EZAudioPlotGL.m */,
|
||||
9417A72F1867DD3400D9D37B /* EZAudioPlotGLKViewController.h */,
|
||||
9417A7301867DD3400D9D37B /* EZAudioPlotGLKViewController.m */,
|
||||
9417A7311867DD3400D9D37B /* EZMicrophone.h */,
|
||||
9417A7321867DD3400D9D37B /* EZMicrophone.m */,
|
||||
668E4F861A90607500F4B814 /* EZMicrophone.h */,
|
||||
668E4F871A90607500F4B814 /* EZMicrophone.m */,
|
||||
9417A7331867DD3400D9D37B /* EZOutput.h */,
|
||||
9417A7341867DD3400D9D37B /* EZOutput.m */,
|
||||
9417A7351867DD3400D9D37B /* EZPlot.h */,
|
||||
@@ -371,16 +376,17 @@
|
||||
files = (
|
||||
9417A7481867DD3400D9D37B /* TPCircularBuffer.c in Sources */,
|
||||
9417A7451867DD3400D9D37B /* EZOutput.m in Sources */,
|
||||
668E4F821A905DAF00F4B814 /* EZAudioConverter.m in Sources */,
|
||||
9417A7431867DD3400D9D37B /* EZAudioPlotGLKViewController.m in Sources */,
|
||||
94056F31185BD86D00EB94BA /* PlayFileViewController.m in Sources */,
|
||||
9417A73E1867DD3400D9D37B /* AEFloatConverter.m in Sources */,
|
||||
94056F0E185BD83400EB94BA /* AppDelegate.m in Sources */,
|
||||
9417A7411867DD3400D9D37B /* EZAudioPlot.m in Sources */,
|
||||
9417A7401867DD3400D9D37B /* EZAudioFile.m in Sources */,
|
||||
668E4F881A90607500F4B814 /* EZMicrophone.m in Sources */,
|
||||
668E4F851A905F8700F4B814 /* EZAudioWaveformData.m in Sources */,
|
||||
9417A7421867DD3400D9D37B /* EZAudioPlotGL.m in Sources */,
|
||||
94056F07185BD83400EB94BA /* main.m in Sources */,
|
||||
9417A7461867DD3400D9D37B /* EZPlot.m in Sources */,
|
||||
9417A7441867DD3400D9D37B /* EZMicrophone.m in Sources */,
|
||||
9417A7471867DD3400D9D37B /* EZRecorder.m in Sources */,
|
||||
9417A73F1867DD3400D9D37B /* EZAudio.m in Sources */,
|
||||
);
|
||||
|
||||
+15
-1
@@ -27,6 +27,9 @@
|
||||
|
||||
// Import EZAudio header
|
||||
#import "EZAudio.h"
|
||||
#import "EZAudioFile.h"
|
||||
#import "EZOutput.h"
|
||||
#import "EZAudioPlot.h"
|
||||
|
||||
/**
|
||||
Here's the default audio file included with the example
|
||||
@@ -47,7 +50,8 @@
|
||||
/**
|
||||
The CoreGraphics based audio plot
|
||||
*/
|
||||
@property (nonatomic,weak) IBOutlet EZAudioPlotGL *audioPlot;
|
||||
@property (nonatomic,weak) IBOutlet EZAudioPlot *audioPlotLeft;
|
||||
@property (nonatomic,weak) IBOutlet EZAudioPlot *audioPlotRight;
|
||||
|
||||
#pragma mark - UI Extras
|
||||
/**
|
||||
@@ -60,12 +64,22 @@
|
||||
*/
|
||||
@property (nonatomic,weak) IBOutlet NSSlider *framePositionSlider;
|
||||
|
||||
/**
|
||||
A slider to adjust the sample rate.
|
||||
*/
|
||||
@property (nonatomic,weak) IBOutlet NSSlider *sampleRateSlider;
|
||||
|
||||
/**
|
||||
A BOOL indicating whether or not we've reached the end of the file
|
||||
*/
|
||||
@property (nonatomic,assign) BOOL eof;
|
||||
|
||||
#pragma mark - Actions
|
||||
/**
|
||||
Changes the sampling frequency on the output unit
|
||||
*/
|
||||
-(IBAction)changeOutputSamplingFrequency:(id)sender;
|
||||
|
||||
/**
|
||||
Switches the plot drawing type between a buffer plot (visualizes the current stream of audio data from the update function) or a rolling plot (visualizes the audio data over time, this is the classic waveform look)
|
||||
*/
|
||||
|
||||
+84
-66
@@ -32,7 +32,6 @@
|
||||
|
||||
@implementation PlayFileViewController
|
||||
@synthesize audioFile;
|
||||
@synthesize audioPlot;
|
||||
@synthesize eof = _eof;
|
||||
@synthesize framePositionSlider;
|
||||
|
||||
@@ -72,15 +71,20 @@
|
||||
Customizing the audio plot's look
|
||||
*/
|
||||
// Background color
|
||||
self.audioPlot.backgroundColor = [NSColor colorWithCalibratedRed: 0.816 green: 0.349 blue: 0.255 alpha: 1];
|
||||
self.audioPlotLeft.backgroundColor = [NSColor colorWithCalibratedRed:0.1 green:0.3 blue:0.2 alpha:1.0];
|
||||
self.audioPlotRight.backgroundColor = [NSColor colorWithCalibratedRed:0.1 green:0.3 blue:0.2 alpha:1.0];
|
||||
// Waveform color
|
||||
self.audioPlot.color = [NSColor colorWithCalibratedRed: 1.000 green: 1.000 blue: 1.000 alpha: 1];
|
||||
self.audioPlotLeft.color = [NSColor colorWithCalibratedRed: 1.000 green: 1.000 blue: 1.000 alpha: 1];
|
||||
self.audioPlotRight.color = [NSColor colorWithCalibratedRed: 1.000 green: 1.000 blue: 1.000 alpha: 1];
|
||||
// Plot type
|
||||
self.audioPlot.plotType = EZPlotTypeBuffer;
|
||||
self.audioPlotLeft.plotType = EZPlotTypeBuffer;
|
||||
self.audioPlotRight.plotType = EZPlotTypeBuffer;
|
||||
// Fill
|
||||
self.audioPlot.shouldFill = YES;
|
||||
self.audioPlotLeft.shouldFill = YES;
|
||||
self.audioPlotRight.shouldFill = YES;
|
||||
// Mirror
|
||||
self.audioPlot.shouldMirror = YES;
|
||||
self.audioPlotLeft.shouldMirror = YES;
|
||||
self.audioPlotRight.shouldMirror = YES;
|
||||
|
||||
/*
|
||||
Try opening the sample file
|
||||
@@ -104,6 +108,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
-(void)changeOutputSamplingFrequency:(id)sender
|
||||
{
|
||||
AudioStreamBasicDescription asbd = [EZOutput sharedOutput].audioStreamBasicDescription;
|
||||
float samplingFrequency = ((NSSlider *)sender).floatValue;
|
||||
asbd.mSampleRate = samplingFrequency;
|
||||
[[EZOutput sharedOutput] setAudioStreamBasicDescription:asbd];
|
||||
}
|
||||
|
||||
-(void)openFile:(id)sender {
|
||||
NSOpenPanel* openDlg = [NSOpenPanel openPanel];
|
||||
openDlg.canChooseFiles = YES;
|
||||
@@ -111,6 +123,7 @@
|
||||
openDlg.delegate = self;
|
||||
if( [openDlg runModal] == NSOKButton ){
|
||||
NSArray *selectedFiles = [openDlg URLs];
|
||||
NSLog(@"selected files: %@", selectedFiles);
|
||||
[self openFileWithFilePathURL:selectedFiles.firstObject];
|
||||
}
|
||||
}
|
||||
@@ -120,10 +133,6 @@
|
||||
if( self.eof ){
|
||||
[self.audioFile seekToFrame:0];
|
||||
}
|
||||
if( self.audioPlot.plotType == EZPlotTypeBuffer &&
|
||||
self.audioPlot.shouldFill == YES ){
|
||||
self.audioPlot.plotType = EZPlotTypeRolling;
|
||||
}
|
||||
[EZOutput sharedOutput].outputDataSource = self;
|
||||
[[EZOutput sharedOutput] startPlayback];
|
||||
}
|
||||
@@ -143,11 +152,11 @@
|
||||
*/
|
||||
-(void)drawBufferPlot {
|
||||
// Change the plot type to the buffer plot
|
||||
self.audioPlot.plotType = EZPlotTypeBuffer;
|
||||
self.audioPlotLeft.plotType = EZPlotTypeBuffer;
|
||||
// Don't fill
|
||||
self.audioPlot.shouldFill = NO;
|
||||
self.audioPlotLeft.shouldFill = NO;
|
||||
// Don't mirror over the x-axis
|
||||
self.audioPlot.shouldMirror = NO;
|
||||
self.audioPlotLeft.shouldMirror = NO;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -155,19 +164,25 @@
|
||||
*/
|
||||
-(void)drawRollingPlot {
|
||||
// Change the plot type to the rolling plot
|
||||
self.audioPlot.plotType = EZPlotTypeRolling;
|
||||
self.audioPlotLeft.plotType = EZPlotTypeRolling;
|
||||
// Fill the waveform
|
||||
self.audioPlot.shouldFill = YES;
|
||||
self.audioPlotLeft.shouldFill = YES;
|
||||
// Mirror over the x-axis
|
||||
self.audioPlot.shouldMirror = YES;
|
||||
self.audioPlotLeft.shouldMirror = YES;
|
||||
}
|
||||
|
||||
-(void)openFileWithFilePathURL:(NSURL*)filePathURL {
|
||||
|
||||
// Stop playback
|
||||
[[EZOutput sharedOutput] stopPlayback];
|
||||
|
||||
self.audioFile = [EZAudioFile audioFileWithURL:filePathURL andDelegate:self];
|
||||
|
||||
AudioStreamBasicDescription asbd;
|
||||
self.audioFile = [EZAudioFile audioFileWithURL:filePathURL
|
||||
delegate:self
|
||||
permission:EZAudioFilePermissionRead
|
||||
fileFormat:asbd];
|
||||
[[EZOutput sharedOutput] setAudioStreamBasicDescription:self.audioFile.clientFormat];
|
||||
|
||||
self.eof = NO;
|
||||
self.filePathLabel.stringValue = filePathURL.lastPathComponent;
|
||||
self.framePositionSlider.minValue = 0.0f;
|
||||
@@ -176,33 +191,50 @@
|
||||
self.plotSegmentControl.selectedSegment = 1;
|
||||
|
||||
// Set the client format from the EZAudioFile on the output
|
||||
[[EZOutput sharedOutput] setAudioStreamBasicDescription:self.audioFile.clientFormat];
|
||||
|
||||
#pragma mark Mess Around With Audio Stream Basic Description Here!
|
||||
self.sampleRateSlider.floatValue = self.audioFile.clientFormat.mSampleRate;
|
||||
|
||||
// Plot the whole waveform
|
||||
self.audioPlot.plotType = EZPlotTypeBuffer;
|
||||
self.audioPlot.shouldFill = YES;
|
||||
self.audioPlot.shouldMirror = YES;
|
||||
[self.audioFile getWaveformDataWithCompletionBlock:^(float *waveformData, UInt32 length) {
|
||||
self.audioPlot.shouldFill = YES;
|
||||
self.audioPlot.shouldMirror = YES;
|
||||
[self.audioPlot updateBuffer:waveformData withBufferSize:length];
|
||||
self.audioPlotLeft.plotType = EZPlotTypeBuffer;
|
||||
self.audioPlotLeft.shouldFill = YES;
|
||||
self.audioPlotLeft.shouldMirror = YES;
|
||||
self.audioPlotRight.plotType = EZPlotTypeBuffer;
|
||||
self.audioPlotRight.shouldFill = YES;
|
||||
self.audioPlotRight.shouldMirror = YES;
|
||||
[self.audioFile getWaveformDataWithCompletionBlock:^(EZAudioWaveformData *data) {
|
||||
self.audioPlotLeft.shouldFill = YES;
|
||||
self.audioPlotLeft.shouldMirror = YES;
|
||||
self.audioPlotRight.shouldFill = YES;
|
||||
self.audioPlotRight.shouldMirror = YES;
|
||||
if (data.numberOfChannels > 1)
|
||||
{
|
||||
[self.audioPlotLeft updateBuffer:[data bufferForChannel:0] withBufferSize:data.bufferSize];
|
||||
[self.audioPlotRight updateBuffer:[data bufferForChannel:1] withBufferSize:data.bufferSize];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self.audioPlotLeft updateBuffer:[data bufferForChannel:0] withBufferSize:data.bufferSize];
|
||||
}
|
||||
}];
|
||||
|
||||
}
|
||||
|
||||
#pragma mark - EZAudioFileDelegate
|
||||
-(void)audioFile:(EZAudioFile *)audioFile readAudio:(float **)buffer withBufferSize:(UInt32)bufferSize withNumberOfChannels:(UInt32)numberOfChannels {
|
||||
if( [EZOutput sharedOutput].isPlaying ){
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if( self.audioPlot.plotType == EZPlotTypeBuffer &&
|
||||
self.audioPlot.shouldFill == YES &&
|
||||
self.audioPlot.shouldMirror == YES ){
|
||||
self.audioPlot.shouldFill = NO;
|
||||
self.audioPlot.shouldMirror = NO;
|
||||
}
|
||||
[self.audioPlot updateBuffer:buffer[0] withBufferSize:bufferSize];
|
||||
});
|
||||
}
|
||||
-(void)audioFile:(EZAudioFile *)audioFile
|
||||
readAudio:(float **)buffer
|
||||
withBufferSize:(UInt32)bufferSize
|
||||
withNumberOfChannels:(UInt32)numberOfChannels {
|
||||
// if( [EZOutput sharedOutput].isPlaying ){
|
||||
// dispatch_async(dispatch_get_main_queue(), ^{
|
||||
// if( self.audioPlotLeft.plotType == EZPlotTypeBuffer &&
|
||||
// self.audioPlotLeft.shouldFill == YES &&
|
||||
// self.audioPlotLeft.shouldMirror == YES ){
|
||||
// self.audioPlotLeft.shouldFill = NO;
|
||||
// self.audioPlotLeft.shouldMirror = NO;
|
||||
// }
|
||||
// [self.audioPlotLeft updateBuffer:buffer[0] withBufferSize:bufferSize];
|
||||
// });
|
||||
// }
|
||||
}
|
||||
|
||||
-(void)audioFile:(EZAudioFile *)audioFile
|
||||
@@ -215,36 +247,22 @@
|
||||
}
|
||||
|
||||
#pragma mark - EZOutputDataSource
|
||||
-(AudioBufferList *)output:(EZOutput *)output
|
||||
needsBufferListWithFrames:(UInt32)frames
|
||||
withBufferSize:(UInt32 *)bufferSize {
|
||||
if( self.audioFile ){
|
||||
|
||||
// Reached the end of the file
|
||||
if( self.eof ){
|
||||
// Here's what you do to loop the file
|
||||
[self.audioFile seekToFrame:0];
|
||||
self.eof = NO;
|
||||
}
|
||||
|
||||
// Allocate a buffer list to hold the file's data
|
||||
AudioBufferList *bufferList = [EZAudio audioBufferList];
|
||||
BOOL eof;
|
||||
-(void) output:(EZOutput*)output
|
||||
shouldFillAudioBufferList:(AudioBufferList*)audioBufferList
|
||||
withNumberOfFrames:(UInt32)frames
|
||||
{
|
||||
if( self.audioFile )
|
||||
{
|
||||
UInt32 bufferSize;
|
||||
[self.audioFile readFrames:frames
|
||||
audioBufferList:bufferList
|
||||
bufferSize:bufferSize
|
||||
eof:&eof];
|
||||
self.eof = eof;
|
||||
|
||||
// Reached the end of the file on the last read
|
||||
if( eof ){
|
||||
[EZAudio freeBufferList:bufferList];
|
||||
return nil;
|
||||
audioBufferList:audioBufferList
|
||||
bufferSize:&bufferSize
|
||||
eof:&_eof];
|
||||
if( _eof )
|
||||
{
|
||||
[self seekToFrame:0];
|
||||
}
|
||||
return bufferList;
|
||||
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
#pragma mark - NSOpenSavePanelDelegate
|
||||
|
||||
+59
-47
@@ -1,37 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="4514" systemVersion="13A603" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6254" systemVersion="14C109" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<dependencies>
|
||||
<deployment version="1070" identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="4514"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6254"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="PlayFileViewController">
|
||||
<connections>
|
||||
<outlet property="audioPlot" destination="Lz1-Gs-1lD" id="V5w-yH-ZVR"/>
|
||||
<outlet property="audioPlotLeft" destination="aHI-vj-Ccv" id="Pwl-P4-MyY"/>
|
||||
<outlet property="audioPlotRight" destination="Lz1-Gs-1lD" id="GKN-Pb-Ejy"/>
|
||||
<outlet property="filePathLabel" destination="0eT-7c-7fJ" id="IGv-mA-5Hw"/>
|
||||
<outlet property="framePositionSlider" destination="CFP-v0-TzQ" id="3oy-Xn-4JK"/>
|
||||
<outlet property="playButton" destination="OQp-Lr-dlS" id="K5R-Qg-7DY"/>
|
||||
<outlet property="plotSegmentControl" destination="bZW-tA-C61" id="4ic-Ou-qh2"/>
|
||||
<outlet property="sampleRateSlider" destination="rRH-oS-VV3" id="8ij-Ff-CZK"/>
|
||||
<outlet property="view" destination="Xpo-HP-Ost" id="zlj-bW-4iz"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<customView id="Xpo-HP-Ost">
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="272"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="421"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<customView translatesAutoresizingMaskIntoConstraints="NO" id="Lz1-Gs-1lD" customClass="EZAudioPlotGL">
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="148"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
</customView>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="2Ma-jj-U3z">
|
||||
<rect key="frame" x="14" y="224" width="125" height="32"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="21" id="Lm5-0d-A72"/>
|
||||
<constraint firstAttribute="width" constant="113" id="Tij-5V-y1Q"/>
|
||||
</constraints>
|
||||
<rect key="frame" x="14" y="373" width="125" height="32"/>
|
||||
<buttonCell key="cell" type="push" title="Choose File..." bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="KLq-bf-Xkh">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
@@ -40,9 +33,8 @@
|
||||
<action selector="openFile:" target="-2" id="3QB-hU-LDl"/>
|
||||
</connections>
|
||||
</button>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="0eT-7c-7fJ">
|
||||
<rect key="frame" x="141" y="235" width="321" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="0eT-7c-7fJ">
|
||||
<rect key="frame" x="141" y="384" width="38" height="17"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingMiddle" sendsActionOnEndEditing="YES" title="Label" id="vXQ-HF-vLX">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
@@ -50,12 +42,7 @@
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="OQp-Lr-dlS">
|
||||
<rect key="frame" x="14" y="191" width="125" height="32"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="21" id="geC-A9-48j"/>
|
||||
<constraint firstAttribute="width" constant="113" id="lc3-4H-QK6"/>
|
||||
</constraints>
|
||||
<rect key="frame" x="14" y="340" width="125" height="32"/>
|
||||
<buttonCell key="cell" type="push" title="Play" alternateTitle="Pause" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="Z2A-7U-sb6">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
@@ -65,9 +52,11 @@
|
||||
</connections>
|
||||
</button>
|
||||
<segmentedControl verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="bZW-tA-C61">
|
||||
<rect key="frame" x="333" y="196" width="129" height="24"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<segmentedCell key="cell" alignment="left" style="rounded" trackingMode="selectOne" id="8U1-ER-vPJ">
|
||||
<rect key="frame" x="333" y="345" width="129" height="24"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="125" id="3Yc-x7-gJk"/>
|
||||
</constraints>
|
||||
<segmentedCell key="cell" borderStyle="border" alignment="left" style="rounded" trackingMode="selectOne" id="8U1-ER-vPJ">
|
||||
<font key="font" metaFont="system"/>
|
||||
<segments>
|
||||
<segment label="Buffer" selected="YES"/>
|
||||
@@ -79,32 +68,55 @@
|
||||
</connections>
|
||||
</segmentedControl>
|
||||
<slider verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="CFP-v0-TzQ">
|
||||
<rect key="frame" x="18" y="159" width="444" height="21"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<sliderCell key="cell" alignment="left" maxValue="100" doubleValue="9.3380614657210401" tickMarkPosition="above" sliderType="linear" id="gPc-pN-dmP"/>
|
||||
<rect key="frame" x="18" y="308" width="444" height="20"/>
|
||||
<sliderCell key="cell" continuous="YES" alignment="left" maxValue="100" doubleValue="9.3380614657210401" tickMarkPosition="above" sliderType="linear" id="gPc-pN-dmP"/>
|
||||
<connections>
|
||||
<action selector="seekToFrame:" target="-2" id="iVY-so-6X2"/>
|
||||
</connections>
|
||||
</slider>
|
||||
<slider verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="rRH-oS-VV3">
|
||||
<rect key="frame" x="141" y="348" width="96" height="20"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="92" id="Ul6-1Z-zf6"/>
|
||||
</constraints>
|
||||
<sliderCell key="cell" state="on" alignment="left" minValue="8000" maxValue="88200" doubleValue="44100" tickMarkPosition="above" sliderType="linear" id="xbX-Ce-da5"/>
|
||||
<connections>
|
||||
<action selector="changeOutputSamplingFrequency:" target="-2" id="yWM-Ei-ztA"/>
|
||||
</connections>
|
||||
</slider>
|
||||
<customView translatesAutoresizingMaskIntoConstraints="NO" id="aHI-vj-Ccv" customClass="EZAudioPlot">
|
||||
<rect key="frame" x="0.0" y="148" width="480" height="148"/>
|
||||
</customView>
|
||||
<customView translatesAutoresizingMaskIntoConstraints="NO" id="Lz1-Gs-1lD" customClass="EZAudioPlot">
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="148"/>
|
||||
</customView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="CFP-v0-TzQ" firstAttribute="top" secondItem="OQp-Lr-dlS" secondAttribute="bottom" constant="20" id="6uf-rh-zEf"/>
|
||||
<constraint firstItem="0eT-7c-7fJ" firstAttribute="leading" secondItem="2Ma-jj-U3z" secondAttribute="trailing" constant="10" id="AcA-Rv-Lwl"/>
|
||||
<constraint firstItem="Lz1-Gs-1lD" firstAttribute="top" secondItem="CFP-v0-TzQ" secondAttribute="bottom" constant="13" id="JjU-ri-rxV"/>
|
||||
<constraint firstItem="0eT-7c-7fJ" firstAttribute="top" secondItem="Xpo-HP-Ost" secondAttribute="top" constant="20" id="T86-Jj-i0N"/>
|
||||
<constraint firstAttribute="bottom" secondItem="Lz1-Gs-1lD" secondAttribute="bottom" id="U2b-77-5uo"/>
|
||||
<constraint firstAttribute="trailing" secondItem="bZW-tA-C61" secondAttribute="trailing" constant="20" id="UGO-OL-Dmk"/>
|
||||
<constraint firstAttribute="trailing" secondItem="CFP-v0-TzQ" secondAttribute="trailing" constant="20" id="UpE-fD-Skp"/>
|
||||
<constraint firstItem="2Ma-jj-U3z" firstAttribute="top" secondItem="Xpo-HP-Ost" secondAttribute="top" constant="20" id="a1a-7J-lzc"/>
|
||||
<constraint firstItem="Lz1-Gs-1lD" firstAttribute="leading" secondItem="Xpo-HP-Ost" secondAttribute="leading" id="bZz-am-fqe"/>
|
||||
<constraint firstItem="2Ma-jj-U3z" firstAttribute="leading" secondItem="Xpo-HP-Ost" secondAttribute="leading" constant="20" id="dc1-KX-H5W"/>
|
||||
<constraint firstItem="OQp-Lr-dlS" firstAttribute="leading" secondItem="Xpo-HP-Ost" secondAttribute="leading" constant="20" id="g5e-2M-sHn"/>
|
||||
<constraint firstAttribute="trailing" secondItem="Lz1-Gs-1lD" secondAttribute="trailing" id="jDC-Iz-9c4"/>
|
||||
<constraint firstItem="bZW-tA-C61" firstAttribute="top" secondItem="0eT-7c-7fJ" secondAttribute="bottom" constant="16" id="ll5-1d-SaQ"/>
|
||||
<constraint firstItem="CFP-v0-TzQ" firstAttribute="leading" secondItem="Xpo-HP-Ost" secondAttribute="leading" constant="20" id="qhq-Io-tdF"/>
|
||||
<constraint firstItem="Lz1-Gs-1lD" firstAttribute="leading" secondItem="Xpo-HP-Ost" secondAttribute="leading" id="xtr-M9-Uot"/>
|
||||
<constraint firstItem="OQp-Lr-dlS" firstAttribute="top" secondItem="2Ma-jj-U3z" secondAttribute="bottom" constant="12" id="yGn-40-QqT"/>
|
||||
<constraint firstItem="CFP-v0-TzQ" firstAttribute="top" secondItem="OQp-Lr-dlS" secondAttribute="bottom" constant="21" id="2Xm-ZL-QU7"/>
|
||||
<constraint firstItem="rRH-oS-VV3" firstAttribute="leading" secondItem="0eT-7c-7fJ" secondAttribute="leading" id="3CU-am-fxR"/>
|
||||
<constraint firstItem="2Ma-jj-U3z" firstAttribute="leading" secondItem="Xpo-HP-Ost" secondAttribute="leading" constant="20" symbolic="YES" id="5ch-hJ-6Kp"/>
|
||||
<constraint firstItem="CFP-v0-TzQ" firstAttribute="trailing" secondItem="bZW-tA-C61" secondAttribute="trailing" id="7rr-w2-gvA"/>
|
||||
<constraint firstAttribute="trailing" secondItem="bZW-tA-C61" secondAttribute="trailing" constant="20" symbolic="YES" id="A17-rX-9Sa"/>
|
||||
<constraint firstItem="OQp-Lr-dlS" firstAttribute="top" secondItem="2Ma-jj-U3z" secondAttribute="bottom" constant="12" symbolic="YES" id="Fpt-Cg-5Ur"/>
|
||||
<constraint firstItem="OQp-Lr-dlS" firstAttribute="trailing" secondItem="2Ma-jj-U3z" secondAttribute="trailing" id="GoP-Jj-F0w"/>
|
||||
<constraint firstItem="Lz1-Gs-1lD" firstAttribute="top" secondItem="aHI-vj-Ccv" secondAttribute="bottom" id="Izh-a4-03r"/>
|
||||
<constraint firstItem="2Ma-jj-U3z" firstAttribute="top" secondItem="Xpo-HP-Ost" secondAttribute="top" constant="20" symbolic="YES" id="KHJ-an-Hqi"/>
|
||||
<constraint firstItem="0eT-7c-7fJ" firstAttribute="top" secondItem="2Ma-jj-U3z" secondAttribute="top" id="NAh-en-Pw9"/>
|
||||
<constraint firstItem="Lz1-Gs-1lD" firstAttribute="leading" secondItem="Xpo-HP-Ost" secondAttribute="leading" id="P2a-D3-NNl"/>
|
||||
<constraint firstItem="0eT-7c-7fJ" firstAttribute="leading" secondItem="2Ma-jj-U3z" secondAttribute="trailing" constant="10" id="PBC-BN-wJn"/>
|
||||
<constraint firstItem="aHI-vj-Ccv" firstAttribute="height" secondItem="Lz1-Gs-1lD" secondAttribute="height" id="RkJ-OA-oUM"/>
|
||||
<constraint firstItem="OQp-Lr-dlS" firstAttribute="leading" secondItem="2Ma-jj-U3z" secondAttribute="leading" id="YaB-GO-JhC"/>
|
||||
<constraint firstItem="Lz1-Gs-1lD" firstAttribute="trailing" secondItem="aHI-vj-Ccv" secondAttribute="trailing" id="Z3I-LF-Cja"/>
|
||||
<constraint firstItem="CFP-v0-TzQ" firstAttribute="centerX" secondItem="aHI-vj-Ccv" secondAttribute="centerX" id="Zdn-mM-g9p"/>
|
||||
<constraint firstItem="Lz1-Gs-1lD" firstAttribute="leading" secondItem="aHI-vj-Ccv" secondAttribute="leading" id="cTW-4H-R0G"/>
|
||||
<constraint firstItem="bZW-tA-C61" firstAttribute="top" secondItem="OQp-Lr-dlS" secondAttribute="top" id="jls-iH-yCV"/>
|
||||
<constraint firstItem="rRH-oS-VV3" firstAttribute="baseline" secondItem="OQp-Lr-dlS" secondAttribute="baseline" id="mHm-mA-sbt"/>
|
||||
<constraint firstItem="Lz1-Gs-1lD" firstAttribute="top" secondItem="Xpo-HP-Ost" secondAttribute="top" constant="273" id="oy9-te-LMx"/>
|
||||
<constraint firstAttribute="bottom" secondItem="Lz1-Gs-1lD" secondAttribute="bottom" id="sl1-b2-YvQ"/>
|
||||
<constraint firstItem="OQp-Lr-dlS" firstAttribute="leading" secondItem="CFP-v0-TzQ" secondAttribute="leading" id="tLV-2q-F9W"/>
|
||||
<constraint firstItem="aHI-vj-Ccv" firstAttribute="top" secondItem="CFP-v0-TzQ" secondAttribute="bottom" constant="14" id="z81-ib-E9q"/>
|
||||
</constraints>
|
||||
<point key="canvasLocation" x="241" y="331.5"/>
|
||||
</customView>
|
||||
</objects>
|
||||
</document>
|
||||
</document>
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
// By default this will record a file to /Users/YOUR_USERNAME/Documents/test.caf
|
||||
#define kAudioFilePath [NSString stringWithFormat:@"%@%@",NSHomeDirectory(),@"/Documents/test.caf"]
|
||||
#define kAudioFilePath [NSString stringWithFormat:@"%@%@",NSHomeDirectory(),@"/Documents/test.m4a"]
|
||||
|
||||
/**
|
||||
We will allow this view controller to act as an EZMicrophoneDelegate. This is how we listen for the microphone callback.
|
||||
|
||||
+73
-58
@@ -75,56 +75,64 @@
|
||||
}
|
||||
|
||||
#pragma mark - Customize the Audio Plot
|
||||
-(void)awakeFromNib {
|
||||
|
||||
/*
|
||||
Customizing the audio plot's look
|
||||
*/
|
||||
// Background color
|
||||
self.audioPlot.backgroundColor = [NSColor colorWithCalibratedRed: 0.984 green: 0.71 blue: 0.365 alpha: 1];
|
||||
// Waveform color
|
||||
self.audioPlot.color = [NSColor colorWithCalibratedRed: 1.000 green: 1.000 blue: 1.000 alpha: 1];
|
||||
// Plot type
|
||||
self.audioPlot.plotType = EZPlotTypeRolling;
|
||||
// Fill
|
||||
self.audioPlot.shouldFill = YES;
|
||||
// Mirror
|
||||
self.audioPlot.shouldMirror = YES;
|
||||
|
||||
// Configure the play button
|
||||
[self.playButton setHidden:YES];
|
||||
|
||||
/*
|
||||
Start the microphone
|
||||
*/
|
||||
[self.microphone startFetchingAudio];
|
||||
-(void)awakeFromNib
|
||||
{
|
||||
/*
|
||||
Customizing the audio plot's look
|
||||
*/
|
||||
// Background color
|
||||
self.audioPlot.backgroundColor = [NSColor colorWithCalibratedRed: 0.175 green: 0.151 blue: 0.137 alpha: 1];
|
||||
// Waveform color
|
||||
self.audioPlot.color = [NSColor colorWithCalibratedRed: 1.000 green: 1.000 blue: 1.000 alpha: 1];
|
||||
// Plot type
|
||||
self.audioPlot.plotType = EZPlotTypeRolling;
|
||||
// Fill
|
||||
self.audioPlot.shouldFill = YES;
|
||||
// Mirror
|
||||
self.audioPlot.shouldMirror = YES;
|
||||
|
||||
// Configure the play button
|
||||
[self.playButton setHidden:YES];
|
||||
|
||||
/*
|
||||
Start the microphone
|
||||
*/
|
||||
[self.microphone startFetchingAudio];
|
||||
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
-(void)playFile:(id)sender {
|
||||
|
||||
// Update microphone state
|
||||
[self.microphone stopFetchingAudio];
|
||||
self.microphoneToggle.state = NSOffState;
|
||||
[self.microphoneToggle setEnabled:NO];
|
||||
|
||||
// Update recording state
|
||||
self.isRecording = NO;
|
||||
self.recordingToggle.state = NSOffState;
|
||||
[self.recordingToggle setEnabled:NO];
|
||||
|
||||
// Create Audio Player
|
||||
if( self.audioPlayer ){
|
||||
if( self.audioPlayer.playing ) [self.audioPlayer stop];
|
||||
self.audioPlayer = nil;
|
||||
}
|
||||
NSError *err;
|
||||
self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:kAudioFilePath]
|
||||
// Update microphone state
|
||||
[self.microphone stopFetchingAudio];
|
||||
self.microphoneToggle.state = NSOffState;
|
||||
[self.microphoneToggle setEnabled:NO];
|
||||
|
||||
// Update recording state
|
||||
self.isRecording = NO;
|
||||
self.recordingToggle.state = NSOffState;
|
||||
[self.recordingToggle setEnabled:NO];
|
||||
|
||||
// Create Audio Player
|
||||
if( self.audioPlayer )
|
||||
{
|
||||
if( self.audioPlayer.playing ) [self.audioPlayer stop];
|
||||
self.audioPlayer = nil;
|
||||
}
|
||||
|
||||
// Close the audio file
|
||||
if( self.recorder )
|
||||
{
|
||||
[self.recorder closeAudioFile];
|
||||
}
|
||||
|
||||
NSError *err;
|
||||
self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:kAudioFilePath]
|
||||
error:&err];
|
||||
|
||||
[self.audioPlayer play];
|
||||
self.audioPlayer.delegate = self;
|
||||
|
||||
[self.audioPlayer play];
|
||||
self.audioPlayer.delegate = self;
|
||||
|
||||
}
|
||||
|
||||
@@ -141,9 +149,28 @@
|
||||
}
|
||||
}
|
||||
|
||||
-(void)toggleRecording:(id)sender {
|
||||
[self.playButton setHidden:NO];
|
||||
self.isRecording = (BOOL)[sender state];
|
||||
-(void)toggleRecording:(id)sender
|
||||
{
|
||||
[self.playButton setHidden:NO];
|
||||
switch( [sender state] )
|
||||
{
|
||||
case NSOffState:
|
||||
[self.recorder closeAudioFile];
|
||||
break;
|
||||
|
||||
case NSOnState:
|
||||
/*
|
||||
Create the recorder
|
||||
*/
|
||||
self.recorder = [EZRecorder recorderWithDestinationURL:[NSURL fileURLWithPath:kAudioFilePath]
|
||||
sourceFormat:self.microphone.audioStreamBasicDescription
|
||||
destinationFileType:EZRecorderFileTypeM4A];
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
self.isRecording = (BOOL)[sender state];
|
||||
}
|
||||
|
||||
#pragma mark - EZMicrophoneDelegate
|
||||
@@ -162,18 +189,6 @@ withNumberOfChannels:(UInt32)numberOfChannels {
|
||||
});
|
||||
}
|
||||
|
||||
-(void)microphone:(EZMicrophone *)microphone hasAudioStreamBasicDescription:(AudioStreamBasicDescription)audioStreamBasicDescription {
|
||||
// The AudioStreamBasicDescription of the microphone stream. This is useful when configuring the EZRecorder or telling another component what audio format type to expect.
|
||||
|
||||
// Here's a print function to allow you to inspect it a little easier
|
||||
[EZAudio printASBD:audioStreamBasicDescription];
|
||||
|
||||
// We can initialize the recorder with this ASBD
|
||||
self.recorder = [EZRecorder recorderWithDestinationURL:[NSURL fileURLWithPath:kAudioFilePath]
|
||||
andSourceFormat:audioStreamBasicDescription];
|
||||
|
||||
}
|
||||
|
||||
// Append the microphone data coming as a AudioBufferList with the specified buffer size to the recorder
|
||||
-(void)microphone:(EZMicrophone *)microphone
|
||||
hasBufferList:(AudioBufferList *)bufferList
|
||||
|
||||
+2
@@ -63,4 +63,6 @@
|
||||
*/
|
||||
-(IBAction)openFile:(id)sender;
|
||||
|
||||
-(IBAction)snapshot:(id)sender;
|
||||
|
||||
@end
|
||||
|
||||
+21
-21
@@ -66,25 +66,16 @@
|
||||
|
||||
#pragma mark - Customize the Audio Plot
|
||||
-(void)awakeFromNib {
|
||||
|
||||
/*
|
||||
Customizing the audio plot's look
|
||||
*/
|
||||
// Background color
|
||||
self.audioPlot.backgroundColor = [NSColor colorWithCalibratedRed: 0.169 green: 0.643 blue: 0.675 alpha: 1];
|
||||
// Waveform color
|
||||
self.audioPlot.color = [NSColor colorWithCalibratedRed: 1.000 green: 1.000 blue: 1.000 alpha: 1];
|
||||
// Plot type
|
||||
self.audioPlot.plotType = EZPlotTypeBuffer;
|
||||
// Fill
|
||||
self.audioPlot.shouldFill = YES;
|
||||
// Mirror
|
||||
self.audioPlot.shouldMirror = YES;
|
||||
|
||||
/*
|
||||
Try opening the sample file
|
||||
*/
|
||||
[self openFileWithFilePathURL:[NSURL fileURLWithPath:kAudioFileDefault]];
|
||||
self.audioPlot.wantsLayer = YES;
|
||||
self.audioPlot.backgroundColor = [NSColor clearColor];
|
||||
self.audioPlot.plotType = EZPlotTypeBuffer;
|
||||
self.audioPlot.shouldFill = YES;
|
||||
self.audioPlot.shouldMirror = YES;
|
||||
self.audioPlot.color = [NSColor colorWithCalibratedRed:0
|
||||
green:0.676
|
||||
blue:0.575
|
||||
alpha:1];
|
||||
[self openFileWithFilePathURL:[NSURL fileURLWithPath:kAudioFileDefault]];
|
||||
|
||||
}
|
||||
|
||||
@@ -100,6 +91,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
- (void)snapshot:(id)sender
|
||||
{
|
||||
NSBitmapImageRep* imageRep = [self.audioPlot bitmapImageRepForCachingDisplayInRect:self.audioPlot.bounds];
|
||||
[self.audioPlot cacheDisplayInRect:self.audioPlot.bounds toBitmapImageRep:imageRep];
|
||||
NSData* data = [imageRep representationUsingType:NSPNGFileType properties:nil];
|
||||
[data writeToFile:@"/Users/haris/Documents/waveform.png" atomically:NO];
|
||||
}
|
||||
|
||||
#pragma mark - Action Extensions
|
||||
-(void)openFileWithFilePathURL:(NSURL*)filePathURL {
|
||||
|
||||
@@ -111,8 +110,9 @@
|
||||
self.audioPlot.plotType = EZPlotTypeBuffer;
|
||||
self.audioPlot.shouldFill = YES;
|
||||
self.audioPlot.shouldMirror = YES;
|
||||
[self.audioFile getWaveformDataWithCompletionBlock:^(float *waveformData, UInt32 length) {
|
||||
[self.audioPlot updateBuffer:waveformData withBufferSize:length];
|
||||
[self.audioFile getWaveformDataWithCompletionBlock:^(EZAudioWaveformData *waveformData) {
|
||||
[self.audioPlot updateBuffer:[waveformData bufferForChannel:1]
|
||||
withBufferSize:waveformData.bufferSize];
|
||||
}];
|
||||
|
||||
}
|
||||
|
||||
+18
-9
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="4514" systemVersion="13A603" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6245" systemVersion="13F34" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
|
||||
<dependencies>
|
||||
<deployment version="1070" identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="4514"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6245"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="WaveformFromFileViewController">
|
||||
@@ -13,18 +13,16 @@
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<customView id="0tP-1Y-eJu">
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="272"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<customView translatesAutoresizingMaskIntoConstraints="NO" id="gA7-R7-vbt" customClass="EZAudioPlot">
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="211"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
</customView>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="xHD-WQ-u4W">
|
||||
<rect key="frame" x="14" y="224" width="125" height="32"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="113" id="0jv-eA-dSV"/>
|
||||
<constraint firstAttribute="height" constant="21" id="1Yn-bF-mfb"/>
|
||||
@@ -37,18 +35,28 @@
|
||||
<action selector="openFile:" target="-2" id="oIG-3z-prU"/>
|
||||
</connections>
|
||||
</button>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="zS7-9z-YB1">
|
||||
<rect key="frame" x="141" y="235" width="321" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="zS7-9z-YB1">
|
||||
<rect key="frame" x="141" y="235" width="213" height="17"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingMiddle" sendsActionOnEndEditing="YES" title="Label" id="jWL-Kx-s9B">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="2Q5-5T-qQ4">
|
||||
<rect key="frame" x="375" y="224" width="97" height="32"/>
|
||||
<buttonCell key="cell" type="push" title="snapshot" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="8ht-Cu-65q">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="snapshot:" target="-2" id="RLS-Qr-Gbu"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="zS7-9z-YB1" firstAttribute="leading" secondItem="xHD-WQ-u4W" secondAttribute="trailing" constant="10" id="4RW-fI-JJs"/>
|
||||
<constraint firstItem="2Q5-5T-qQ4" firstAttribute="top" secondItem="0tP-1Y-eJu" secondAttribute="top" constant="20" id="E3b-p3-BoQ"/>
|
||||
<constraint firstAttribute="trailing" relation="lessThanOrEqual" secondItem="zS7-9z-YB1" secondAttribute="trailing" constant="20" id="NTC-UB-Fma"/>
|
||||
<constraint firstAttribute="trailing" secondItem="gA7-R7-vbt" secondAttribute="trailing" id="Tsg-V9-2HL"/>
|
||||
<constraint firstItem="gA7-R7-vbt" firstAttribute="leading" secondItem="0tP-1Y-eJu" secondAttribute="leading" id="WAi-gw-Gx4"/>
|
||||
@@ -56,8 +64,9 @@
|
||||
<constraint firstItem="zS7-9z-YB1" firstAttribute="top" secondItem="0tP-1Y-eJu" secondAttribute="top" constant="20" id="gjc-Xh-3J0"/>
|
||||
<constraint firstItem="xHD-WQ-u4W" firstAttribute="leading" secondItem="0tP-1Y-eJu" secondAttribute="leading" constant="20" id="ide-qF-Q25"/>
|
||||
<constraint firstItem="xHD-WQ-u4W" firstAttribute="top" secondItem="0tP-1Y-eJu" secondAttribute="top" constant="20" id="iiM-ji-9ME"/>
|
||||
<constraint firstAttribute="trailing" secondItem="2Q5-5T-qQ4" secondAttribute="trailing" constant="14" id="j3N-CF-2Rt"/>
|
||||
<constraint firstAttribute="bottom" secondItem="gA7-R7-vbt" secondAttribute="bottom" id="nf1-Zn-U5V"/>
|
||||
</constraints>
|
||||
</customView>
|
||||
</objects>
|
||||
</document>
|
||||
</document>
|
||||
|
||||
Vendored
BIN
Binary file not shown.
+4
@@ -37,6 +37,7 @@
|
||||
9417A7E41867DDD600D9D37B /* TPCircularBuffer.c in Sources */ = {isa = PBXBuildFile; fileRef = 9417A7D51867DDD600D9D37B /* TPCircularBuffer.c */; };
|
||||
9417A7E51867DDD600D9D37B /* CHANGELOG in Resources */ = {isa = PBXBuildFile; fileRef = 9417A7D81867DDD600D9D37B /* CHANGELOG */; };
|
||||
9417A7E61867DDD600D9D37B /* VERSION in Resources */ = {isa = PBXBuildFile; fileRef = 9417A7D91867DDD600D9D37B /* VERSION */; };
|
||||
94FBB77318B15690007CAE45 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 94FBB77218B15690007CAE45 /* AVFoundation.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -96,6 +97,7 @@
|
||||
9417A7D61867DDD600D9D37B /* TPCircularBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPCircularBuffer.h; sourceTree = "<group>"; };
|
||||
9417A7D81867DDD600D9D37B /* CHANGELOG */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CHANGELOG; sourceTree = "<group>"; };
|
||||
9417A7D91867DDD600D9D37B /* VERSION */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = VERSION; sourceTree = "<group>"; };
|
||||
94FBB77218B15690007CAE45 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -103,6 +105,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
94FBB77318B15690007CAE45 /* AVFoundation.framework in Frameworks */,
|
||||
94057026185E612A00EB94BA /* GLKit.framework in Frameworks */,
|
||||
94057024185E612100EB94BA /* AudioToolbox.framework in Frameworks */,
|
||||
94056F80185E593500EB94BA /* CoreGraphics.framework in Frameworks */,
|
||||
@@ -146,6 +149,7 @@
|
||||
94056F7C185E593500EB94BA /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
94FBB77218B15690007CAE45 /* AVFoundation.framework */,
|
||||
94057025185E612A00EB94BA /* GLKit.framework */,
|
||||
94057023185E612000EB94BA /* AudioToolbox.framework */,
|
||||
94056F7D185E593500EB94BA /* Foundation.framework */,
|
||||
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0510"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "94056F79185E593500EB94BA"
|
||||
BuildableName = "EZAudioCoreGraphicsWaveformExample.app"
|
||||
BlueprintName = "EZAudioCoreGraphicsWaveformExample"
|
||||
ReferencedContainer = "container:EZAudioCoreGraphicsWaveformExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
buildConfiguration = "Debug">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "94056F9D185E593500EB94BA"
|
||||
BuildableName = "EZAudioCoreGraphicsWaveformExampleTests.xctest"
|
||||
BlueprintName = "EZAudioCoreGraphicsWaveformExampleTests"
|
||||
ReferencedContainer = "container:EZAudioCoreGraphicsWaveformExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "94056F79185E593500EB94BA"
|
||||
BuildableName = "EZAudioCoreGraphicsWaveformExample.app"
|
||||
BlueprintName = "EZAudioCoreGraphicsWaveformExample"
|
||||
ReferencedContainer = "container:EZAudioCoreGraphicsWaveformExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Debug"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "94056F79185E593500EB94BA"
|
||||
BuildableName = "EZAudioCoreGraphicsWaveformExample.app"
|
||||
BlueprintName = "EZAudioCoreGraphicsWaveformExample"
|
||||
ReferencedContainer = "container:EZAudioCoreGraphicsWaveformExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Release"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "94056F79185E593500EB94BA"
|
||||
BuildableName = "EZAudioCoreGraphicsWaveformExample.app"
|
||||
BlueprintName = "EZAudioCoreGraphicsWaveformExample"
|
||||
ReferencedContainer = "container:EZAudioCoreGraphicsWaveformExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>SchemeUserState</key>
|
||||
<dict>
|
||||
<key>EZAudioCoreGraphicsWaveformExample.xcscheme</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>SuppressBuildableAutocreation</key>
|
||||
<dict>
|
||||
<key>94056F79185E593500EB94BA</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>94056F9D185E593500EB94BA</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
+5
-5
@@ -10,29 +10,29 @@
|
||||
<string>EZAudioExamplesiOS</string>
|
||||
<key>IDESourceControlProjectOriginsDictionary</key>
|
||||
<dict>
|
||||
<key>F77EC8D9-F815-4829-9274-5DA08EA98D6B</key>
|
||||
<key>9D6FF97A89F512CD81EAE1A971A1D2EB03E03F7C</key>
|
||||
<string>https://github.com/syedhali/EZAudio.git</string>
|
||||
</dict>
|
||||
<key>IDESourceControlProjectPath</key>
|
||||
<string>EZAudioExamples/iOS/EZAudioExamplesiOS.xcworkspace</string>
|
||||
<key>IDESourceControlProjectRelativeInstallPathDictionary</key>
|
||||
<dict>
|
||||
<key>F77EC8D9-F815-4829-9274-5DA08EA98D6B</key>
|
||||
<key>9D6FF97A89F512CD81EAE1A971A1D2EB03E03F7C</key>
|
||||
<string>../../..</string>
|
||||
</dict>
|
||||
<key>IDESourceControlProjectURL</key>
|
||||
<string>https://github.com/syedhali/EZAudio.git</string>
|
||||
<key>IDESourceControlProjectVersion</key>
|
||||
<integer>110</integer>
|
||||
<integer>111</integer>
|
||||
<key>IDESourceControlProjectWCCIdentifier</key>
|
||||
<string>F77EC8D9-F815-4829-9274-5DA08EA98D6B</string>
|
||||
<string>9D6FF97A89F512CD81EAE1A971A1D2EB03E03F7C</string>
|
||||
<key>IDESourceControlProjectWCConfigurations</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
|
||||
<string>public.vcs.git</string>
|
||||
<key>IDESourceControlWCCIdentifierKey</key>
|
||||
<string>F77EC8D9-F815-4829-9274-5DA08EA98D6B</string>
|
||||
<string>9D6FF97A89F512CD81EAE1A971A1D2EB03E03F7C</string>
|
||||
<key>IDESourceControlWCCName</key>
|
||||
<string>EZAudio</string>
|
||||
</dict>
|
||||
|
||||
BIN
Binary file not shown.
@@ -38,6 +38,7 @@
|
||||
9417A9CD1871E96300D9D37B /* VERSION in Resources */ = {isa = PBXBuildFile; fileRef = 9417A9C01871E96300D9D37B /* VERSION */; };
|
||||
9417A9D01871E97D00D9D37B /* FFTViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A9CF1871E97D00D9D37B /* FFTViewController.m */; };
|
||||
9417A9D21871EA5900D9D37B /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9417A9D11871EA5900D9D37B /* Accelerate.framework */; };
|
||||
94FBB77918B156B8007CAE45 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 94FBB77818B156B8007CAE45 /* AVFoundation.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -98,6 +99,7 @@
|
||||
9417A9CE1871E97D00D9D37B /* FFTViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FFTViewController.h; sourceTree = "<group>"; };
|
||||
9417A9CF1871E97D00D9D37B /* FFTViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FFTViewController.m; sourceTree = "<group>"; };
|
||||
9417A9D11871EA5900D9D37B /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; };
|
||||
94FBB77818B156B8007CAE45 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -105,6 +107,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
94FBB77918B156B8007CAE45 /* AVFoundation.framework in Frameworks */,
|
||||
9417A9D21871EA5900D9D37B /* Accelerate.framework in Frameworks */,
|
||||
9417A9A61871E8A100D9D37B /* GLKit.framework in Frameworks */,
|
||||
9417A9A41871E89500D9D37B /* AudioToolbox.framework in Frameworks */,
|
||||
@@ -149,6 +152,7 @@
|
||||
9417A96A1871E88300D9D37B /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
94FBB77818B156B8007CAE45 /* AVFoundation.framework */,
|
||||
9417A9D11871EA5900D9D37B /* Accelerate.framework */,
|
||||
9417A9A51871E8A100D9D37B /* GLKit.framework */,
|
||||
9417A9A31871E89500D9D37B /* AudioToolbox.framework */,
|
||||
@@ -577,6 +581,7 @@
|
||||
9417A99F1871E88300D9D37B /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
9417A9A01871E88300D9D37B /* Build configuration list for PBXNativeTarget "EZAudioFFTExampleTests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
@@ -585,6 +590,7 @@
|
||||
9417A9A21871E88300D9D37B /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0510"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "9417A9671871E88300D9D37B"
|
||||
BuildableName = "EZAudioFFTExample.app"
|
||||
BlueprintName = "EZAudioFFTExample"
|
||||
ReferencedContainer = "container:EZAudioFFTExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
buildConfiguration = "Debug">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "9417A98B1871E88300D9D37B"
|
||||
BuildableName = "EZAudioFFTExampleTests.xctest"
|
||||
BlueprintName = "EZAudioFFTExampleTests"
|
||||
ReferencedContainer = "container:EZAudioFFTExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "9417A9671871E88300D9D37B"
|
||||
BuildableName = "EZAudioFFTExample.app"
|
||||
BlueprintName = "EZAudioFFTExample"
|
||||
ReferencedContainer = "container:EZAudioFFTExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Debug"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "9417A9671871E88300D9D37B"
|
||||
BuildableName = "EZAudioFFTExample.app"
|
||||
BlueprintName = "EZAudioFFTExample"
|
||||
ReferencedContainer = "container:EZAudioFFTExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Release"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "9417A9671871E88300D9D37B"
|
||||
BuildableName = "EZAudioFFTExample.app"
|
||||
BlueprintName = "EZAudioFFTExample"
|
||||
ReferencedContainer = "container:EZAudioFFTExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>SchemeUserState</key>
|
||||
<dict>
|
||||
<key>EZAudioFFTExample.xcscheme</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>6</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>SuppressBuildableAutocreation</key>
|
||||
<dict>
|
||||
<key>9417A9671871E88300D9D37B</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>9417A98B1871E88300D9D37B</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
+16
-19
@@ -1,7 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="4514" systemVersion="13A603" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" initialViewController="vXZ-lx-hvc">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6185.11" systemVersion="13E28" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" initialViewController="vXZ-lx-hvc">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="3747"/>
|
||||
<deployment defaultVersion="1792" identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6190.4"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
@@ -18,30 +19,23 @@
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="0IN-AU-L5p" customClass="EZAudioPlotGL">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="284"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" relation="lessThanOrEqual" constant="284" id="evV-xs-Een"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="tRL-re-aMl" customClass="EZAudioPlot">
|
||||
<rect key="frame" x="0.0" y="284" width="320" height="284"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" relation="lessThanOrEqual" priority="86" constant="284" id="G4S-hR-Wmj"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="tRL-re-aMl" secondAttribute="bottom" id="6Xx-qX-MTy"/>
|
||||
<constraint firstItem="0IN-AU-L5p" firstAttribute="top" secondItem="kh9-bI-dsS" secondAttribute="top" id="83g-qr-GsR"/>
|
||||
<constraint firstAttribute="trailing" secondItem="tRL-re-aMl" secondAttribute="trailing" id="eTI-rb-OrM"/>
|
||||
<constraint firstAttribute="trailing" secondItem="0IN-AU-L5p" secondAttribute="trailing" id="iL0-3u-CeX"/>
|
||||
<constraint firstItem="0IN-AU-L5p" firstAttribute="leading" secondItem="kh9-bI-dsS" secondAttribute="leading" id="j4O-TP-2Vf"/>
|
||||
<constraint firstItem="tRL-re-aMl" firstAttribute="leading" secondItem="kh9-bI-dsS" secondAttribute="leading" id="jtj-vj-fPm"/>
|
||||
<constraint firstItem="tRL-re-aMl" firstAttribute="top" secondItem="0IN-AU-L5p" secondAttribute="bottom" id="pZY-xg-Sb9"/>
|
||||
<constraint firstItem="tRL-re-aMl" firstAttribute="height" secondItem="0IN-AU-L5p" secondAttribute="height" id="0x5-uT-6oH"/>
|
||||
<constraint firstItem="tRL-re-aMl" firstAttribute="leading" secondItem="kh9-bI-dsS" secondAttribute="leading" id="1PO-c6-Jpi"/>
|
||||
<constraint firstItem="0IN-AU-L5p" firstAttribute="top" secondItem="kh9-bI-dsS" secondAttribute="top" id="3j1-j9-0EV"/>
|
||||
<constraint firstItem="Ghx-UD-SsY" firstAttribute="top" secondItem="tRL-re-aMl" secondAttribute="bottom" id="9sP-5w-dcD"/>
|
||||
<constraint firstAttribute="trailing" secondItem="tRL-re-aMl" secondAttribute="trailing" id="KSC-e5-GVL"/>
|
||||
<constraint firstItem="0IN-AU-L5p" firstAttribute="leading" secondItem="kh9-bI-dsS" secondAttribute="leading" id="Ut5-w2-ONV"/>
|
||||
<constraint firstItem="tRL-re-aMl" firstAttribute="top" secondItem="0IN-AU-L5p" secondAttribute="bottom" id="V11-Sm-jlk"/>
|
||||
<constraint firstAttribute="trailing" secondItem="0IN-AU-L5p" secondAttribute="trailing" id="XFq-Me-fvw"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<connections>
|
||||
@@ -56,6 +50,9 @@
|
||||
<simulatedMetricsContainer key="defaultSimulatedMetrics">
|
||||
<simulatedStatusBarMetrics key="statusBar"/>
|
||||
<simulatedOrientationMetrics key="orientation"/>
|
||||
<simulatedScreenMetrics key="destination" type="retina4"/>
|
||||
<simulatedScreenMetrics key="destination" type="retina4">
|
||||
<size key="portraitSize" width="320" height="568"/>
|
||||
<size key="landscapeSize" width="568" height="320"/>
|
||||
</simulatedScreenMetrics>
|
||||
</simulatedMetricsContainer>
|
||||
</document>
|
||||
</document>
|
||||
|
||||
+4
@@ -37,6 +37,7 @@
|
||||
9417A80B1867DDE300D9D37B /* TPCircularBuffer.c in Sources */ = {isa = PBXBuildFile; fileRef = 9417A7FC1867DDE300D9D37B /* TPCircularBuffer.c */; };
|
||||
9417A80C1867DDE300D9D37B /* CHANGELOG in Resources */ = {isa = PBXBuildFile; fileRef = 9417A7FF1867DDE300D9D37B /* CHANGELOG */; };
|
||||
9417A80D1867DDE300D9D37B /* VERSION in Resources */ = {isa = PBXBuildFile; fileRef = 9417A8001867DDE300D9D37B /* VERSION */; };
|
||||
94FBB77518B15698007CAE45 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 94FBB77418B15698007CAE45 /* AVFoundation.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -96,6 +97,7 @@
|
||||
9417A7FD1867DDE300D9D37B /* TPCircularBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPCircularBuffer.h; sourceTree = "<group>"; };
|
||||
9417A7FF1867DDE300D9D37B /* CHANGELOG */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CHANGELOG; sourceTree = "<group>"; };
|
||||
9417A8001867DDE300D9D37B /* VERSION */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = VERSION; sourceTree = "<group>"; };
|
||||
94FBB77418B15698007CAE45 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -103,6 +105,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
94FBB77518B15698007CAE45 /* AVFoundation.framework in Frameworks */,
|
||||
9417A6CD18658FCD00D9D37B /* GLKit.framework in Frameworks */,
|
||||
9417A6CB18658FC900D9D37B /* AudioToolbox.framework in Frameworks */,
|
||||
94056FEE185E5EAF00EB94BA /* CoreGraphics.framework in Frameworks */,
|
||||
@@ -146,6 +149,7 @@
|
||||
94056FEA185E5EAF00EB94BA /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
94FBB77418B15698007CAE45 /* AVFoundation.framework */,
|
||||
9417A6CC18658FCD00D9D37B /* GLKit.framework */,
|
||||
9417A6CA18658FC900D9D37B /* AudioToolbox.framework */,
|
||||
94056FEB185E5EAF00EB94BA /* Foundation.framework */,
|
||||
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0510"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "94056FE7185E5EAF00EB94BA"
|
||||
BuildableName = "EZAudioOpenGLWaveformExample.app"
|
||||
BlueprintName = "EZAudioOpenGLWaveformExample"
|
||||
ReferencedContainer = "container:EZAudioOpenGLWaveformExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
buildConfiguration = "Debug">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "9405700B185E5EAF00EB94BA"
|
||||
BuildableName = "EZAudioOpenGLWaveformExampleTests.xctest"
|
||||
BlueprintName = "EZAudioOpenGLWaveformExampleTests"
|
||||
ReferencedContainer = "container:EZAudioOpenGLWaveformExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "94056FE7185E5EAF00EB94BA"
|
||||
BuildableName = "EZAudioOpenGLWaveformExample.app"
|
||||
BlueprintName = "EZAudioOpenGLWaveformExample"
|
||||
ReferencedContainer = "container:EZAudioOpenGLWaveformExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Debug"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "94056FE7185E5EAF00EB94BA"
|
||||
BuildableName = "EZAudioOpenGLWaveformExample.app"
|
||||
BlueprintName = "EZAudioOpenGLWaveformExample"
|
||||
ReferencedContainer = "container:EZAudioOpenGLWaveformExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Release"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "94056FE7185E5EAF00EB94BA"
|
||||
BuildableName = "EZAudioOpenGLWaveformExample.app"
|
||||
BlueprintName = "EZAudioOpenGLWaveformExample"
|
||||
ReferencedContainer = "container:EZAudioOpenGLWaveformExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>SchemeUserState</key>
|
||||
<dict>
|
||||
<key>EZAudioOpenGLWaveformExample.xcscheme</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>SuppressBuildableAutocreation</key>
|
||||
<dict>
|
||||
<key>94056FE7185E5EAF00EB94BA</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>9405700B185E5EAF00EB94BA</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0510"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "9417A6091864D4DC00D9D37B"
|
||||
BuildableName = "EZAudioPassThroughExample.app"
|
||||
BlueprintName = "EZAudioPassThroughExample"
|
||||
ReferencedContainer = "container:EZAudioPassThroughExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
buildConfiguration = "Debug">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "9417A62D1864D4DC00D9D37B"
|
||||
BuildableName = "EZAudioPassThroughExampleTests.xctest"
|
||||
BlueprintName = "EZAudioPassThroughExampleTests"
|
||||
ReferencedContainer = "container:EZAudioPassThroughExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "9417A6091864D4DC00D9D37B"
|
||||
BuildableName = "EZAudioPassThroughExample.app"
|
||||
BlueprintName = "EZAudioPassThroughExample"
|
||||
ReferencedContainer = "container:EZAudioPassThroughExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Debug"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "9417A6091864D4DC00D9D37B"
|
||||
BuildableName = "EZAudioPassThroughExample.app"
|
||||
BlueprintName = "EZAudioPassThroughExample"
|
||||
ReferencedContainer = "container:EZAudioPassThroughExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Release"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "9417A6091864D4DC00D9D37B"
|
||||
BuildableName = "EZAudioPassThroughExample.app"
|
||||
BlueprintName = "EZAudioPassThroughExample"
|
||||
ReferencedContainer = "container:EZAudioPassThroughExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>SchemeUserState</key>
|
||||
<dict>
|
||||
<key>EZAudioPassThroughExample.xcscheme</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>5</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>SuppressBuildableAutocreation</key>
|
||||
<dict>
|
||||
<key>9417A6091864D4DC00D9D37B</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>9417A62D1864D4DC00D9D37B</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
Binary file not shown.
+4
@@ -38,6 +38,7 @@
|
||||
944D0409186038A60076EF7A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 944D0407186038A60076EF7A /* InfoPlist.strings */; };
|
||||
944D040B186038A60076EF7A /* EZAudioPlayFileExampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 944D040A186038A60076EF7A /* EZAudioPlayFileExampleTests.m */; };
|
||||
944D043D1860398B0076EF7A /* PlayFileViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 944D043C1860398A0076EF7A /* PlayFileViewController.m */; };
|
||||
94BE6F86188F55CC00841D98 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 94BE6F85188F55CC00841D98 /* AVFoundation.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -98,6 +99,7 @@
|
||||
944D040A186038A60076EF7A /* EZAudioPlayFileExampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EZAudioPlayFileExampleTests.m; sourceTree = "<group>"; };
|
||||
944D043B1860398A0076EF7A /* PlayFileViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlayFileViewController.h; sourceTree = "<group>"; };
|
||||
944D043C1860398A0076EF7A /* PlayFileViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PlayFileViewController.m; sourceTree = "<group>"; };
|
||||
94BE6F85188F55CC00841D98 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -105,6 +107,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
94BE6F86188F55CC00841D98 /* AVFoundation.framework in Frameworks */,
|
||||
9417A6C918658FC000D9D37B /* GLKit.framework in Frameworks */,
|
||||
9417A6C718658FB500D9D37B /* AudioToolbox.framework in Frameworks */,
|
||||
944D03DF186038A60076EF7A /* CoreGraphics.framework in Frameworks */,
|
||||
@@ -188,6 +191,7 @@
|
||||
944D03DB186038A60076EF7A /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
94BE6F85188F55CC00841D98 /* AVFoundation.framework */,
|
||||
9417A6C818658FC000D9D37B /* GLKit.framework */,
|
||||
9417A6C618658FB500D9D37B /* AudioToolbox.framework */,
|
||||
944D03DC186038A60076EF7A /* Foundation.framework */,
|
||||
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0510"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "944D03D8186038A60076EF7A"
|
||||
BuildableName = "EZAudioPlayFileExample.app"
|
||||
BlueprintName = "EZAudioPlayFileExample"
|
||||
ReferencedContainer = "container:EZAudioPlayFileExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
buildConfiguration = "Debug">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "944D03FC186038A60076EF7A"
|
||||
BuildableName = "EZAudioPlayFileExampleTests.xctest"
|
||||
BlueprintName = "EZAudioPlayFileExampleTests"
|
||||
ReferencedContainer = "container:EZAudioPlayFileExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "944D03D8186038A60076EF7A"
|
||||
BuildableName = "EZAudioPlayFileExample.app"
|
||||
BlueprintName = "EZAudioPlayFileExample"
|
||||
ReferencedContainer = "container:EZAudioPlayFileExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Debug"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "944D03D8186038A60076EF7A"
|
||||
BuildableName = "EZAudioPlayFileExample.app"
|
||||
BlueprintName = "EZAudioPlayFileExample"
|
||||
ReferencedContainer = "container:EZAudioPlayFileExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Release"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "944D03D8186038A60076EF7A"
|
||||
BuildableName = "EZAudioPlayFileExample.app"
|
||||
BlueprintName = "EZAudioPlayFileExample"
|
||||
ReferencedContainer = "container:EZAudioPlayFileExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>SchemeUserState</key>
|
||||
<dict>
|
||||
<key>EZAudioPlayFileExample.xcscheme</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>2</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>SuppressBuildableAutocreation</key>
|
||||
<dict>
|
||||
<key>944D03D8186038A60076EF7A</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>944D03FC186038A60076EF7A</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -8,11 +8,26 @@
|
||||
|
||||
#import "AppDelegate.h"
|
||||
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
// Override point for customization after application launch.
|
||||
|
||||
// Remember to configure your audio session
|
||||
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
|
||||
NSError *err = NULL;
|
||||
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:&err];
|
||||
if( err ){
|
||||
NSLog(@"There was an error creating the audio session");
|
||||
}
|
||||
[audioSession overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:NULL];
|
||||
if( err ){
|
||||
NSLog(@"There was an error sending the audio to the speakers");
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="4514" systemVersion="13A603" targetRuntime="iOS.CocoaTouch.iPad" propertyAccessControl="none" useAutolayout="YES" initialViewController="BYZ-38-t0r">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="5056" systemVersion="13D65" targetRuntime="iOS.CocoaTouch.iPad" propertyAccessControl="none" useAutolayout="YES" initialViewController="BYZ-38-t0r">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="3747"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="3733"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Play File View Controller-->
|
||||
@@ -48,7 +48,7 @@
|
||||
<action selector="play:" destination="BYZ-38-t0r" eventType="touchUpInside" id="1V6-g2-0vG"/>
|
||||
</connections>
|
||||
</button>
|
||||
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.5" minValue="0.0" maxValue="1" continuous="NO" translatesAutoresizingMaskIntoConstraints="NO" id="BeD-bR-Pie">
|
||||
<slider opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.5" minValue="0.0" maxValue="1" continuous="NO" translatesAutoresizingMaskIntoConstraints="NO" id="BeD-bR-Pie">
|
||||
<rect key="frame" x="18" y="902" width="732" height="34"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<connections>
|
||||
@@ -95,4 +95,4 @@
|
||||
<simulatedOrientationMetrics key="orientation"/>
|
||||
<simulatedScreenMetrics key="destination"/>
|
||||
</simulatedMetricsContainer>
|
||||
</document>
|
||||
</document>
|
||||
|
||||
+5
-9
@@ -1,7 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="4514" systemVersion="13A603" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" initialViewController="vXZ-lx-hvc">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6254" systemVersion="14C109" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" initialViewController="vXZ-lx-hvc">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="3747"/>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6247"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Play File View Controller-->
|
||||
@@ -18,11 +19,9 @@
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="uPM-eF-6ct" customClass="EZAudioPlotGL">
|
||||
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="TsA-XB-MTe">
|
||||
<rect key="frame" x="187" y="520" width="113" height="29"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<segments>
|
||||
<segment title="Buffer"/>
|
||||
<segment title="Rolling"/>
|
||||
@@ -33,14 +32,12 @@
|
||||
</segmentedControl>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Sh2-sS-uR6">
|
||||
<rect key="frame" x="20" y="488" width="280" height="21"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="xKX-cQ-AvV">
|
||||
<rect key="frame" x="20" y="519" width="30" height="30"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<state key="normal" title="Play">
|
||||
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</state>
|
||||
@@ -48,9 +45,8 @@
|
||||
<action selector="play:" destination="vXZ-lx-hvc" eventType="touchUpInside" id="0vr-5R-dT3"/>
|
||||
</connections>
|
||||
</button>
|
||||
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.5" minValue="0.0" maxValue="1" continuous="NO" translatesAutoresizingMaskIntoConstraints="NO" id="tSM-7B-ujg">
|
||||
<slider opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.5" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="tSM-7B-ujg">
|
||||
<rect key="frame" x="18" y="447" width="284" height="34"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<connections>
|
||||
<action selector="seekToFrame:" destination="vXZ-lx-hvc" eventType="valueChanged" id="3A8-F6-h6C"/>
|
||||
</connections>
|
||||
@@ -95,4 +91,4 @@
|
||||
<simulatedOrientationMetrics key="orientation"/>
|
||||
<simulatedScreenMetrics key="destination" type="retina4"/>
|
||||
</simulatedMetricsContainer>
|
||||
</document>
|
||||
</document>
|
||||
|
||||
+1
@@ -15,6 +15,7 @@
|
||||
Here's the default audio file included with the example
|
||||
*/
|
||||
#define kAudioFileDefault [[NSBundle mainBundle] pathForResource:@"simple-drum-beat" ofType:@"wav"]
|
||||
//#define kAudioFileDefault [[NSBundle mainBundle] pathForResource:@"Röyksopp - Röyksopp Forever" ofType:@"mp3"]
|
||||
|
||||
/**
|
||||
Using the EZOutputDataSource to provide output data to the EZOutput component.
|
||||
|
||||
+16
-32
@@ -133,7 +133,7 @@
|
||||
[[EZOutput sharedOutput] stopPlayback];
|
||||
|
||||
self.audioFile = [EZAudioFile audioFileWithURL:filePathURL];
|
||||
self.audioFile.audioFileDelegate = self;
|
||||
self.audioFile.delegate = self;
|
||||
self.eof = NO;
|
||||
self.filePathLabel.text = filePathURL.lastPathComponent;
|
||||
self.framePositionSlider.maximumValue = (float)self.audioFile.totalFrames;
|
||||
@@ -156,8 +156,8 @@
|
||||
readAudio:(float **)buffer
|
||||
withBufferSize:(UInt32)bufferSize
|
||||
withNumberOfChannels:(UInt32)numberOfChannels {
|
||||
if( [EZOutput sharedOutput].isPlaying ){
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if( [EZOutput sharedOutput].isPlaying ){
|
||||
if( self.audioPlot.plotType == EZPlotTypeBuffer &&
|
||||
self.audioPlot.shouldFill == YES &&
|
||||
self.audioPlot.shouldMirror == YES ){
|
||||
@@ -165,8 +165,8 @@ withNumberOfChannels:(UInt32)numberOfChannels {
|
||||
self.audioPlot.shouldMirror = NO;
|
||||
}
|
||||
[self.audioPlot updateBuffer:buffer[0] withBufferSize:bufferSize];
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
-(void)audioFile:(EZAudioFile *)audioFile
|
||||
@@ -179,36 +179,20 @@ withNumberOfChannels:(UInt32)numberOfChannels {
|
||||
}
|
||||
|
||||
#pragma mark - EZOutputDataSource
|
||||
-(AudioBufferList *)output:(EZOutput *)output
|
||||
needsBufferListWithFrames:(UInt32)frames
|
||||
withBufferSize:(UInt32 *)bufferSize {
|
||||
if( self.audioFile ){
|
||||
|
||||
// Reached the end of the file
|
||||
if( self.eof ){
|
||||
// Here's what you do to loop the file
|
||||
[self.audioFile seekToFrame:0];
|
||||
self.eof = NO;
|
||||
}
|
||||
|
||||
// Allocate a buffer list to hold the file's data
|
||||
AudioBufferList *bufferList = [EZAudio audioBufferList];
|
||||
BOOL eof;
|
||||
-(void)output:(EZOutput *)output shouldFillAudioBufferList:(AudioBufferList *)audioBufferList withNumberOfFrames:(UInt32)frames
|
||||
{
|
||||
if( self.audioFile )
|
||||
{
|
||||
UInt32 bufferSize;
|
||||
[self.audioFile readFrames:frames
|
||||
audioBufferList:bufferList
|
||||
bufferSize:bufferSize
|
||||
eof:&eof];
|
||||
self.eof = eof;
|
||||
|
||||
// Reached the end of the file on the last read
|
||||
if( eof ){
|
||||
[EZAudio freeBufferList:bufferList];
|
||||
return nil;
|
||||
audioBufferList:audioBufferList
|
||||
bufferSize:&bufferSize
|
||||
eof:&_eof];
|
||||
if( _eof )
|
||||
{
|
||||
[self seekToFrame:0];
|
||||
}
|
||||
return bufferList;
|
||||
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
-(AudioStreamBasicDescription)outputHasAudioStreamBasicDescription:(EZOutput *)output {
|
||||
|
||||
Binary file not shown.
+96
@@ -0,0 +1,96 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0510"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "940570C7185E7F8300EB94BA"
|
||||
BuildableName = "EZAudioRecordExample.app"
|
||||
BlueprintName = "EZAudioRecordExample"
|
||||
ReferencedContainer = "container:EZAudioRecordExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
buildConfiguration = "Debug">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "940570EB185E7F8300EB94BA"
|
||||
BuildableName = "EZAudioRecordExampleTests.xctest"
|
||||
BlueprintName = "EZAudioRecordExampleTests"
|
||||
ReferencedContainer = "container:EZAudioRecordExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "940570C7185E7F8300EB94BA"
|
||||
BuildableName = "EZAudioRecordExample.app"
|
||||
BlueprintName = "EZAudioRecordExample"
|
||||
ReferencedContainer = "container:EZAudioRecordExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Debug"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "940570C7185E7F8300EB94BA"
|
||||
BuildableName = "EZAudioRecordExample.app"
|
||||
BlueprintName = "EZAudioRecordExample"
|
||||
ReferencedContainer = "container:EZAudioRecordExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Release"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "940570C7185E7F8300EB94BA"
|
||||
BuildableName = "EZAudioRecordExample.app"
|
||||
BlueprintName = "EZAudioRecordExample"
|
||||
ReferencedContainer = "container:EZAudioRecordExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>SchemeUserState</key>
|
||||
<dict>
|
||||
<key>EZAudioRecordExample.xcscheme</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>3</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>SuppressBuildableAutocreation</key>
|
||||
<dict>
|
||||
<key>940570C7185E7F8300EB94BA</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>940570EB185E7F8300EB94BA</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
|
||||
// By default this will record a file to the application's documents directory (within the application's sandbox)
|
||||
#define kAudioFilePath @"EZAudioTest.caf"
|
||||
#define kAudioFilePath @"EZAudioTest.m4a"
|
||||
|
||||
@interface RecordViewController : UIViewController <AVAudioPlayerDelegate,EZMicrophoneDelegate>
|
||||
|
||||
|
||||
+67
-46
@@ -49,8 +49,10 @@
|
||||
|
||||
#pragma mark - Initialize View Controller Here
|
||||
-(void)initializeViewController {
|
||||
// Create an instance of the microphone and tell it to use this view controller instance as the delegate
|
||||
self.microphone = [EZMicrophone microphoneWithDelegate:self];
|
||||
// Create an instance of the microphone and tell it to use this view controller instance as the delegate
|
||||
self.microphone = [EZMicrophone microphoneWithDelegate:self];
|
||||
|
||||
|
||||
}
|
||||
|
||||
#pragma mark - Customize the Audio Plot
|
||||
@@ -91,29 +93,41 @@
|
||||
}
|
||||
|
||||
#pragma mark - Actions
|
||||
-(void)playFile:(id)sender {
|
||||
|
||||
// Update microphone state
|
||||
[self.microphone stopFetchingAudio];
|
||||
self.microphoneTextField.text = @"Microphone Off";
|
||||
self.microphoneSwitch.on = NO;
|
||||
|
||||
// Update recording state
|
||||
self.isRecording = NO;
|
||||
self.recordingTextField.text = @"Not Recording";
|
||||
self.recordSwitch.on = NO;
|
||||
|
||||
// Create Audio Player
|
||||
if( self.audioPlayer ){
|
||||
if( self.audioPlayer.playing ) [self.audioPlayer stop];
|
||||
self.audioPlayer = nil;
|
||||
}
|
||||
NSError *err;
|
||||
self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[self testFilePathURL]
|
||||
-(void)playFile:(id)sender
|
||||
{
|
||||
|
||||
// Update microphone state
|
||||
[self.microphone stopFetchingAudio];
|
||||
self.microphoneTextField.text = @"Microphone Off";
|
||||
self.microphoneSwitch.on = NO;
|
||||
|
||||
// Update recording state
|
||||
self.isRecording = NO;
|
||||
self.recordingTextField.text = @"Not Recording";
|
||||
self.recordSwitch.on = NO;
|
||||
|
||||
// Create Audio Player
|
||||
if( self.audioPlayer )
|
||||
{
|
||||
if( self.audioPlayer.playing )
|
||||
{
|
||||
[self.audioPlayer stop];
|
||||
}
|
||||
self.audioPlayer = nil;
|
||||
}
|
||||
|
||||
// Close the audio file
|
||||
if( self.recorder )
|
||||
{
|
||||
[self.recorder closeAudioFile];
|
||||
}
|
||||
|
||||
NSError *err;
|
||||
self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[self testFilePathURL]
|
||||
error:&err];
|
||||
[self.audioPlayer play];
|
||||
self.audioPlayer.delegate = self;
|
||||
self.playingTextField.text = @"Playing";
|
||||
[self.audioPlayer play];
|
||||
self.audioPlayer.delegate = self;
|
||||
self.playingTextField.text = @"Playing";
|
||||
|
||||
}
|
||||
|
||||
@@ -137,15 +151,32 @@
|
||||
|
||||
-(void)toggleRecording:(id)sender {
|
||||
|
||||
self.playingTextField.text = @"Not Playing";
|
||||
if( self.audioPlayer ){
|
||||
if( self.audioPlayer.playing ) [self.audioPlayer stop];
|
||||
self.audioPlayer = nil;
|
||||
}
|
||||
self.playButton.hidden = NO;
|
||||
|
||||
self.isRecording = (BOOL)[sender isOn];
|
||||
self.recordingTextField.text = self.isRecording ? @"Recording" : @"Not Recording";
|
||||
self.playingTextField.text = @"Not Playing";
|
||||
if( self.audioPlayer )
|
||||
{
|
||||
if( self.audioPlayer.playing )
|
||||
{
|
||||
[self.audioPlayer stop];
|
||||
}
|
||||
self.audioPlayer = nil;
|
||||
}
|
||||
self.playButton.hidden = NO;
|
||||
|
||||
if( [sender isOn] )
|
||||
{
|
||||
/*
|
||||
Create the recorder
|
||||
*/
|
||||
self.recorder = [EZRecorder recorderWithDestinationURL:[self testFilePathURL]
|
||||
sourceFormat:self.microphone.audioStreamBasicDescription
|
||||
destinationFileType:EZRecorderFileTypeM4A];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self.recorder closeAudioFile];
|
||||
}
|
||||
self.isRecording = (BOOL)[sender isOn];
|
||||
self.recordingTextField.text = self.isRecording ? @"Recording" : @"Not Recording";
|
||||
}
|
||||
|
||||
#pragma mark - EZMicrophoneDelegate
|
||||
@@ -164,18 +195,6 @@ withNumberOfChannels:(UInt32)numberOfChannels {
|
||||
});
|
||||
}
|
||||
|
||||
-(void)microphone:(EZMicrophone *)microphone hasAudioStreamBasicDescription:(AudioStreamBasicDescription)audioStreamBasicDescription {
|
||||
// The AudioStreamBasicDescription of the microphone stream. This is useful when configuring the EZRecorder or telling another component what audio format type to expect.
|
||||
|
||||
// Here's a print function to allow you to inspect it a little easier
|
||||
[EZAudio printASBD:audioStreamBasicDescription];
|
||||
|
||||
// We can initialize the recorder with this ASBD
|
||||
self.recorder = [EZRecorder recorderWithDestinationURL:[self testFilePathURL]
|
||||
andSourceFormat:audioStreamBasicDescription];
|
||||
|
||||
}
|
||||
|
||||
-(void)microphone:(EZMicrophone *)microphone
|
||||
hasBufferList:(AudioBufferList *)bufferList
|
||||
withBufferSize:(UInt32)bufferSize
|
||||
@@ -215,7 +234,9 @@ withNumberOfChannels:(UInt32)numberOfChannels {
|
||||
}
|
||||
|
||||
-(NSURL*)testFilePathURL {
|
||||
return [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/%@",[self applicationDocumentsDirectory],kAudioFilePath]];
|
||||
return [NSURL fileURLWithPath:[NSString stringWithFormat:@"%@/%@",
|
||||
[self applicationDocumentsDirectory],
|
||||
kAudioFilePath]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+4
@@ -38,6 +38,7 @@
|
||||
9417A8801867DE0F00D9D37B /* TPCircularBuffer.c in Sources */ = {isa = PBXBuildFile; fileRef = 9417A8711867DE0F00D9D37B /* TPCircularBuffer.c */; };
|
||||
9417A8811867DE0F00D9D37B /* CHANGELOG in Resources */ = {isa = PBXBuildFile; fileRef = 9417A8741867DE0F00D9D37B /* CHANGELOG */; };
|
||||
9417A8821867DE0F00D9D37B /* VERSION in Resources */ = {isa = PBXBuildFile; fileRef = 9417A8751867DE0F00D9D37B /* VERSION */; };
|
||||
94FBB77718B156AE007CAE45 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 94FBB77618B156AE007CAE45 /* AVFoundation.framework */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -98,6 +99,7 @@
|
||||
9417A8721867DE0F00D9D37B /* TPCircularBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPCircularBuffer.h; sourceTree = "<group>"; };
|
||||
9417A8741867DE0F00D9D37B /* CHANGELOG */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CHANGELOG; sourceTree = "<group>"; };
|
||||
9417A8751867DE0F00D9D37B /* VERSION */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = VERSION; sourceTree = "<group>"; };
|
||||
94FBB77618B156AE007CAE45 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -105,6 +107,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
94FBB77718B156AE007CAE45 /* AVFoundation.framework in Frameworks */,
|
||||
9417A6C318658F9C00D9D37B /* GLKit.framework in Frameworks */,
|
||||
9417A6C118658F9700D9D37B /* AudioToolbox.framework in Frameworks */,
|
||||
94057060185E69D400EB94BA /* CoreGraphics.framework in Frameworks */,
|
||||
@@ -148,6 +151,7 @@
|
||||
9405705C185E69D400EB94BA /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
94FBB77618B156AE007CAE45 /* AVFoundation.framework */,
|
||||
9417A6C218658F9C00D9D37B /* GLKit.framework */,
|
||||
9417A6C018658F9700D9D37B /* AudioToolbox.framework */,
|
||||
9405705D185E69D400EB94BA /* Foundation.framework */,
|
||||
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0510"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "94057059185E69D400EB94BA"
|
||||
BuildableName = "EZAudioWaveformFromFileExample.app"
|
||||
BlueprintName = "EZAudioWaveformFromFileExample"
|
||||
ReferencedContainer = "container:EZAudioWaveformFromFileExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
buildConfiguration = "Debug">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "9405707D185E69D400EB94BA"
|
||||
BuildableName = "EZAudioWaveformFromFileExampleTests.xctest"
|
||||
BlueprintName = "EZAudioWaveformFromFileExampleTests"
|
||||
ReferencedContainer = "container:EZAudioWaveformFromFileExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "94057059185E69D400EB94BA"
|
||||
BuildableName = "EZAudioWaveformFromFileExample.app"
|
||||
BlueprintName = "EZAudioWaveformFromFileExample"
|
||||
ReferencedContainer = "container:EZAudioWaveformFromFileExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Debug"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "94057059185E69D400EB94BA"
|
||||
BuildableName = "EZAudioWaveformFromFileExample.app"
|
||||
BlueprintName = "EZAudioWaveformFromFileExample"
|
||||
ReferencedContainer = "container:EZAudioWaveformFromFileExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Release"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "94057059185E69D400EB94BA"
|
||||
BuildableName = "EZAudioWaveformFromFileExample.app"
|
||||
BlueprintName = "EZAudioWaveformFromFileExample"
|
||||
ReferencedContainer = "container:EZAudioWaveformFromFileExample.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>SchemeUserState</key>
|
||||
<dict>
|
||||
<key>EZAudioWaveformFromFileExample.xcscheme</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>4</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>SuppressBuildableAutocreation</key>
|
||||
<dict>
|
||||
<key>94057059185E69D400EB94BA</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>9405707D185E69D400EB94BA</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -6,6 +6,9 @@ A simple, intuitive audio framework for iOS and OSX.
|
||||
*The Official EZAudio Page:*
|
||||
http://syedharisali.com/projects/EZAudio/getting-started
|
||||
|
||||
## Update
|
||||
Thank you everyone for using EZAudio! Just an update - I'm working on a 1.0.0 production version of EZAudio that will contain a bunch of improvements in the API, feature an EZAudioPlayer, and hooks for the DOUAudioStreamer for visualizing remote streaming audio. To make the next version of EZAudio even better I encourage you all to email me your feedback, feature requests, and experiences using the framework. Thanks!
|
||||
|
||||
##Features
|
||||

|
||||
|
||||
@@ -78,7 +81,7 @@ Shows how to calculate the real-time FFT of the audio data coming from the `EZMi
|
||||

|
||||
|
||||
### Documentation
|
||||
The official documentation for EZAudio can be found here: http://cocoadocs.org/docsets/EZAudio/0.0.2/
|
||||
The official documentation for EZAudio can be found here: http://cocoadocs.org/docsets/EZAudio/0.0.5/
|
||||
<br>You can also generate the docset yourself using appledocs by running the appledocs on the EZAudio source folder.
|
||||
|
||||
##Getting Started
|
||||
@@ -98,6 +101,7 @@ To begin using `EZAudio` you must first make sure you have the proper build requ
|
||||
###Frameworks
|
||||
**iOS**
|
||||
- AudioToolbox
|
||||
- AVFoundation
|
||||
- GLKit
|
||||
|
||||
|
||||
@@ -113,7 +117,7 @@ To begin using `EZAudio` you must first make sure you have the proper build requ
|
||||
You can add EZAudio to your project in a few ways: <br><br>1.) The easiest way to use EZAudio is via <a href="http://cocoapods.org/", target="_blank">Cocoapods</a>. Simply add EZAudio to your <a href="http://guides.cocoapods.org/using/the-podfile.html", target="_blank">Podfile</a> like so:
|
||||
|
||||
`
|
||||
pod 'EZAudio', '~> 0.0.2'
|
||||
pod 'EZAudio', '~> 0.0.6'
|
||||
`
|
||||
|
||||
2.) Alternatively, you could clone or fork this repo and just drag and drop the source into your project.
|
||||
@@ -328,9 +332,9 @@ Provides flexible playback to the default output device by asking the `EZOutputD
|
||||
-(TPCircularBuffer*)outputShouldUseCircularBuffer:(EZOutput *)output;
|
||||
|
||||
// Provides the audio callback with a buffer list, number of frames, and buffer size to use
|
||||
-(AudioBufferList*) output:(EZOutput*)output
|
||||
needsBufferListWithFrames:(UInt32)frames
|
||||
withBufferSize:(UInt32*)bufferSize;
|
||||
-(void) output:(EZOutput *)output
|
||||
shouldFillAudioBufferList:(AudioBufferList *)audioBufferList
|
||||
withNumberOfFrames:(UInt32)frames;
|
||||
```
|
||||
|
||||
**_Relevant Example Projects_**
|
||||
@@ -362,36 +366,22 @@ Alternatively, you could also use the shared output instance and just assign it
|
||||
One method to play back audio is to provide an AudioBufferList (for instance, reading from an `EZAudioFile`):
|
||||
```objectivec
|
||||
// Use the AudioBufferList datasource method to read from an EZAudioFile
|
||||
-(AudioBufferList *)output:(EZOutput *)output
|
||||
needsBufferListWithFrames:(UInt32)frames
|
||||
withBufferSize:(UInt32 *)bufferSize {
|
||||
if( self.audioFile ){
|
||||
|
||||
// Reached the end of the file
|
||||
if( self.eof ){
|
||||
// Here's what you do to loop the file
|
||||
[self.audioFile seekToFrame:0];
|
||||
self.eof = NO;
|
||||
}
|
||||
|
||||
// Allocate a buffer list to hold the file's data
|
||||
AudioBufferList *bufferList = [EZAudio audioBufferList];
|
||||
BOOL eof;
|
||||
-(void) output:(EZOutput *)output
|
||||
shouldFillAudioBufferList:(AudioBufferList *)audioBufferList
|
||||
withNumberOfFrames:(UInt32)frames
|
||||
{
|
||||
if( self.audioFile )
|
||||
{
|
||||
UInt32 bufferSize;
|
||||
[self.audioFile readFrames:frames
|
||||
audioBufferList:bufferList
|
||||
bufferSize:bufferSize
|
||||
eof:&eof];
|
||||
self.eof = eof;
|
||||
|
||||
// Reached the end of the file on the last read
|
||||
if( eof ){
|
||||
[EZAudio freeBufferList:bufferList];
|
||||
return nil;
|
||||
audioBufferList:audioBufferList
|
||||
bufferSize:&bufferSize
|
||||
eof:&_eof];
|
||||
if( _eof )
|
||||
{
|
||||
[self seekToFrame:0];
|
||||
}
|
||||
return bufferList;
|
||||
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
```
|
||||
####Playback Using A Circular Buffer
|
||||
|
||||
Reference in New Issue
Block a user