Compare commits

..

20 Commits

Author SHA1 Message Date
Syed Haris Ali c4fb3e27e7 moving these to new examples repo 2015-02-14 21:10:59 -08:00
Syed Haris Ali a6252b02dd adding utility classes 2015-02-14 21:08:35 -08:00
Syed Haris Ali 7f0bf3bb6c more cleanup 2015-02-14 19:23:48 -08:00
Syed Haris Ali 62b79ce6c2 got waveform data generating for interleaved and non-interleaved types 2015-02-14 19:17:50 -08:00
Syed Haris Ali 1415b5591e added copy 2015-02-14 19:01:42 -08:00
Syed Haris Ali ab892b9f74 added benchmark for speed test 2015-02-14 18:52:02 -08:00
Syed Haris Ali fde27c2db7 starting work on reading all data from audio file and then parsing data 2015-02-14 18:47:47 -08:00
Syed Haris Ali 1055e3b1dc cleaned up waveform getters and added synchronous fetching 2015-02-14 18:36:36 -08:00
Syed Haris Ali ceba0dd415 removed logging statement 2015-02-14 18:23:55 -08:00
Syed Haris Ali 6d6ae3e761 added property for total client frames in addition to total file frames 2015-02-14 18:02:30 -08:00
Syed Haris Ali 5261cb2961 fixed issue with total frames not properly recalculated for different sample rates 2015-02-14 16:33:40 -08:00
Syed Haris Ali 65491d453d added better way to obtain waveform data for all channels 2015-02-14 16:16:29 -08:00
Syed Haris Ali 4389a48875 made more progress, read is working and added mutex to prevent bogus buffer list err 2015-02-12 00:31:51 -08:00
Syed Haris Ali 3b4382986f trying to figure out why read is failing 2015-02-11 23:15:00 -08:00
Syed Haris Ali b327e5b140 added file format to initializer 2015-02-11 23:02:21 -08:00
Syed Haris Ali 73adf239ec added back metadata method 2015-02-11 23:02:21 -08:00
Syed Haris Ali c2c05b683b bit more cleanup in header 2015-02-11 23:02:21 -08:00
Syed Haris Ali 0bdade977d got simple initWithURL method back 2015-02-11 23:02:21 -08:00
Syed Haris Ali 3b0f32abaa added more robust file opening method 2015-02-11 23:02:21 -08:00
Syed Haris Ali 804cb1c3ef refactored process of creating EZAudioFile to allow read/write permissions on existing or new files 2015-02-11 23:02:21 -08:00
227 changed files with 13932 additions and 13566 deletions
Vendored
BIN
View File
Binary file not shown.
-17
View File
@@ -1,17 +0,0 @@
.DS_Store
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
*.xccheckout
*.moved-aside
DerivedData
*.hmap
*.ipa
*.xcuserstate
-168
View File
@@ -1,168 +0,0 @@
//
// EZAudioDevice.h
// MicrophoneTest
//
// Created by Syed Haris Ali on 4/3/15.
// Copyright (c) 2015 Syed Haris Ali. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
#if TARGET_OS_IPHONE
#import <AVFoundation/AVFoundation.h>
#elif TARGET_OS_MAC
#endif
/**
The EZAudioDevice provides an interface for getting the available input and output hardware devices on iOS and OSX. On iOS the EZAudioDevice uses the available devices found from the AVAudioSession, while on OSX the EZAudioDevice wraps the AudioHardware API to find any devices that are connected including the built-in devices (for instance, Built-In Microphone, Display Audio). Since the AVAudioSession and AudioHardware APIs are quite different the EZAudioDevice has different properties available on each platform. The EZMicrophone now supports setting any specific EZAudioDevice from the `inputDevices` function.
*/
@interface EZAudioDevice : NSObject
//------------------------------------------------------------------------------
#pragma mark - Class Methods
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// @name Getting The Devices
//------------------------------------------------------------------------------
/**
Enumerates all the available input devices and returns the result in an NSArray of EZAudioDevice instances.
@return An NSArray containing EZAudioDevice instances, one for each available input device.
*/
+ (NSArray *)inputDevices;
//------------------------------------------------------------------------------
/**
Enumerates all the available output devices and returns the result in an NSArray of EZAudioDevice instances.
@return An NSArray of output EZAudioDevice instances.
*/
+ (NSArray *)outputDevices;
#if TARGET_OS_IPHONE
/**
Provides the current EZAudioDevice that is being used to pull input.
- iOS only
@return An EZAudioDevice instance representing the currently selected input device.
*/
+ (EZAudioDevice *)currentInputDevice;
/**
Provides the current EZAudioDevice that is being used to output audio.
- iOS only
@return An EZAudioDevice instance representing the currently selected ouotput device.
*/
+ (EZAudioDevice *)currentOutputDevice;
//------------------------------------------------------------------------------
/**
Enumerates all the available input devices.
- iOS only
@param block When enumerating this block executes repeatedly for each EZAudioDevice found. It contains two arguments - first, the EZAudioDevice found, then a pointer to a stop BOOL to allow breaking out of the enumeration)
*/
+ (void)enumerateInputDevicesUsingBlock:(void(^)(EZAudioDevice *device,
BOOL *stop))block;
//------------------------------------------------------------------------------
/**
Enumerates all the available output devices.
- iOS only
@param block When enumerating this block executes repeatedly for each EZAudioDevice found. It contains two arguments - first, the EZAudioDevice found, then a pointer to a stop BOOL to allow breaking out of the enumeration)
*/
+ (void)enumerateOutputDevicesUsingBlock:(void (^)(EZAudioDevice *device,
BOOL *stop))block;
#elif TARGET_OS_MAC
/**
Enumerates all the available devices and returns the result in an NSArray of EZAudioDevice instances.
- OSX only
@return An NSArray of input and output EZAudioDevice instances.
*/
+ (NSArray *)devices;
//------------------------------------------------------------------------------
/**
Enumerates all the available devices.
- OSX only
@param block When enumerating this block executes repeatedly for each EZAudioDevice found. It contains two arguments - first, the EZAudioDevice found, then a pointer to a stop BOOL to allow breaking out of the enumeration)
*/
+ (void)enumerateDevicesUsingBlock:(void(^)(EZAudioDevice *device,
BOOL *stop))block;
#endif
//------------------------------------------------------------------------------
#pragma mark - Properties
//------------------------------------------------------------------------------
/**
An NSString representing a human-reable version of the device.
*/
@property (nonatomic, copy, readonly) NSString *name;
#if TARGET_OS_IPHONE
/**
An AVAudioSessionPortDescription describing an input or output hardware port.
- iOS only
*/
@property (nonatomic, strong, readonly) AVAudioSessionPortDescription *port;
//------------------------------------------------------------------------------
/**
An AVAudioSessionDataSourceDescription describing a specific data source for the `port` provided.
- iOS only
*/
@property (nonatomic, strong, readonly) AVAudioSessionDataSourceDescription *dataSource;
#elif TARGET_OS_MAC
/**
An AudioDeviceID representing the device in the AudioHardware API.
- OSX only
*/
@property (nonatomic, assign, readonly) AudioDeviceID deviceID;
//------------------------------------------------------------------------------
/**
An NSString representing the name of the manufacturer of the device.
- OSX only
*/
@property (nonatomic, copy, readonly) NSString *manufacturer;
//------------------------------------------------------------------------------
/**
An NSInteger representing the number of input channels available.
- OSX only
*/
@property (nonatomic, assign, readonly) NSInteger inputChannelCount;
//------------------------------------------------------------------------------
/**
An NSInteger representing the number of output channels available.
- OSX only
*/
@property (nonatomic, assign, readonly) NSInteger outputChannelCount;
//------------------------------------------------------------------------------
/**
An NSString representing the persistent identifier for the AudioDevice.
- OSX only
*/
@property (nonatomic, copy, readonly) NSString *UID;
#endif
@end
-420
View File
@@ -1,420 +0,0 @@
//
// EZAudioDevice.m
// MicrophoneTest
//
// Created by Syed Haris Ali on 4/3/15.
// Copyright (c) 2015 Syed Haris Ali. All rights reserved.
//
#import "EZAudioDevice.h"
#import "EZAudioUtilities.h"
@interface EZAudioDevice ()
@property (nonatomic, copy, readwrite) NSString *name;
#if TARGET_OS_IPHONE
@property (nonatomic, strong, readwrite) AVAudioSessionPortDescription *port;
@property (nonatomic, strong, readwrite) AVAudioSessionDataSourceDescription *dataSource;
#elif TARGET_OS_MAC
@property (nonatomic, assign, readwrite) AudioDeviceID deviceID;
@property (nonatomic, copy, readwrite) NSString *manufacturer;
@property (nonatomic, assign, readwrite) NSInteger inputChannelCount;
@property (nonatomic, assign, readwrite) NSInteger outputChannelCount;
@property (nonatomic, copy, readwrite) NSString *UID;
#endif
@end
@implementation EZAudioDevice
#if TARGET_OS_IPHONE
//------------------------------------------------------------------------------
+ (EZAudioDevice *)currentInputDevice
{
AVAudioSession *session = [AVAudioSession sharedInstance];
AVAudioSessionPortDescription *port = [[[session currentRoute] inputs] firstObject];
AVAudioSessionDataSourceDescription *dataSource = [session inputDataSource];
EZAudioDevice *device = [[EZAudioDevice alloc] init];
device.port = port;
device.dataSource = dataSource;
return device;
}
//------------------------------------------------------------------------------
+ (EZAudioDevice *)currentOutputDevice
{
AVAudioSession *session = [AVAudioSession sharedInstance];
AVAudioSessionPortDescription *port = [[[session currentRoute] outputs] firstObject];
AVAudioSessionDataSourceDescription *dataSource = [session outputDataSource];
EZAudioDevice *device = [[EZAudioDevice alloc] init];
device.port = port;
device.dataSource = dataSource;
return device;
}
//------------------------------------------------------------------------------
+ (NSArray *)inputDevices
{
__block NSMutableArray *devices = [NSMutableArray array];
[self enumerateInputDevicesUsingBlock:^(EZAudioDevice *device, BOOL *stop)
{
[devices addObject:device];
}];
return devices;
}
//------------------------------------------------------------------------------
+ (NSArray *)outputDevices
{
__block NSMutableArray *devices = [NSMutableArray array];
[self enumerateOutputDevicesUsingBlock:^(EZAudioDevice *device, BOOL *stop)
{
[devices addObject:device];
}];
return devices;
}
//------------------------------------------------------------------------------
+ (void)enumerateInputDevicesUsingBlock:(void (^)(EZAudioDevice *, BOOL *))block
{
if (!block)
{
return;
}
NSArray *inputs = [[AVAudioSession sharedInstance] availableInputs];
if (inputs == nil)
{
NSLog(@"Audio session is not active! In order to enumerate the audio devices you must set the category and set active the audio session for your iOS app before calling this function.");
return;
}
BOOL stop;
for (AVAudioSessionPortDescription *inputDevicePortDescription in inputs)
{
// add any additional sub-devices
NSArray *dataSources = [inputDevicePortDescription dataSources];
if (dataSources.count)
{
for (AVAudioSessionDataSourceDescription *inputDeviceDataSourceDescription in dataSources)
{
EZAudioDevice *device = [[EZAudioDevice alloc] init];
device.port = inputDevicePortDescription;
device.dataSource = inputDeviceDataSourceDescription;
block(device, &stop);
}
}
else
{
EZAudioDevice *device = [[EZAudioDevice alloc] init];
device.port = inputDevicePortDescription;
block(device, &stop);
}
}
}
//------------------------------------------------------------------------------
+ (void)enumerateOutputDevicesUsingBlock:(void (^)(EZAudioDevice *, BOOL *))block
{
if (!block)
{
return;
}
AVAudioSessionRouteDescription *currentRoute = [[AVAudioSession sharedInstance] currentRoute];
NSArray *portDescriptions = [currentRoute outputs];
BOOL stop;
for (AVAudioSessionPortDescription *outputDevicePortDescription in portDescriptions)
{
// add any additional sub-devices
NSArray *dataSources = [outputDevicePortDescription dataSources];
if (dataSources.count)
{
for (AVAudioSessionDataSourceDescription *outputDeviceDataSourceDescription in dataSources)
{
EZAudioDevice *device = [[EZAudioDevice alloc] init];
device.port = outputDevicePortDescription;
device.dataSource = outputDeviceDataSourceDescription;
block(device, &stop);
}
}
else
{
EZAudioDevice *device = [[EZAudioDevice alloc] init];
device.port = outputDevicePortDescription;
block(device, &stop);
}
}
}
//------------------------------------------------------------------------------
- (NSString *)name
{
NSMutableString *name = [NSMutableString string];
if (self.port)
{
[name appendString:self.port.portName];
}
if (self.dataSource)
{
[name appendFormat:@": %@", self.dataSource.dataSourceName];
}
return name;
}
//------------------------------------------------------------------------------
- (NSString *)description
{
return [NSString stringWithFormat:@"%@ { port: %@, data source: %@ }",
[super description],
self.port,
self.dataSource];
}
//------------------------------------------------------------------------------
- (BOOL)isEqual:(id)object
{
if ([object isKindOfClass:self.class])
{
EZAudioDevice *device = (EZAudioDevice *)object;
BOOL isPortUIDEqual = [device.port.UID isEqualToString:self.port.UID];
BOOL isDataSourceIDEqual = device.dataSource.dataSourceID.longValue == self.dataSource.dataSourceID.longValue;
return isPortUIDEqual && isDataSourceIDEqual;
}
else
{
return [super isEqual:object];
}
}
#elif TARGET_OS_MAC
+ (void)enumerateDevicesUsingBlock:(void(^)(EZAudioDevice *device,
BOOL *stop))block
{
if (!block)
{
return;
}
// get the present system devices
AudioObjectPropertyAddress address = [self addressForPropertySelector:kAudioHardwarePropertyDevices];
UInt32 devicesDataSize;
[EZAudioUtilities checkResult:AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
&address,
0,
NULL,
&devicesDataSize)
operation:"Failed to get data size"];
// enumerate devices
NSInteger count = devicesDataSize / sizeof(AudioDeviceID);
AudioDeviceID *deviceIDs = (AudioDeviceID *)malloc(devicesDataSize);
// fill in the devices
[EZAudioUtilities checkResult:AudioObjectGetPropertyData(kAudioObjectSystemObject,
&address,
0,
NULL,
&devicesDataSize,
deviceIDs)
operation:"Failed to get device IDs for available devices on OSX"];
BOOL stop = NO;
for (UInt32 i = 0; i < count; i++)
{
AudioDeviceID deviceID = deviceIDs[i];
EZAudioDevice *device = [[EZAudioDevice alloc] init];
device.deviceID = deviceID;
device.manufacturer = [self manufacturerForDeviceID:deviceID];
device.name = [self namePropertyForDeviceID:deviceID];
device.UID = [self UIDPropertyForDeviceID:deviceID];
device.inputChannelCount = [self channelCountForScope:kAudioObjectPropertyScopeInput forDeviceID:deviceID];
device.outputChannelCount = [self channelCountForScope:kAudioObjectPropertyScopeOutput forDeviceID:deviceID];
block(device, &stop);
if (stop)
{
break;
}
}
free(deviceIDs);
}
//------------------------------------------------------------------------------
+ (NSArray *)devices
{
__block NSMutableArray *devices = [NSMutableArray array];
[self enumerateDevicesUsingBlock:^(EZAudioDevice *device, BOOL *stop)
{
[devices addObject:device];
}];
return devices;
}
//------------------------------------------------------------------------------
+ (NSArray *)inputDevices
{
__block NSMutableArray *devices = [NSMutableArray array];
[self enumerateDevicesUsingBlock:^(EZAudioDevice *device, BOOL *stop)
{
if (device.inputChannelCount > 0)
{
[devices addObject:device];
}
}];
return devices;
}
//------------------------------------------------------------------------------
+ (NSArray *)outputDevices
{
__block NSMutableArray *devices = [NSMutableArray array];
[self enumerateDevicesUsingBlock:^(EZAudioDevice *device, BOOL *stop)
{
if (device.outputChannelCount > 0)
{
[devices addObject:device];
}
}];
return devices;
}
//------------------------------------------------------------------------------
#pragma mark - Utility
//------------------------------------------------------------------------------
+ (AudioObjectPropertyAddress)addressForPropertySelector:(AudioObjectPropertySelector)selector
{
AudioObjectPropertyAddress address;
address.mScope = kAudioObjectPropertyScopeGlobal;
address.mElement = kAudioObjectPropertyElementMaster;
address.mSelector = selector;
return address;
}
//------------------------------------------------------------------------------
+ (NSString *)stringPropertyForSelector:(AudioObjectPropertySelector)selector
withDeviceID:(AudioDeviceID)deviceID
{
AudioObjectPropertyAddress address = [self addressForPropertySelector:selector];
CFStringRef string;
UInt32 propSize = sizeof(CFStringRef);
NSString *errorString = [NSString stringWithFormat:@"Failed to get device property (%u)",(unsigned int)selector];
[EZAudioUtilities checkResult:AudioObjectGetPropertyData(deviceID,
&address,
0,
NULL,
&propSize,
&string)
operation:errorString.UTF8String];
return (__bridge_transfer NSString *)string;
}
//------------------------------------------------------------------------------
+ (NSInteger)channelCountForScope:(AudioObjectPropertyScope)scope
forDeviceID:(AudioDeviceID)deviceID
{
AudioObjectPropertyAddress address;
address.mScope = scope;
address.mElement = kAudioObjectPropertyElementMaster;
address.mSelector = kAudioDevicePropertyStreamConfiguration;
AudioBufferList streamConfiguration;
UInt32 propSize = sizeof(streamConfiguration);
[EZAudioUtilities checkResult:AudioObjectGetPropertyData(deviceID,
&address,
0,
NULL,
&propSize,
&streamConfiguration)
operation:"Failed to get frame size"];
NSInteger channelCount = 0;
for (NSInteger i = 0; i < streamConfiguration.mNumberBuffers; i++)
{
channelCount += streamConfiguration.mBuffers[i].mNumberChannels;
}
return channelCount;
}
//------------------------------------------------------------------------------
+ (NSString *)manufacturerForDeviceID:(AudioDeviceID)deviceID
{
return [self stringPropertyForSelector:kAudioDevicePropertyDeviceManufacturerCFString
withDeviceID:deviceID];
}
//------------------------------------------------------------------------------
+ (NSString *)namePropertyForDeviceID:(AudioDeviceID)deviceID
{
return [self stringPropertyForSelector:kAudioDevicePropertyDeviceNameCFString
withDeviceID:deviceID];
}
//------------------------------------------------------------------------------
+ (NSString *)UIDPropertyForDeviceID:(AudioDeviceID)deviceID
{
return [self stringPropertyForSelector:kAudioDevicePropertyDeviceUID
withDeviceID:deviceID];
}
//------------------------------------------------------------------------------
- (NSString *)description
{
return [NSString stringWithFormat:@"%@ { deviceID: %i, manufacturer: %@, name: %@, UID: %@, inputChannelCount: %ld, outputChannelCount: %ld }",
[super description],
self.deviceID,
self.manufacturer,
self.name,
self.UID,
self.inputChannelCount,
self.outputChannelCount];
}
//------------------------------------------------------------------------------
- (BOOL)isEqual:(id)object
{
if ([object isKindOfClass:self.class])
{
EZAudioDevice *device = (EZAudioDevice *)object;
return [self.UID isEqualToString:device.UID];
}
else
{
return [super isEqual:object];
}
}
//------------------------------------------------------------------------------
#endif
@end
-701
View File
@@ -1,701 +0,0 @@
//
// EZAudioFile.m
// EZAudio
//
// Created by Syed Haris Ali on 12/1/13.
// Copyright (c) 2013 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 "EZAudioFile.h"
#import "EZAudioUtilities.h"
#import "EZAudioFloatConverter.h"
#import "EZAudioFloatData.h"
#include <pthread.h>
// constants
static UInt32 EZAudioFileWaveformDefaultResolution = 1024;
static NSString *EZAudioFileWaveformDataQueueIdentifier = @"com.ezaudio.waveformQueue";
//------------------------------------------------------------------------------
typedef struct
{
AudioFileID audioFileID;
AudioStreamBasicDescription clientFormat;
NSTimeInterval duration;
ExtAudioFileRef extAudioFileRef;
AudioStreamBasicDescription fileFormat;
SInt64 frames;
CFURLRef sourceURL;
} EZAudioFileInfo;
//------------------------------------------------------------------------------
#pragma mark - EZAudioFile
//------------------------------------------------------------------------------
@interface EZAudioFile ()
@property (nonatomic, strong) EZAudioFloatConverter *floatConverter;
@property (nonatomic) float **floatData;
@property (nonatomic) EZAudioFileInfo *info;
@property (nonatomic) pthread_mutex_t lock;
@property (nonatomic) dispatch_queue_t waveformQueue;
@end
//------------------------------------------------------------------------------
@implementation EZAudioFile
//------------------------------------------------------------------------------
#pragma mark - Dealloc
//------------------------------------------------------------------------------
- (void)dealloc
{
self.floatConverter = nil;
pthread_mutex_destroy(&_lock);
[EZAudioUtilities freeFloatBuffers:self.floatData numberOfChannels:self.clientFormat.mChannelsPerFrame];
[EZAudioUtilities checkResult:ExtAudioFileDispose(self.info->extAudioFileRef) operation:"Failed to dispose of ext audio file"];
free(self.info);
}
//------------------------------------------------------------------------------
#pragma mark - Initialization
//------------------------------------------------------------------------------
- (instancetype)init
{
self = [super init];
if (self)
{
self.info = (EZAudioFileInfo *)malloc(sizeof(EZAudioFileInfo));
_floatData = NULL;
pthread_mutex_init(&_lock, NULL);
_waveformQueue = dispatch_queue_create(EZAudioFileWaveformDataQueueIdentifier.UTF8String, DISPATCH_QUEUE_PRIORITY_DEFAULT);
}
return self;
}
//------------------------------------------------------------------------------
- (instancetype)initWithURL:(NSURL *)url
{
return [self initWithURL:url delegate:nil];
}
//------------------------------------------------------------------------------
- (instancetype)initWithURL:(NSURL *)url
delegate:(id<EZAudioFileDelegate>)delegate
{
return [self initWithURL:url
delegate:delegate
clientFormat:[self.class defaultClientFormat]];
}
//------------------------------------------------------------------------------
- (instancetype)initWithURL:(NSURL *)url
delegate:(id<EZAudioFileDelegate>)delegate
clientFormat:(AudioStreamBasicDescription)clientFormat
{
self = [self init];
if (self)
{
self.info->sourceURL = (__bridge CFURLRef)(url);
self.info->clientFormat = clientFormat;
self.delegate = delegate;
if (![self setup])
{
return nil;
}
}
return self;
}
//------------------------------------------------------------------------------
#pragma mark - Class Initializers
//------------------------------------------------------------------------------
+ (instancetype)audioFileWithURL:(NSURL *)url
{
return [[self alloc] initWithURL:url];
}
//------------------------------------------------------------------------------
+ (instancetype)audioFileWithURL:(NSURL *)url
delegate:(id<EZAudioFileDelegate>)delegate
{
return [[self alloc] initWithURL:url delegate:delegate];
}
//------------------------------------------------------------------------------
+ (instancetype)audioFileWithURL:(NSURL *)url
delegate:(id<EZAudioFileDelegate>)delegate
clientFormat:(AudioStreamBasicDescription)clientFormat
{
return [[self alloc] initWithURL:url
delegate:delegate
clientFormat:clientFormat];
}
//------------------------------------------------------------------------------
#pragma mark - NSCopying
//------------------------------------------------------------------------------
- (id)copyWithZone:(NSZone *)zone
{
return [EZAudioFile audioFileWithURL:self.url];
}
//------------------------------------------------------------------------------
#pragma mark - Class Methods
//------------------------------------------------------------------------------
+ (AudioStreamBasicDescription)defaultClientFormat
{
return [EZAudioUtilities stereoFloatNonInterleavedFormatWithSampleRate:[self defaultClientFormatSampleRate]];
}
//------------------------------------------------------------------------------
+ (Float64)defaultClientFormatSampleRate
{
return 44100.0f;
}
//------------------------------------------------------------------------------
+ (NSArray *)supportedAudioFileTypes
{
return @
[
@"aac",
@"caf",
@"aif",
@"aiff",
@"aifc",
@"mp3",
@"mp4",
@"m4a",
@"snd",
@"au",
@"sd2",
@"wav"
];
}
//------------------------------------------------------------------------------
#pragma mark - Setup
//------------------------------------------------------------------------------
- (BOOL)setup
{
//
// Try to open the file, bail if the file could not be opened
//
BOOL success = [self openAudioFile];
if (!success)
{
return success;
}
//
// Set the client format
//
self.clientFormat = self.info->clientFormat;
return YES;
}
//------------------------------------------------------------------------------
#pragma mark - Creating/Opening Audio File
//------------------------------------------------------------------------------
- (BOOL)openAudioFile
{
//
// Need a source url
//
NSAssert(self.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 an ExtAudioFileRef for the file handle
//
if (fileExists)
{
[EZAudioUtilities checkResult:ExtAudioFileOpenURL(url, &self.info->extAudioFileRef)
operation:"Failed to create ExtAudioFileRef"];
}
else
{
return NO;
}
//
// Get the underlying AudioFileID
//
UInt32 propSize = sizeof(self.info->audioFileID);
[EZAudioUtilities checkResult:ExtAudioFileGetProperty(self.info->extAudioFileRef,
kExtAudioFileProperty_AudioFile,
&propSize,
&self.info->audioFileID)
operation:"Failed to get underlying AudioFileID"];
//
// Store the file format
//
propSize = sizeof(self.info->fileFormat);
[EZAudioUtilities checkResult:ExtAudioFileGetProperty(self.info->extAudioFileRef,
kExtAudioFileProperty_FileDataFormat,
&propSize,
&self.info->fileFormat)
operation:"Failed to get file audio format on existing audio file"];
//
// Get the total frames and duration
//
propSize = sizeof(SInt64);
[EZAudioUtilities checkResult:ExtAudioFileGetProperty(self.info->extAudioFileRef,
kExtAudioFileProperty_FileLengthFrames,
&propSize,
&self.info->frames)
operation:"Failed to get total frames"];
self.info->duration = (NSTimeInterval) self.info->frames / self.info->fileFormat.mSampleRate;
return YES;
}
//------------------------------------------------------------------------------
#pragma mark - Events
//------------------------------------------------------------------------------
- (void)readFrames:(UInt32)frames
audioBufferList:(AudioBufferList *)audioBufferList
bufferSize:(UInt32 *)bufferSize
eof:(BOOL *)eof
{
if (pthread_mutex_trylock(&_lock) == 0)
{
// perform read
[EZAudioUtilities checkResult:ExtAudioFileRead(self.info->extAudioFileRef,
&frames,
audioBufferList)
operation:"Failed to read audio data from file"];
*bufferSize = frames;
*eof = frames == 0;
// 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:)])
{
// convert into float data
[self.floatConverter convertDataFromAudioBufferList:audioBufferList
withNumberOfFrames:*bufferSize
toFloatBuffers:self.floatData];
// notify delegate
UInt32 channels = self.clientFormat.mChannelsPerFrame;
[self.delegate audioFile:self
readAudio:self.floatData
withBufferSize:*bufferSize
withNumberOfChannels:channels];
}
pthread_mutex_unlock(&_lock);
}
}
//------------------------------------------------------------------------------
- (void)seekToFrame:(SInt64)frame
{
if (pthread_mutex_trylock(&_lock) == 0)
{
[EZAudioUtilities 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
//------------------------------------------------------------------------------
- (AudioStreamBasicDescription)floatFormat
{
return [EZAudioUtilities stereoFloatNonInterleavedFormatWithSampleRate:44100.0f];
}
//------------------------------------------------------------------------------
- (EZAudioFloatData *)getWaveformData
{
return [self getWaveformDataWithNumberOfPoints:EZAudioFileWaveformDefaultResolution];
}
//------------------------------------------------------------------------------
- (EZAudioFloatData *)getWaveformDataWithNumberOfPoints:(UInt32)numberOfPoints
{
EZAudioFloatData *waveformData;
if (pthread_mutex_trylock(&_lock) == 0)
{
// store current frame
SInt64 currentFrame = self.frameIndex;
BOOL interleaved = [EZAudioUtilities isInterleaved:self.clientFormat];
UInt32 channels = self.clientFormat.mChannelsPerFrame;
float **data = (float **)malloc( sizeof(float*) * channels );
for (int i = 0; i < channels; i++)
{
data[i] = (float *)malloc( sizeof(float) * numberOfPoints );
}
// seek to 0
[EZAudioUtilities checkResult:ExtAudioFileSeek(self.info->extAudioFileRef,
0)
operation:"Failed to seek frame position within audio file"];
// calculate the required number of frames per buffer
SInt64 framesPerBuffer = ((SInt64) self.totalClientFrames / numberOfPoints);
SInt64 framesPerChannel = framesPerBuffer / channels;
// allocate an audio buffer list
AudioBufferList *audioBufferList = [EZAudioUtilities audioBufferListWithNumberOfFrames:(UInt32)framesPerBuffer
numberOfChannels:self.info->clientFormat.mChannelsPerFrame
interleaved:interleaved];
// read through file and calculate rms at each point
for (SInt64 i = 0; i < numberOfPoints; i++)
{
UInt32 bufferSize = (UInt32) framesPerBuffer;
[EZAudioUtilities checkResult:ExtAudioFileRead(self.info->extAudioFileRef,
&bufferSize,
audioBufferList)
operation:"Failed to read audio data from file waveform"];
if (interleaved)
{
float *buffer = (float *)audioBufferList->mBuffers[0].mData;
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 = [EZAudioUtilities RMS:channelData length:(UInt32)framesPerChannel];
data[channel][i] = rms;
}
}
else
{
for (int channel = 0; channel < channels; channel++)
{
float *channelData = audioBufferList->mBuffers[channel].mData;
float rms = [EZAudioUtilities RMS:channelData length:bufferSize];
data[channel][i] = rms;
}
}
}
// clean up
[EZAudioUtilities freeBufferList:audioBufferList];
// seek back to previous position
[EZAudioUtilities checkResult:ExtAudioFileSeek(self.info->extAudioFileRef,
currentFrame)
operation:"Failed to seek frame position within audio file"];
pthread_mutex_unlock(&_lock);
waveformData = [EZAudioFloatData dataWithNumberOfChannels:channels
buffers:(float **)data
bufferSize:numberOfPoints];
// cleanup
for (int i = 0; i < channels; i++)
{
free(data[i]);
}
free(data);
}
return waveformData;
}
//------------------------------------------------------------------------------
- (void)getWaveformDataWithCompletionBlock:(EZAudioWaveformDataCompletionBlock)waveformDataCompletionBlock
{
[self getWaveformDataWithNumberOfPoints:EZAudioFileWaveformDefaultResolution
completion:waveformDataCompletionBlock];
}
//------------------------------------------------------------------------------
- (void)getWaveformDataWithNumberOfPoints:(UInt32)numberOfPoints
completion:(EZAudioWaveformDataCompletionBlock)completion
{
if (!completion)
{
return;
}
// async get waveform data
__weak EZAudioFile *weakSelf = self;
dispatch_async(self.waveformQueue, ^{
EZAudioFloatData *waveformData = [weakSelf getWaveformDataWithNumberOfPoints:numberOfPoints];
dispatch_async(dispatch_get_main_queue(), ^{
completion(waveformData.buffers, waveformData.bufferSize);
});
});
}
//------------------------------------------------------------------------------
- (AudioStreamBasicDescription)clientFormat
{
return self.info->clientFormat;
}
//------------------------------------------------------------------------------
- (NSTimeInterval)currentTime
{
return [EZAudioUtilities MAP:(float)[self frameIndex]
leftMin:0.0f
leftMax:(float)[self totalFrames]
rightMin:0.0f
rightMax:[self duration]];
}
//------------------------------------------------------------------------------
- (NSTimeInterval)duration
{
return self.info->duration;
}
//------------------------------------------------------------------------------
- (AudioStreamBasicDescription)fileFormat
{
return self.info->fileFormat;
}
//------------------------------------------------------------------------------
- (NSString *)formattedCurrentTime
{
return [EZAudioUtilities displayTimeStringFromSeconds:[self currentTime]];
}
//------------------------------------------------------------------------------
- (NSString *)formattedDuration
{
return [EZAudioUtilities displayTimeStringFromSeconds:[self duration]];
}
//------------------------------------------------------------------------------
- (SInt64)frameIndex
{
SInt64 frameIndex;
[EZAudioUtilities checkResult:ExtAudioFileTell(self.info->extAudioFileRef, &frameIndex)
operation:"Failed to get frame index"];
return frameIndex;
}
//------------------------------------------------------------------------------
- (NSDictionary *)metadata
{
// get size of metadata property (dictionary)
UInt32 propSize = sizeof(self.info->audioFileID);
CFDictionaryRef metadata;
UInt32 writable;
[EZAudioUtilities checkResult:AudioFileGetPropertyInfo(self.info->audioFileID,
kAudioFilePropertyInfoDictionary,
&propSize,
&writable)
operation:"Failed to get the size of the metadata dictionary"];
// pull metadata
[EZAudioUtilities checkResult:AudioFileGetProperty(self.info->audioFileID,
kAudioFilePropertyInfoDictionary,
&propSize,
&metadata)
operation:"Failed to get metadata dictionary"];
// cast to NSDictionary
return (__bridge NSDictionary*)metadata;
}
//------------------------------------------------------------------------------
- (NSTimeInterval)totalDuration
{
return self.info->duration;
}
//------------------------------------------------------------------------------
- (SInt64)totalClientFrames
{
SInt64 totalFrames = [self totalFrames];
AudioStreamBasicDescription clientFormat = self.info->clientFormat;
AudioStreamBasicDescription fileFormat = self.info->fileFormat;
BOOL sameSampleRate = clientFormat.mSampleRate == fileFormat.mSampleRate;
if (!sameSampleRate)
{
totalFrames = self.info->duration * clientFormat.mSampleRate;
}
return totalFrames;
}
//------------------------------------------------------------------------------
- (SInt64)totalFrames
{
return self.info->frames;
}
//------------------------------------------------------------------------------
- (NSURL *)url
{
return (__bridge NSURL*)self.info->sourceURL;
}
//------------------------------------------------------------------------------
#pragma mark - Setters
//------------------------------------------------------------------------------
- (void)setClientFormat:(AudioStreamBasicDescription)clientFormat
{
//
// Clear any float data currently cached
//
if (self.floatData)
{
self.floatData = nil;
}
//
// Client format can only be linear PCM!
//
NSAssert([EZAudioUtilities isLinearPCM:clientFormat], @"Client format must be linear PCM");
//
// Store the client format
//
self.info->clientFormat = clientFormat;
//
// Set the client format on the ExtAudioFileRef
//
[EZAudioUtilities checkResult:ExtAudioFileSetProperty(self.info->extAudioFileRef,
kExtAudioFileProperty_ClientDataFormat,
sizeof(clientFormat),
&clientFormat)
operation:"Couldn't set client data format on file"];
//
// Create a new float converter using the client format as the input format
//
self.floatConverter = [EZAudioFloatConverter converterWithInputFormat:clientFormat];
//
// Determine how big our float buffers need to be to hold a buffer of float
// data for the audio received callback.
//
UInt32 maxPacketSize;
UInt32 propSize = sizeof(maxPacketSize);
[EZAudioUtilities checkResult:ExtAudioFileGetProperty(self.info->extAudioFileRef,
kExtAudioFileProperty_ClientMaxPacketSize,
&propSize,
&maxPacketSize)
operation:"Failed to get max packet size"];
self.floatData = [EZAudioUtilities floatBuffersWithNumberOfFrames:1024
numberOfChannels:self.clientFormat.mChannelsPerFrame];
}
//------------------------------------------------------------------------------
- (void)setCurrentTime:(NSTimeInterval)currentTime
{
NSAssert(currentTime < [self duration], @"Invalid seek operation, expected current time to be less than duration");
SInt64 frame = [EZAudioUtilities MAP:currentTime
leftMin:0.0f
leftMax:[self duration]
rightMin:0.0f
rightMax:[self totalFrames]];
[self seekToFrame:frame];
}
//------------------------------------------------------------------------------
#pragma mark - Description
//------------------------------------------------------------------------------
- (NSString *)description
{
return [NSString stringWithFormat:@"%@ {\n"
" url: %@,\n"
" duration: %f,\n"
" totalFrames: %lld,\n"
" metadata: %@,\n"
" fileFormat: { %@ },\n"
" clientFormat: { %@ } \n"
"}",
[super description],
[self url],
[self duration],
[self totalFrames],
[self metadata],
[EZAudioUtilities stringForAudioStreamBasicDescription:[self fileFormat]],
[EZAudioUtilities stringForAudioStreamBasicDescription:[self clientFormat]]];
}
//------------------------------------------------------------------------------
@end
-414
View File
@@ -1,414 +0,0 @@
//
// 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 "EZAudioFile.h"
#import "EZOutput.h"
@class EZAudioPlayer;
//------------------------------------------------------------------------------
#pragma mark - Notifications
//------------------------------------------------------------------------------
/**
Notification that occurs whenever the EZAudioPlayer changes its `audioFile` property. Check the new value using the EZAudioPlayer's `audioFile` property.
*/
FOUNDATION_EXPORT NSString * const EZAudioPlayerDidChangeAudioFileNotification;
/**
Notification that occurs whenever the EZAudioPlayer changes its `device` property. Check the new value using the EZAudioPlayer's `device` property.
*/
FOUNDATION_EXPORT NSString * const EZAudioPlayerDidChangeOutputDeviceNotification;
/**
Notification that occurs whenever the EZAudioPlayer changes its `output` component's `pan` property. Check the new value using the EZAudioPlayer's `pan` property.
*/
FOUNDATION_EXPORT NSString * const EZAudioPlayerDidChangePanNotification;
/**
Notification that occurs whenever the EZAudioPlayer changes its `output` component's play state. Check the new value using the EZAudioPlayer's `isPlaying` property.
*/
FOUNDATION_EXPORT NSString * const EZAudioPlayerDidChangePlayStateNotification;
/**
Notification that occurs whenever the EZAudioPlayer changes its `output` component's `volume` property. Check the new value using the EZAudioPlayer's `volume` property.
*/
FOUNDATION_EXPORT NSString * const EZAudioPlayerDidChangeVolumeNotification;
/**
Notification that occurs whenever the EZAudioPlayer has reached the end of a file and its `shouldLoop` property has been set to NO.
*/
FOUNDATION_EXPORT NSString * const EZAudioPlayerDidReachEndOfFileNotification;
/**
Notification that occurs whenever the EZAudioPlayer performs a seek via the `seekToFrame` method or `setCurrentTime:` property setter. Check the new `currentTime` or `frameIndex` value using the EZAudioPlayer's `currentTime` or `frameIndex` property, respectively.
*/
FOUNDATION_EXPORT NSString * const EZAudioPlayerDidSeekNotification;
//------------------------------------------------------------------------------
#pragma mark - EZAudioPlayerDelegate
//------------------------------------------------------------------------------
/**
The EZAudioPlayerDelegate provides event callbacks for the EZAudioPlayer. Since 0.5.0 the EZAudioPlayerDelegate provides a smaller set of delegate methods in favor of notifications to allow multiple receivers of the EZAudioPlayer event callbacks since only one player is typically used in an application. Specifically, these methods are provided for high frequency callbacks that wrap the EZAudioPlayer's internal EZAudioFile and EZOutput instances.
@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'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
playedAudio:(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
//------------------------------------------------------------------------------
#pragma mark - EZAudioPlayer
//------------------------------------------------------------------------------
/**
The EZAudioPlayer provides an interface that combines the EZAudioFile and EZOutput to play local audio files. This class acts as the master delegate (the EZAudioFileDelegate) over whatever EZAudioFile instance, the `audioFile` property, it is using for playback as well as the EZOutputDelegate and EZOutputDataSource over whatever EZOutput instance is set as the `output`. Classes that want to get the EZAudioFileDelegate callbacks should implement the EZAudioPlayer's EZAudioPlayerDelegate on the EZAudioPlayer instance. Since 0.5.0 the EZAudioPlayer offers notifications over the usual delegate methods to allow multiple receivers to get the EZAudioPlayer's state changes since one player will typically be used in one application. The EZAudioPlayerDelegate, the `delegate`, provides callbacks for high frequency methods that simply wrap the EZAudioFileDelegate and EZOutputDelegate callbacks for providing the audio buffer played as well as the position updating (you will typically have one scrub bar in an application).
*/
@interface EZAudioPlayer : NSObject <EZAudioFileDelegate,
EZOutputDataSource,
EZOutputDelegate>
//------------------------------------------------------------------------------
#pragma mark - Properties
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Properties
///-----------------------------------------------------------
/**
The EZAudioPlayerDelegate that will handle the audio player callbacks
*/
@property (nonatomic, weak) id<EZAudioPlayerDelegate> delegate;
//------------------------------------------------------------------------------
/**
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
*/
- (instancetype)initWithAudioFile:(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 delegate The receiver that will act as the EZAudioPlayerDelegate. Set to nil if it should have no delegate or use the initWithAudioFile: function instead.
@return The newly created instance of the EZAudioPlayer
*/
- (instancetype)initWithAudioFile:(EZAudioFile *)audioFile
delegate:(id<EZAudioPlayerDelegate>)delegate;
//------------------------------------------------------------------------------
/**
Initializes the EZAudioPlayer with an EZAudioPlayerDelegate.
@param delegate The receiver that will act as the EZAudioPlayerDelegate. Set to nil if it should have no delegate or use the initWithAudioFile: function instead.
@return The newly created instance of the EZAudioPlayer
*/
- (instancetype)initWithDelegate:(id<EZAudioPlayerDelegate>)delegate;
//------------------------------------------------------------------------------
/**
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
*/
- (instancetype)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 delegate The receiver that will act as the EZAudioPlayerDelegate. Set to nil if it should have no delegate or use the initWithAudioFile: function instead.
@return The newly created instance of the EZAudioPlayer
*/
- (instancetype)initWithURL:(NSURL*)url
delegate:(id<EZAudioPlayerDelegate>)delegate;
//------------------------------------------------------------------------------
#pragma mark - Class Initializers
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Class Initializers
///-----------------------------------------------------------
/**
Class initializer that creates a default EZAudioPlayer.
@return The newly created instance of the EZAudioPlayer
*/
+ (instancetype)audioPlayer;
//------------------------------------------------------------------------------
/**
Class initializer that creates 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
*/
+ (instancetype)audioPlayerWithAudioFile:(EZAudioFile *)audioFile;
//------------------------------------------------------------------------------
/**
Class initializer that creates 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 delegate The receiver that will act as the EZAudioPlayerDelegate. Set to nil if it should have no delegate or use the audioPlayerWithAudioFile: function instead.
@return The newly created instance of the EZAudioPlayer
*/
+ (instancetype)audioPlayerWithAudioFile:(EZAudioFile *)audioFile
delegate:(id<EZAudioPlayerDelegate>)delegate;
//------------------------------------------------------------------------------
/**
Class initializer that creates a default EZAudioPlayer with an EZAudioPlayerDelegate..
@return The newly created instance of the EZAudioPlayer
*/
+ (instancetype)audioPlayerWithDelegate:(id<EZAudioPlayerDelegate>)delegate;
//------------------------------------------------------------------------------
/**
Class initializer that creates 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
*/
+ (instancetype)audioPlayerWithURL:(NSURL*)url;
//------------------------------------------------------------------------------
/**
Class initializer that creates 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 delegate 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
*/
+ (instancetype)audioPlayerWithURL:(NSURL*)url
delegate:(id<EZAudioPlayerDelegate>)delegate;
//------------------------------------------------------------------------------
#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.
*/
+ (instancetype)sharedAudioPlayer;
//------------------------------------------------------------------------------
#pragma mark - Properties
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Properties
///-----------------------------------------------------------
/**
Provides the EZAudioFile instance that is being used as the datasource for playback. When set it creates a copy of the EZAudioFile provided for internal use. This does not use the EZAudioFile by reference, but instead creates a copy of the EZAudioFile instance provided.
*/
@property (nonatomic, readwrite, copy) EZAudioFile *audioFile;
//------------------------------------------------------------------------------
/**
Provides the current offset in the audio file as an NSTimeInterval (i.e. in seconds). When setting this it will determine the correct frame offset and perform a `seekToFrame` to the new time offset.
@warning Make sure the new current time offset is less than the `duration` or you will receive an invalid seek assertion.
*/
@property (nonatomic, readwrite) NSTimeInterval currentTime;
//------------------------------------------------------------------------------
/**
The EZAudioDevice instance that is being used by the `output`. Similarly, setting this just sets the `device` property of the `output`.
*/
@property (readwrite) EZAudioDevice *device;
//------------------------------------------------------------------------------
/**
Provides the duration of the audio file in seconds.
*/
@property (readonly) NSTimeInterval duration;
//------------------------------------------------------------------------------
/**
Provides the current time as an NSString with the time format MM:SS.
*/
@property (readonly) NSString *formattedCurrentTime;
//------------------------------------------------------------------------------
/**
Provides the duration as an NSString with the time format MM:SS.
*/
@property (readonly) NSString *formattedDuration;
//------------------------------------------------------------------------------
/**
Provides the EZOutput that is being used to handle the actual playback of the audio data. This property is also settable, but note that the EZAudioPlayer will become the output's EZOutputDataSource and EZOutputDelegate. To listen for the EZOutput's delegate methods your view should implement the EZAudioPlayerDelegate and set itself as the EZAudioPlayer's `delegate`.
*/
@property (nonatomic, strong, readwrite) EZOutput *output;
//------------------------------------------------------------------------------
/**
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.
*/
@property (readonly) 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,
*/
@property (readonly) BOOL isPlaying;
//------------------------------------------------------------------------------
/**
Provides the current pan from the audio player's internal `output` component. Setting the pan adjusts the direction of the audio signal from left (0) to right (1). Default is 0.5 (middle).
*/
@property (nonatomic, assign) float pan;
//------------------------------------------------------------------------------
/**
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.
*/
@property (readonly) 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.
*/
@property (nonatomic, copy, readonly) NSURL *url;
//------------------------------------------------------------------------------
/**
Provides the current volume from the audio player's internal `output` component. Setting the volume adjusts the gain of the output between 0 and 1. Default is 1.
*/
@property (nonatomic, assign) float volume;
//------------------------------------------------------------------------------
#pragma mark - Actions
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Controlling Playback
///-----------------------------------------------------------
/**
Starts playback.
*/
- (void)play;
//------------------------------------------------------------------------------
/**
Loads an EZAudioFile and immediately starts playing it.
@param audioFile An EZAudioFile to use for immediate playback.
*/
- (void)playAudioFile:(EZAudioFile *)audioFile;
//------------------------------------------------------------------------------
/**
Pauses playback.
*/
- (void)pause;
//------------------------------------------------------------------------------
/**
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
-441
View File
@@ -1,441 +0,0 @@
//
// 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"
#import "EZAudioUtilities.h"
//------------------------------------------------------------------------------
#pragma mark - Notifications
//------------------------------------------------------------------------------
NSString * const EZAudioPlayerDidChangeAudioFileNotification = @"EZAudioPlayerDidChangeAudioFileNotification";
NSString * const EZAudioPlayerDidChangeOutputDeviceNotification = @"EZAudioPlayerDidChangeOutputDeviceNotification";
NSString * const EZAudioPlayerDidChangePanNotification = @"EZAudioPlayerDidChangePanNotification";
NSString * const EZAudioPlayerDidChangePlayStateNotification = @"EZAudioPlayerDidChangePlayStateNotification";
NSString * const EZAudioPlayerDidChangeVolumeNotification = @"EZAudioPlayerDidChangeVolumeNotification";
NSString * const EZAudioPlayerDidReachEndOfFileNotification = @"EZAudioPlayerDidReachEndOfFileNotification";
NSString * const EZAudioPlayerDidSeekNotification = @"EZAudioPlayerDidSeekNotification";
//------------------------------------------------------------------------------
#pragma mark - EZAudioPlayer (Implementation)
//------------------------------------------------------------------------------
@implementation EZAudioPlayer
//------------------------------------------------------------------------------
#pragma mark - Class Methods
//------------------------------------------------------------------------------
+ (instancetype)audioPlayer
{
return [[self alloc] init];
}
//------------------------------------------------------------------------------
+ (instancetype)audioPlayerWithDelegate:(id<EZAudioPlayerDelegate>)delegate
{
return [[self alloc] initWithDelegate:delegate];
}
//------------------------------------------------------------------------------
+ (instancetype)audioPlayerWithAudioFile:(EZAudioFile *)audioFile
{
return [[self alloc] initWithAudioFile:audioFile];
}
//------------------------------------------------------------------------------
+ (instancetype)audioPlayerWithAudioFile:(EZAudioFile *)audioFile
delegate:(id<EZAudioPlayerDelegate>)delegate
{
return [[self alloc] initWithAudioFile:audioFile
delegate:delegate];
}
//------------------------------------------------------------------------------
+ (instancetype)audioPlayerWithURL:(NSURL *)url
{
return [[self alloc] initWithURL:url];
}
//------------------------------------------------------------------------------
+ (instancetype)audioPlayerWithURL:(NSURL *)url
delegate:(id<EZAudioPlayerDelegate>)delegate
{
return [[self alloc] initWithURL:url delegate:delegate];
}
//------------------------------------------------------------------------------
#pragma mark - Initialization
//------------------------------------------------------------------------------
- (instancetype)init
{
self = [super init];
if (self)
{
[self setup];
}
return self;
}
//------------------------------------------------------------------------------
- (instancetype)initWithDelegate:(id<EZAudioPlayerDelegate>)delegate
{
self = [self init];
if (self)
{
self.delegate = delegate;
}
return self;
}
//------------------------------------------------------------------------------
- (instancetype)initWithAudioFile:(EZAudioFile *)audioFile
{
return [self initWithAudioFile:audioFile delegate:nil];
}
//------------------------------------------------------------------------------
- (instancetype)initWithAudioFile:(EZAudioFile *)audioFile
delegate:(id<EZAudioPlayerDelegate>)delegate
{
self = [self initWithDelegate:delegate];
if (self)
{
self.audioFile = audioFile;
}
return self;
}
//------------------------------------------------------------------------------
- (instancetype)initWithURL:(NSURL *)url
{
return [self initWithURL:url delegate:nil];
}
//------------------------------------------------------------------------------
- (instancetype)initWithURL:(NSURL *)url
delegate:(id<EZAudioPlayerDelegate>)delegate
{
self = [self initWithDelegate:delegate];
if (self)
{
self.audioFile = [EZAudioFile audioFileWithURL:url delegate:self];
}
return self;
}
//------------------------------------------------------------------------------
#pragma mark - Singleton
//------------------------------------------------------------------------------
+ (instancetype)sharedAudioPlayer
{
static EZAudioPlayer *player;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
player = [[self alloc] init];
});
return player;
}
//------------------------------------------------------------------------------
#pragma mark - Setup
//------------------------------------------------------------------------------
- (void)setup
{
self.output = [EZOutput output];
}
//------------------------------------------------------------------------------
#pragma mark - Getters
//------------------------------------------------------------------------------
- (NSTimeInterval)currentTime
{
return [self.audioFile currentTime];
}
//------------------------------------------------------------------------------
- (EZAudioDevice *)device
{
return [self.output device];
}
//------------------------------------------------------------------------------
- (NSTimeInterval)duration
{
return [self.audioFile duration];
}
//------------------------------------------------------------------------------
- (NSString *)formattedCurrentTime
{
return [self.audioFile formattedCurrentTime];
}
//------------------------------------------------------------------------------
- (NSString *)formattedDuration
{
return [self.audioFile formattedDuration];
}
//------------------------------------------------------------------------------
- (SInt64)frameIndex
{
return [self.audioFile frameIndex];
}
//------------------------------------------------------------------------------
- (BOOL)isPlaying
{
return [self.output isPlaying];
}
//------------------------------------------------------------------------------
- (float)pan
{
return [self.output pan];
}
//------------------------------------------------------------------------------
- (SInt64)totalFrames
{
return [self.audioFile totalFrames];
}
//------------------------------------------------------------------------------
- (float)volume
{
return [self.output volume];
}
//------------------------------------------------------------------------------
#pragma mark - Setters
//------------------------------------------------------------------------------
- (void)setAudioFile:(EZAudioFile *)audioFile
{
_audioFile = [audioFile copy];
_audioFile.delegate = self;
AudioStreamBasicDescription inputFormat = _audioFile.clientFormat;
[self.output setInputFormat:inputFormat];
[[NSNotificationCenter defaultCenter] postNotificationName:EZAudioPlayerDidChangeAudioFileNotification
object:self];
}
//------------------------------------------------------------------------------
- (void)setCurrentTime:(NSTimeInterval)currentTime
{
[self.audioFile setCurrentTime:currentTime];
[[NSNotificationCenter defaultCenter] postNotificationName:EZAudioPlayerDidSeekNotification
object:self];
}
//------------------------------------------------------------------------------
- (void)setDevice:(EZAudioDevice *)device
{
[self.output setDevice:device];
}
//------------------------------------------------------------------------------
- (void)setOutput:(EZOutput *)output
{
_output = output;
_output.dataSource = self;
_output.delegate = self;
}
//------------------------------------------------------------------------------
- (void)setPan:(float)pan
{
[self.output setPan:pan];
[[NSNotificationCenter defaultCenter] postNotificationName:EZAudioPlayerDidChangePanNotification
object:self];
}
//------------------------------------------------------------------------------
- (void)setVolume:(float)volume
{
[self.output setVolume:volume];
[[NSNotificationCenter defaultCenter] postNotificationName:EZAudioPlayerDidChangeVolumeNotification
object:self];
}
//------------------------------------------------------------------------------
#pragma mark - Actions
//------------------------------------------------------------------------------
- (void)play
{
[self.output startPlayback];
}
//------------------------------------------------------------------------------
- (void)playAudioFile:(EZAudioFile *)audioFile
{
//
// stop playing anything that might currently be playing
//
[self pause];
//
// set new stream
//
self.audioFile = audioFile;
//
// begin playback
//
[self play];
}
//------------------------------------------------------------------------------
- (void)pause
{
[self.output stopPlayback];
}
//------------------------------------------------------------------------------
- (void)seekToFrame:(SInt64)frame
{
[self.audioFile seekToFrame:frame];
[[NSNotificationCenter defaultCenter] postNotificationName:EZAudioPlayerDidSeekNotification
object:self];
}
//------------------------------------------------------------------------------
#pragma mark - EZOutputDataSource
//------------------------------------------------------------------------------
- (OSStatus) output:(EZOutput *)output
shouldFillAudioBufferList:(AudioBufferList *)audioBufferList
withNumberOfFrames:(UInt32)frames
timestamp:(const AudioTimeStamp *)timestamp
{
if (self.audioFile)
{
UInt32 bufferSize;
BOOL eof;
[self.audioFile readFrames:frames
audioBufferList:audioBufferList
bufferSize:&bufferSize
eof:&eof];
if (eof && self.shouldLoop)
{
[self seekToFrame:0];
}
else if (eof)
{
[self pause];
[self seekToFrame:0];
[[NSNotificationCenter defaultCenter] postNotificationName:EZAudioPlayerDidReachEndOfFileNotification
object:self];
}
}
return noErr;
}
//------------------------------------------------------------------------------
#pragma mark - EZAudioFileDelegate
//------------------------------------------------------------------------------
- (void)audioFile:(EZAudioFile *)audioFile updatedPosition:(SInt64)framePosition
{
if ([self.delegate respondsToSelector:@selector(audioPlayer:updatedPosition:inAudioFile:)])
{
[self.delegate audioPlayer:self
updatedPosition:framePosition
inAudioFile:audioFile];
}
}
//------------------------------------------------------------------------------
#pragma mark - EZOutputDelegate
//------------------------------------------------------------------------------
- (void)output:(EZOutput *)output changedDevice:(EZAudioDevice *)device
{
[[NSNotificationCenter defaultCenter] postNotificationName:EZAudioPlayerDidChangeOutputDeviceNotification
object:self];
}
//------------------------------------------------------------------------------
- (void)output:(EZOutput *)output changedPlayingState:(BOOL)isPlaying
{
[[NSNotificationCenter defaultCenter] postNotificationName:EZAudioPlayerDidChangePlayStateNotification
object:self];
}
//------------------------------------------------------------------------------
- (void) output:(EZOutput *)output
playedAudio:(float **)buffer
withBufferSize:(UInt32)bufferSize
withNumberOfChannels:(UInt32)numberOfChannels
{
if ([self.delegate respondsToSelector:@selector(audioPlayer:playedAudio:withBufferSize:withNumberOfChannels:inAudioFile:)])
{
[self.delegate audioPlayer:self
playedAudio:buffer
withBufferSize:bufferSize
withNumberOfChannels:numberOfChannels
inAudioFile:self.audioFile];
}
}
//------------------------------------------------------------------------------
@end
-627
View File
@@ -1,627 +0,0 @@
//
// EZMicrophone.m
// EZAudio
//
// Created by Syed Haris Ali on 9/2/13.
// Copyright (c) 2015 Syed Haris Ali. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "EZMicrophone.h"
#import "EZAudioFloatConverter.h"
#import "EZAudioUtilities.h"
#import "EZAudioDevice.h"
//------------------------------------------------------------------------------
#pragma mark - Data Structures
//------------------------------------------------------------------------------
typedef struct EZMicrophoneInfo
{
AudioUnit audioUnit;
AudioBufferList *audioBufferList;
float **floatData;
AudioStreamBasicDescription inputFormat;
AudioStreamBasicDescription streamFormat;
} EZMicrophoneInfo;
//------------------------------------------------------------------------------
#pragma mark - Callbacks
//------------------------------------------------------------------------------
static OSStatus EZAudioMicrophoneCallback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData);
//------------------------------------------------------------------------------
#pragma mark - EZMicrophone (Interface Extension)
//------------------------------------------------------------------------------
@interface EZMicrophone ()
@property (nonatomic, strong) EZAudioFloatConverter *floatConverter;
@property (nonatomic, assign) EZMicrophoneInfo *info;
@end
@implementation EZMicrophone
//------------------------------------------------------------------------------
#pragma mark - Dealloc
//------------------------------------------------------------------------------
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
[EZAudioUtilities checkResult:AudioUnitUninitialize(self.info->audioUnit) operation:"Failed to unintialize audio unit for microphone"];
[EZAudioUtilities freeBufferList:self.info->audioBufferList];
[EZAudioUtilities freeFloatBuffers:self.info->floatData
numberOfChannels:self.info->streamFormat.mChannelsPerFrame];
free(self.info);
}
//------------------------------------------------------------------------------
#pragma mark - Initialization
//------------------------------------------------------------------------------
- (id)init
{
self = [super init];
if(self)
{
self.info = (EZMicrophoneInfo *)malloc(sizeof(EZMicrophoneInfo));
memset(self.info, 0, sizeof(EZMicrophoneInfo));
[self setup];
}
return self;
}
//------------------------------------------------------------------------------
- (EZMicrophone *)initWithMicrophoneDelegate:(id<EZMicrophoneDelegate>)delegate
{
self = [super init];
if(self)
{
self.info = (EZMicrophoneInfo *)malloc(sizeof(EZMicrophoneInfo));
memset(self.info, 0, sizeof(EZMicrophoneInfo));
_delegate = delegate;
[self setup];
}
return self;
}
//------------------------------------------------------------------------------
-(EZMicrophone *)initWithMicrophoneDelegate:(id<EZMicrophoneDelegate>)delegate
withAudioStreamBasicDescription:(AudioStreamBasicDescription)audioStreamBasicDescription
{
self = [self initWithMicrophoneDelegate:delegate];
if(self)
{
self.info = (EZMicrophoneInfo *)malloc(sizeof(EZMicrophoneInfo));
memset(self.info, 0, sizeof(EZMicrophoneInfo));
self.info->streamFormat = audioStreamBasicDescription;
_delegate = delegate;
[self setup];
}
return self;
}
//------------------------------------------------------------------------------
- (EZMicrophone *)initWithMicrophoneDelegate:(id<EZMicrophoneDelegate>)delegate
startsImmediately:(BOOL)startsImmediately
{
self = [self initWithMicrophoneDelegate:delegate];
if(self)
{
startsImmediately ? [self startFetchingAudio] : -1;
}
return self;
}
//------------------------------------------------------------------------------
-(EZMicrophone *)initWithMicrophoneDelegate:(id<EZMicrophoneDelegate>)delegate
withAudioStreamBasicDescription:(AudioStreamBasicDescription)audioStreamBasicDescription
startsImmediately:(BOOL)startsImmediately
{
self = [self initWithMicrophoneDelegate:delegate
withAudioStreamBasicDescription:audioStreamBasicDescription];
if(self)
{
startsImmediately ? [self startFetchingAudio] : -1;
}
return self;
}
//------------------------------------------------------------------------------
#pragma mark - Class Initializers
//------------------------------------------------------------------------------
+ (EZMicrophone *)microphoneWithDelegate:(id<EZMicrophoneDelegate>)delegate
{
return [[EZMicrophone alloc] initWithMicrophoneDelegate:delegate];
}
//------------------------------------------------------------------------------
+ (EZMicrophone *)microphoneWithDelegate:(id<EZMicrophoneDelegate>)delegate
withAudioStreamBasicDescription:(AudioStreamBasicDescription)audioStreamBasicDescription
{
return [[EZMicrophone alloc] initWithMicrophoneDelegate:delegate
withAudioStreamBasicDescription:audioStreamBasicDescription];
}
//------------------------------------------------------------------------------
+ (EZMicrophone *)microphoneWithDelegate:(id<EZMicrophoneDelegate>)delegate
startsImmediately:(BOOL)startsImmediately
{
return [[EZMicrophone alloc] initWithMicrophoneDelegate:delegate
startsImmediately:startsImmediately];
}
//------------------------------------------------------------------------------
+ (EZMicrophone *)microphoneWithDelegate:(id<EZMicrophoneDelegate>)delegate
withAudioStreamBasicDescription:(AudioStreamBasicDescription)audioStreamBasicDescription
startsImmediately:(BOOL)startsImmediately
{
return [[EZMicrophone alloc] initWithMicrophoneDelegate:delegate
withAudioStreamBasicDescription:audioStreamBasicDescription
startsImmediately:startsImmediately];
}
//------------------------------------------------------------------------------
#pragma mark - Singleton
//------------------------------------------------------------------------------
+ (EZMicrophone *)sharedMicrophone
{
static EZMicrophone *_sharedMicrophone = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedMicrophone = [[EZMicrophone alloc] init];
});
return _sharedMicrophone;
}
//------------------------------------------------------------------------------
#pragma mark - Setup
//------------------------------------------------------------------------------
- (void)setup
{
// Create an input component description for mic input
AudioComponentDescription inputComponentDescription;
inputComponentDescription.componentType = kAudioUnitType_Output;
inputComponentDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
#if TARGET_OS_IPHONE
inputComponentDescription.componentSubType = kAudioUnitSubType_RemoteIO;
#elif TARGET_OS_MAC
inputComponentDescription.componentSubType = kAudioUnitSubType_HALOutput;
#endif
// get the first matching component
AudioComponent inputComponent = AudioComponentFindNext( NULL , &inputComponentDescription);
NSAssert(inputComponent, @"Couldn't get input component unit!");
// create new instance of component
[EZAudioUtilities checkResult:AudioComponentInstanceNew(inputComponent, &self.info->audioUnit)
operation:"Failed to get audio component instance"];
#if TARGET_OS_IPHONE
// must enable input scope for remote IO unit
UInt32 flag = 1;
[EZAudioUtilities checkResult:AudioUnitSetProperty(self.info->audioUnit,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Input,
1,
&flag,
sizeof(flag))
operation:"Couldn't enable input on remote IO unit."];
EZAudioDevice *currentInputDevice = [EZAudioDevice currentInputDevice];
[self setDevice:currentInputDevice];
#elif TARGET_OS_MAC
NSArray *inputDevices = [EZAudioDevice inputDevices];
EZAudioDevice *defaultMicrophone = [inputDevices lastObject];
[self setDevice:defaultMicrophone];
#endif
UInt32 propSize = sizeof(self.info->inputFormat);
[EZAudioUtilities checkResult:AudioUnitGetProperty(self.info->audioUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
1,
&self.info->inputFormat,
&propSize)
operation:"Failed to get stream format of microphone input scope"];
#if TARGET_OS_IPHONE
self.info->inputFormat.mSampleRate = [[AVAudioSession sharedInstance] sampleRate];
NSAssert(self.info->inputFormat.mSampleRate, @"Expected AVAudioSession sample rate to be greater than 0.0. Did you setup the audio session?");
#elif TARGET_OS_MAC
#endif
if (self.info->streamFormat.mSampleRate == 0.0)
{
self.info->streamFormat = [self defaultStreamFormat];
}
[self setAudioStreamBasicDescription:self.info->streamFormat];
// render callback
AURenderCallbackStruct renderCallbackStruct;
renderCallbackStruct.inputProc = EZAudioMicrophoneCallback;
renderCallbackStruct.inputProcRefCon = (__bridge void *)(self);
[EZAudioUtilities checkResult:AudioUnitSetProperty(self.info->audioUnit,
kAudioOutputUnitProperty_SetInputCallback,
kAudioUnitScope_Global,
1,
&renderCallbackStruct,
sizeof(renderCallbackStruct))
operation:"Failed to set render callback"];
[EZAudioUtilities checkResult:AudioUnitInitialize(self.info->audioUnit)
operation:"Failed to initialize input unit"];
// setup notifications
[self setupNotifications];
}
- (void)setupNotifications
{
#if TARGET_OS_IPHONE
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(microphoneWasInterrupted:)
name:AVAudioSessionInterruptionNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(microphoneRouteChanged:)
name:AVAudioSessionRouteChangeNotification
object:nil];
#elif TARGET_OS_MAC
#endif
}
//------------------------------------------------------------------------------
#pragma mark - Notifications
//------------------------------------------------------------------------------
#if TARGET_OS_IPHONE
- (void)microphoneWasInterrupted:(NSNotification *)notification
{
AVAudioSessionInterruptionType type = [notification.userInfo[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
switch (type)
{
case AVAudioSessionInterruptionTypeBegan:
{
[self stopFetchingAudio];
break;
}
case AVAudioSessionInterruptionTypeEnded:
{
AVAudioSessionInterruptionOptions option = [notification.userInfo[AVAudioSessionInterruptionOptionKey] unsignedIntegerValue];
if (option == AVAudioSessionInterruptionOptionShouldResume)
{
[self startFetchingAudio];
}
break;
}
default:
{
break;
}
}
}
//------------------------------------------------------------------------------
- (void)microphoneRouteChanged:(NSNotification *)notification
{
EZAudioDevice *device = [EZAudioDevice currentInputDevice];
[self setDevice:device];
}
#elif TARGET_OS_MAC
#endif
//------------------------------------------------------------------------------
#pragma mark - Events
//------------------------------------------------------------------------------
-(void)startFetchingAudio
{
[EZAudioUtilities checkResult:AudioOutputUnitStart(self.info->audioUnit)
operation:"Failed to start microphone audio unit"];
}
//------------------------------------------------------------------------------
-(void)stopFetchingAudio
{
[EZAudioUtilities checkResult:AudioOutputUnitStop(self.info->audioUnit)
operation:"Failed to stop microphone audio unit"];
}
//------------------------------------------------------------------------------
#pragma mark - Getters
//------------------------------------------------------------------------------
-(AudioStreamBasicDescription)audioStreamBasicDescription
{
return self.info->streamFormat;
}
//------------------------------------------------------------------------------
-(AudioUnit *)audioUnit
{
return &self.info->audioUnit;
}
//------------------------------------------------------------------------------
- (UInt32)maximumBufferSize
{
UInt32 maximumBufferSize;
UInt32 propSize = sizeof(maximumBufferSize);
[EZAudioUtilities checkResult:AudioUnitGetProperty(self.info->audioUnit,
kAudioUnitProperty_MaximumFramesPerSlice,
kAudioUnitScope_Global,
0,
&maximumBufferSize,
&propSize)
operation:"Failed to get maximum number of frames per slice"];
return maximumBufferSize;
}
//------------------------------------------------------------------------------
#pragma mark - Setters
//------------------------------------------------------------------------------
- (void)setMicrophoneOn:(BOOL)microphoneOn
{
_microphoneOn = microphoneOn;
if (microphoneOn)
{
[self startFetchingAudio];
}
else {
[self stopFetchingAudio];
}
}
//------------------------------------------------------------------------------
- (void)setAudioStreamBasicDescription:(AudioStreamBasicDescription)asbd
{
if (self.floatConverter)
{
[EZAudioUtilities freeBufferList:self.info->audioBufferList];
[EZAudioUtilities freeFloatBuffers:self.info->floatData
numberOfChannels:self.info->streamFormat.mChannelsPerFrame];
}
// set new stream format
self.info->streamFormat = asbd;
[EZAudioUtilities checkResult:AudioUnitSetProperty(self.info->audioUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
0,
&asbd,
sizeof(asbd))
operation:"Failed to set stream format on input scope"];
[EZAudioUtilities checkResult:AudioUnitSetProperty(self.info->audioUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
1,
&asbd,
sizeof(asbd))
operation:"Failed to set stream format on output scope"];
// allocate float buffers
UInt32 maximumBufferSize = [self maximumBufferSize];
BOOL isInterleaved = [EZAudioUtilities isInterleaved:asbd];
UInt32 channels = asbd.mChannelsPerFrame;
self.floatConverter = [[EZAudioFloatConverter alloc] initWithInputFormat:asbd];
self.info->floatData = [EZAudioUtilities floatBuffersWithNumberOfFrames:maximumBufferSize
numberOfChannels:channels];
self.info->audioBufferList = [EZAudioUtilities audioBufferListWithNumberOfFrames:maximumBufferSize
numberOfChannels:channels
interleaved:isInterleaved];
// notify delegate
if ([self.delegate respondsToSelector:@selector(microphone:hasAudioStreamBasicDescription:)])
{
[self.delegate microphone:self hasAudioStreamBasicDescription:asbd];
}
}
//------------------------------------------------------------------------------
- (void)setDevice:(EZAudioDevice *)device
{
#if TARGET_OS_IPHONE
// if the devices are equal then ignore
if ([device isEqual:self.device])
{
return;
}
NSError *error;
[[AVAudioSession sharedInstance] setPreferredInput:device.port error:&error];
if (error)
{
NSLog(@"Error setting input device port (%@), reason: %@",
device.port,
error.localizedDescription);
}
else
{
if (device.dataSource)
{
[[AVAudioSession sharedInstance] setInputDataSource:device.dataSource error:&error];
if (error)
{
NSLog(@"Error setting input data source (%@), reason: %@",
device.dataSource,
error.localizedDescription);
}
}
}
#elif TARGET_OS_MAC
UInt32 inputEnabled = device.inputChannelCount > 0;
[EZAudioUtilities checkResult:AudioUnitSetProperty(self.info->audioUnit,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Input,
1,
&inputEnabled,
sizeof(inputEnabled))
operation:"Failed to set flag on device input"];
UInt32 outputEnabled = device.outputChannelCount > 0;
[EZAudioUtilities checkResult:AudioUnitSetProperty(self.info->audioUnit,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Output,
0,
&outputEnabled,
sizeof(outputEnabled))
operation:"Failed to set flag on device output"];
AudioDeviceID deviceId = device.deviceID;
[EZAudioUtilities checkResult:AudioUnitSetProperty(self.info->audioUnit,
kAudioOutputUnitProperty_CurrentDevice,
kAudioUnitScope_Global,
0,
&deviceId,
sizeof(AudioDeviceID))
operation:"Couldn't set default device on I/O unit"];
#endif
// store device
_device = device;
// notify delegate
if ([self.delegate respondsToSelector:@selector(microphone:changedDevice:)])
{
[self.delegate microphone:self changedDevice:device];
}
}
//------------------------------------------------------------------------------
#pragma mark - Output
//------------------------------------------------------------------------------
- (void)setOutput:(EZOutput *)output
{
_output = output;
_output.inputFormat = self.audioStreamBasicDescription;
_output.dataSource = self;
}
//------------------------------------------------------------------------------
#pragma mark - EZOutputDataSource
//------------------------------------------------------------------------------
- (OSStatus) output:(EZOutput *)output
shouldFillAudioBufferList:(AudioBufferList *)audioBufferList
withNumberOfFrames:(UInt32)frames
timestamp:(const AudioTimeStamp *)timestamp
{
memcpy(audioBufferList,
self.info->audioBufferList,
sizeof(AudioBufferList) + (self.info->audioBufferList->mNumberBuffers - 1)*sizeof(AudioBuffer));
return noErr;
}
//------------------------------------------------------------------------------
#pragma mark - Subclass
//------------------------------------------------------------------------------
- (AudioStreamBasicDescription)defaultStreamFormat
{
return [EZAudioUtilities floatFormatWithNumberOfChannels:[self numberOfChannels]
sampleRate:self.info->inputFormat.mSampleRate];
}
//------------------------------------------------------------------------------
- (UInt32)numberOfChannels
{
#if TARGET_OS_IPHONE
return 1;
#elif TARGET_OS_MAC
return (UInt32)self.device.inputChannelCount;
#endif
}
//------------------------------------------------------------------------------
@end
//------------------------------------------------------------------------------
#pragma mark - Callbacks
//------------------------------------------------------------------------------
static OSStatus EZAudioMicrophoneCallback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData)
{
EZMicrophone *microphone = (__bridge EZMicrophone *)inRefCon;
EZMicrophoneInfo *info = (EZMicrophoneInfo *)microphone.info;
// render audio into buffer
OSStatus result = AudioUnitRender(info->audioUnit,
ioActionFlags,
inTimeStamp,
inBusNumber,
inNumberFrames,
info->audioBufferList);
// notify delegate of new buffer list to process
if ([microphone.delegate respondsToSelector:@selector(microphone:hasBufferList:withBufferSize:withNumberOfChannels:)])
{
[microphone.delegate microphone:microphone
hasBufferList:info->audioBufferList
withBufferSize:inNumberFrames
withNumberOfChannels:info->streamFormat.mChannelsPerFrame];
}
// notify delegate of new float data processed
if ([microphone.delegate respondsToSelector:@selector(microphone:hasAudioReceived:withBufferSize:withNumberOfChannels:)])
{
// convert to float
[microphone.floatConverter convertDataFromAudioBufferList:info->audioBufferList
withNumberOfFrames:inNumberFrames
toFloatBuffers:info->floatData];
[microphone.delegate microphone:microphone
hasAudioReceived:info->floatData
withBufferSize:inNumberFrames
withNumberOfChannels:info->streamFormat.mChannelsPerFrame];
}
return result;
}
-376
View File
@@ -1,376 +0,0 @@
//
// EZOutput.h
// EZAudio
//
// Created by Syed Haris Ali on 12/2/13.
// Copyright (c) 2015 Syed Haris Ali. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
#if TARGET_OS_IPHONE
#elif TARGET_OS_MAC
#import <AudioUnit/AudioUnit.h>
#endif
@class EZAudioDevice;
@class EZOutput;
//------------------------------------------------------------------------------
#pragma mark - Constants
//------------------------------------------------------------------------------
FOUNDATION_EXPORT UInt32 const EZOutputMaximumFramesPerSlice;
FOUNDATION_EXPORT Float64 const EZOutputDefaultSampleRate;
//------------------------------------------------------------------------------
#pragma mark - EZOutputDataSource
//------------------------------------------------------------------------------
/**
The EZOutputDataSource specifies a receiver to provide audio data when the EZOutput is started. Since the 0.4.0 release this has been simplified to only one data source method.
*/
@protocol EZOutputDataSource <NSObject>
@optional
///-----------------------------------------------------------
/// @name Providing Audio Data
///-----------------------------------------------------------
@required
/**
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. The expected format of the audio data provided here is specified by the EZOutput `inputFormat` property. This audio data will be converted into the client format specified by the EZOutput `clientFormat` property.
@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 timestamp A AudioTimeStamp pointer to use if you need the current host time.
@return An OSStatus code. If there was no error then use the noErr status code.
*/
- (OSStatus) output:(EZOutput *)output
shouldFillAudioBufferList:(AudioBufferList *)audioBufferList
withNumberOfFrames:(UInt32)frames
timestamp:(const AudioTimeStamp *)timestamp;
@end
//------------------------------------------------------------------------------
#pragma mark - EZOutputDelegate
//------------------------------------------------------------------------------
/**
The EZOutputDelegate for the EZOutput component provides a receiver to handle play state, device, and audio data change events. This is very similar to the EZMicrophoneDelegate for the EZMicrophone and the EZAudioFileDelegate for the EZAudioFile.
*/
@protocol EZOutputDelegate <NSObject>
@optional
/**
Called anytime the EZOutput starts or stops.
@param output The instance of the EZOutput that triggered the event.
@param isPlaying A BOOL indicating whether the EZOutput instance is playing or not.
*/
- (void)output:(EZOutput *)output changedPlayingState:(BOOL)isPlaying;
//------------------------------------------------------------------------------
/**
Called anytime the `device` changes on an EZOutput instance.
@param output The instance of the EZOutput that triggered the event.
@param device The instance of the new EZAudioDevice the output is using to play audio data.
*/
- (void)output:(EZOutput *)output changedDevice:(EZAudioDevice *)device;
//------------------------------------------------------------------------------
/**
Like the EZMicrophoneDelegate, for the EZOutput this method provides an array of float arrays of the audio received, each float array representing a channel of audio data. This occurs on the background thread so any drawing code must explicity perform its functions on the main thread.
@param output The instance of the EZOutput that triggered the event.
@param buffer The audio data as an array of float arrays. In a stereo signal buffer[0] represents the left channel while buffer[1] would represent the right channel.
@param bufferSize A UInt32 representing the size of each of the buffers (the length of each float array).
@param numberOfChannels A UInt32 representing the number of channels (you can use this to know how many float arrays are in the `buffer` parameter.
@warning This function executes on a background thread to avoid blocking any audio operations. If operations should be performed on any other thread (like the main thread) it should be performed within a dispatch block like so: dispatch_async(dispatch_get_main_queue(), ^{ ...Your Code... })
*/
- (void) output:(EZOutput *)output
playedAudio:(float **)buffer
withBufferSize:(UInt32)bufferSize
withNumberOfChannels:(UInt32)numberOfChannels;
//------------------------------------------------------------------------------
@end
/**
The EZOutput component provides a generic output to glue all the other EZAudio components together and push whatever sound you've created to the default output device (think opposite of the microphone). The EZOutputDataSource provides the required AudioBufferList needed to populate the output buffer while the EZOutputDelegate provides the same kind of mechanism as the EZMicrophoneDelegate or EZAudioFileDelegate in that you will receive a callback that provides non-interleaved, float data for visualizing the output (done using an internal float converter). As of 0.4.0 the EZOutput has been simplified to a single EZOutputDataSource method and now uses an AUGraph to provide format conversion from the `inputFormat` to the playback graph's `clientFormat` linear PCM formats, mixer controls for setting volume and pan settings, hooks to add in any number of effect audio units (see the `connectOutputOfSourceNode:sourceNodeOutputBus:toDestinationNode:destinationNodeInputBus:inGraph:` subclass method), and hardware device toggling (via EZAudioDevice).
*/
@interface EZOutput : NSObject
//------------------------------------------------------------------------------
#pragma mark - Initializers
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Initializers
///-----------------------------------------------------------
/**
Creates a new instance of the EZOutput and allows the caller to specify an EZOutputDataSource.
@param dataSource The EZOutputDataSource that will be used to pull the audio data for the output callback.
@return A newly created instance of the EZOutput class.
*/
- (instancetype)initWithDataSource:(id<EZOutputDataSource>)dataSource;
/**
Creates a new instance of the EZOutput and allows the caller to specify an EZOutputDataSource.
@param dataSource The EZOutputDataSource that will be used to pull the audio data for the output callback.
@param inputFormat The AudioStreamBasicDescription of the EZOutput.
@warning AudioStreamBasicDescription input formats must be linear PCM!
@return A newly created instance of the EZOutput class.
*/
- (instancetype)initWithDataSource:(id<EZOutputDataSource>)dataSource
inputFormat:(AudioStreamBasicDescription)inputFormat;
//------------------------------------------------------------------------------
#pragma mark - Class Initializers
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Class Initializers
///-----------------------------------------------------------
/**
Class method to create a new instance of the EZOutput
@return A newly created instance of the EZOutput class.
*/
+ (instancetype)output;
/**
Class method to create a new instance of the EZOutput and allows the caller to specify an EZOutputDataSource.
@param dataSource The EZOutputDataSource that will be used to pull the audio data for the output callback.
@return A newly created instance of the EZOutput class.
*/
+ (instancetype)outputWithDataSource:(id<EZOutputDataSource>)dataSource;
/**
Class method to create a new instance of the EZOutput and allows the caller to specify an EZOutputDataSource.
@param dataSource The EZOutputDataSource that will be used to pull the audio data for the output callback.
@param audioStreamBasicDescription The AudioStreamBasicDescription of the EZOutput.
@warning AudioStreamBasicDescriptions that are invalid will cause the EZOutput to fail to initialize
@return A newly created instance of the EZOutput class.
*/
+ (instancetype)outputWithDataSource:(id<EZOutputDataSource>)dataSource
inputFormat:(AudioStreamBasicDescription)inputFormat;
//------------------------------------------------------------------------------
#pragma mark - Singleton
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Shared Instance
///-----------------------------------------------------------
/**
Creates a shared instance of the EZOutput (one app will usually only need one output and share the role of the EZOutputDataSource).
@return The shared instance of the EZOutput class.
*/
+ (instancetype)sharedOutput;
//------------------------------------------------------------------------------
#pragma mark - Properties
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Setting/Getting The Stream Formats
///-----------------------------------------------------------
/**
Provides the AudioStreamBasicDescription structure used at the beginning of the playback graph which is then converted into the `clientFormat` using the AUConverter audio unit.
@warning The AudioStreamBasicDescription set here must be linear PCM. Compressed formats are not supported...the EZAudioFile's clientFormat performs the audio conversion on the fly from compressed to linear PCM so there is no additional work to be done there.
@return An AudioStreamBasicDescription structure describing
*/
@property (nonatomic, readwrite) AudioStreamBasicDescription inputFormat;
//------------------------------------------------------------------------------
/**
Provides the AudioStreamBasicDescription structure that serves as the common format used throughout the playback graph (similar to how the EZAudioFile as a clientFormat that is linear PCM to be shared amongst other components). The `inputFormat` is converted into this format at the beginning of the playback graph using an AUConverter audio unit. Defaults to the whatever the `defaultClientFormat` method returns is if a custom one isn't explicitly set.
@warning The AudioStreamBasicDescription set here must be linear PCM. Compressed formats are not supported by Audio Units.
@return An AudioStreamBasicDescription structure describing the common client format for the playback graph.
*/
@property (nonatomic, readwrite) AudioStreamBasicDescription clientFormat;
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Setting/Getting The Data Source and Delegate
///-----------------------------------------------------------
/**
The EZOutputDataSource that provides the audio data in the `inputFormat` for the EZOutput to play. If an EZOutputDataSource is not specified then the EZOutput will just output silence.
*/
@property (nonatomic, weak) id<EZOutputDataSource> dataSource;
//------------------------------------------------------------------------------
/**
The EZOutputDelegate for which to handle the output callbacks
*/
@property (nonatomic, weak) id<EZOutputDelegate> delegate;
//------------------------------------------------------------------------------
/**
Provides a flag indicating whether the EZOutput is pulling audio data from the EZOutputDataSource for playback.
@return YES if the EZOutput is running, NO if it is stopped
*/
@property (readonly) BOOL isPlaying;
//------------------------------------------------------------------------------
/**
Provides the current pan from the audio player's mixer audio unit in the playback graph. Setting the pan adjusts the direction of the audio signal from left (0) to right (1). Default is 0.5 (middle).
*/
@property (nonatomic, assign) float pan;
//------------------------------------------------------------------------------
/**
Provides the current volume from the audio player's mixer audio unit in the playback graph. Setting the volume adjusts the gain of the output between 0 and 1. Default is 1.
*/
@property (nonatomic, assign) float volume;
//------------------------------------------------------------------------------
#pragma mark - Core Audio Properties
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Core Audio Properties
///-----------------------------------------------------------
/**
The AUGraph used to chain together the converter, mixer, and output audio units.
*/
@property (readonly) AUGraph graph;
//------------------------------------------------------------------------------
/**
The AudioUnit that is being used to convert the audio data coming into the output's playback graph.
*/
@property (readonly) AudioUnit converterAudioUnit;
//------------------------------------------------------------------------------
/**
The AudioUnit that is being used as the mixer to adjust the volume on the output's playback graph.
*/
@property (readonly) AudioUnit mixerAudioUnit;
//------------------------------------------------------------------------------
/**
The AudioUnit that is being used as the hardware output for the output's playback graph.
*/
@property (readonly) AudioUnit outputAudioUnit;
//------------------------------------------------------------------------------
#pragma mark - Setters
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Getting/Setting The Output's Hardware Device
///-----------------------------------------------------------
/**
An EZAudioDevice instance that is used to route the audio data out to the speaker. To find a list of available output devices see the EZAudioDevice `outputDevices` method.
*/
@property (nonatomic, strong, readwrite) EZAudioDevice *device;
//------------------------------------------------------------------------------
#pragma mark - Actions
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Starting/Stopping The Output
///-----------------------------------------------------------
/**
Starts pulling audio data from the EZOutputDataSource to the default device output.
*/
- (void)startPlayback;
///-----------------------------------------------------------
/**
Stops pulling audio data from the EZOutputDataSource to the default device output.
*/
- (void)stopPlayback;
//------------------------------------------------------------------------------
#pragma mark - Subclass
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Subclass
///-----------------------------------------------------------
/**
This method handles connecting the converter node to the mixer node within the AUGraph that is being used as the playback graph. Subclasses can override this method and insert their custom nodes to perform effects processing on the audio data being rendered.
This was inspired by Daniel Kennett's blog post on how to add a custom equalizer to a CocoaLibSpotify SPCoreAudioController's AUGraph. For more information see Daniel's post and example code here: http://ikennd.ac/blog/2012/04/augraph-basics-in-cocoalibspotify/.
@param sourceNode An AUNode representing the node the audio data is coming from.
@param sourceNodeOutputBus A UInt32 representing the output bus from the source node that should be connected into the next node's input bus.
@param destinationNode An AUNode representing the node the audio data should be connected to.
@param destinationNodeInputBus A UInt32 representing the input bus the source node's output bus should be connecting to.
@param graph The AUGraph that is being used to hold the playback graph. Same as from the `graph` property.
@return An OSStatus code. For no error return back `noErr`.
*/
- (OSStatus)connectOutputOfSourceNode:(AUNode)sourceNode
sourceNodeOutputBus:(UInt32)sourceNodeOutputBus
toDestinationNode:(AUNode)destinationNode
destinationNodeInputBus:(UInt32)destinationNodeInputBus
inGraph:(AUGraph)graph;
//------------------------------------------------------------------------------
/**
The default AudioStreamBasicDescription set as the client format of the output if no custom `clientFormat` is set. Defaults to a 44.1 kHz stereo, non-interleaved, float format.
@return An AudioStreamBasicDescription that will be used as the default stream format.
*/
- (AudioStreamBasicDescription)defaultClientFormat;
//------------------------------------------------------------------------------
/**
The default AudioStreamBasicDescription set as the `inputFormat` of the output if no custom `inputFormat` is set. Defaults to a 44.1 kHz stereo, non-interleaved, float format.
@return An AudioStreamBasicDescription that will be used as the default stream format.
*/
- (AudioStreamBasicDescription)defaultInputFormat;
//------------------------------------------------------------------------------
/**
The default value used as the AudioUnit subtype when creating the hardware output component. By default this is kAudioUnitSubType_RemoteIO for iOS and kAudioUnitSubType_HALOutput for OSX.
@warning If you change this to anything other than kAudioUnitSubType_HALOutput for OSX you will get a failed assertion because devices can only be set when using the HAL audio unit.
@return An OSType that represents the AudioUnit subtype for the hardware output component.
*/
- (OSType)outputAudioUnitSubType;
@end
-756
View File
@@ -1,756 +0,0 @@
//
// EZOutput.m
// EZAudio
//
// Created by Syed Haris Ali on 12/2/13.
// Copyright (c) 2015 Syed Haris Ali. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "EZOutput.h"
#import "EZAudioDevice.h"
#import "EZAudioFloatConverter.h"
#import "EZAudioUtilities.h"
//------------------------------------------------------------------------------
#pragma mark - Constants
//------------------------------------------------------------------------------
UInt32 const EZOutputMaximumFramesPerSlice = 4096;
Float64 const EZOutputDefaultSampleRate = 44100.0f;
//------------------------------------------------------------------------------
#pragma mark - Data Structures
//------------------------------------------------------------------------------
typedef struct
{
// stream format params
AudioStreamBasicDescription inputFormat;
AudioStreamBasicDescription clientFormat;
// float converted data
float **floatData;
// nodes
EZAudioNodeInfo converterNodeInfo;
EZAudioNodeInfo mixerNodeInfo;
EZAudioNodeInfo outputNodeInfo;
// audio graph
AUGraph graph;
} EZOutputInfo;
//------------------------------------------------------------------------------
#pragma mark - Callbacks (Declaration)
//------------------------------------------------------------------------------
OSStatus EZOutputConverterInputCallback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData);
//------------------------------------------------------------------------------
OSStatus EZOutputGraphRenderCallback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData);
//------------------------------------------------------------------------------
#pragma mark - EZOutput (Interface Extension)
//------------------------------------------------------------------------------
@interface EZOutput ()
@property (nonatomic, strong) EZAudioFloatConverter *floatConverter;
@property (nonatomic, assign) EZOutputInfo *info;
@end
//------------------------------------------------------------------------------
#pragma mark - EZOutput (Implementation)
//------------------------------------------------------------------------------
@implementation EZOutput
//------------------------------------------------------------------------------
#pragma mark - Dealloc
//------------------------------------------------------------------------------
- (void)dealloc
{
if (self.floatConverter)
{
self.floatConverter = nil;
[EZAudioUtilities freeFloatBuffers:self.info->floatData
numberOfChannels:self.info->clientFormat.mChannelsPerFrame];
}
[EZAudioUtilities checkResult:AUGraphStop(self.info->graph)
operation:"Failed to stop graph"];
[EZAudioUtilities checkResult:AUGraphClose(self.info->graph)
operation:"Failed to close graph"];
[EZAudioUtilities checkResult:AUGraphUninitialize(self.info->graph)
operation:"Failed to uninitialize graph"];
free(self.info);
}
//------------------------------------------------------------------------------
#pragma mark - Initialization
//------------------------------------------------------------------------------
- (instancetype) init
{
self = [super init];
if (self)
{
[self setup];
}
return self;
}
//------------------------------------------------------------------------------
- (instancetype)initWithDataSource:(id<EZOutputDataSource>)dataSource
{
self = [self init];
if (self)
{
self.dataSource = dataSource;
}
return self;
}
//------------------------------------------------------------------------------
- (instancetype)initWithDataSource:(id<EZOutputDataSource>)dataSource
inputFormat:(AudioStreamBasicDescription)inputFormat
{
self = [self initWithDataSource:dataSource];
if (self)
{
self.inputFormat = inputFormat;
}
return self;
}
//------------------------------------------------------------------------------
#pragma mark - Class Initializers
//------------------------------------------------------------------------------
+ (instancetype)output
{
return [[self alloc] init];
}
//------------------------------------------------------------------------------
+ (instancetype)outputWithDataSource:(id<EZOutputDataSource>)dataSource
{
return [[self alloc] initWithDataSource:dataSource];
}
//------------------------------------------------------------------------------
+ (instancetype)outputWithDataSource:(id<EZOutputDataSource>)dataSource
inputFormat:(AudioStreamBasicDescription)inputFormat
{
return [[self alloc] initWithDataSource:dataSource
inputFormat:inputFormat];
}
//------------------------------------------------------------------------------
#pragma mark - Singleton
//------------------------------------------------------------------------------
+ (instancetype)sharedOutput
{
static EZOutput *output;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
output = [[self alloc] init];
});
return output;
}
//------------------------------------------------------------------------------
#pragma mark - Setup
//------------------------------------------------------------------------------
- (void)setup
{
//
// Create structure to hold state data
//
self.info = (EZOutputInfo *)malloc(sizeof(EZOutputInfo));
memset(self.info, 0, sizeof(EZOutputInfo));
//
// Setup the audio graph
//
[EZAudioUtilities checkResult:NewAUGraph(&self.info->graph)
operation:"Failed to create graph"];
//
// Add converter node
//
AudioComponentDescription converterDescription;
converterDescription.componentType = kAudioUnitType_FormatConverter;
converterDescription.componentSubType = kAudioUnitSubType_AUConverter;
converterDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
[EZAudioUtilities checkResult:AUGraphAddNode(self.info->graph,
&converterDescription,
&self.info->converterNodeInfo.node)
operation:"Failed to add converter node to audio graph"];
//
// Add mixer node
//
AudioComponentDescription mixerDescription;
mixerDescription.componentType = kAudioUnitType_Mixer;
#if TARGET_OS_IPHONE
mixerDescription.componentSubType = kAudioUnitSubType_MultiChannelMixer;
#elif TARGET_OS_MAC
mixerDescription.componentSubType = kAudioUnitSubType_StereoMixer;
#endif
mixerDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
[EZAudioUtilities checkResult:AUGraphAddNode(self.info->graph,
&mixerDescription,
&self.info->mixerNodeInfo.node)
operation:"Failed to add mixer node to audio graph"];
//
// Add output node
//
AudioComponentDescription outputDescription;
outputDescription.componentType = kAudioUnitType_Output;
outputDescription.componentSubType = [self outputAudioUnitSubType];
outputDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
[EZAudioUtilities checkResult:AUGraphAddNode(self.info->graph,
&outputDescription,
&self.info->outputNodeInfo.node)
operation:"Failed to add output node to audio graph"];
//
// Open the graph
//
[EZAudioUtilities checkResult:AUGraphOpen(self.info->graph)
operation:"Failed to open graph"];
//
// Make node connections
//
OSStatus status = [self connectOutputOfSourceNode:self.info->converterNodeInfo.node
sourceNodeOutputBus:0
toDestinationNode:self.info->mixerNodeInfo.node
destinationNodeInputBus:0
inGraph:self.info->graph];
[EZAudioUtilities checkResult:status
operation:"Failed to connect output of source node to destination node in graph"];
//
// Connect mixer to output
//
[EZAudioUtilities checkResult:AUGraphConnectNodeInput(self.info->graph,
self.info->mixerNodeInfo.node,
0,
self.info->outputNodeInfo.node,
0)
operation:"Failed to connect mixer node to output node"];
//
// Get the audio units
//
[EZAudioUtilities checkResult:AUGraphNodeInfo(self.info->graph,
self.info->converterNodeInfo.node,
&converterDescription,
&self.info->converterNodeInfo.audioUnit)
operation:"Failed to get converter audio unit"];
[EZAudioUtilities checkResult:AUGraphNodeInfo(self.info->graph,
self.info->mixerNodeInfo.node,
&mixerDescription,
&self.info->mixerNodeInfo.audioUnit)
operation:"Failed to get mixer audio unit"];
[EZAudioUtilities checkResult:AUGraphNodeInfo(self.info->graph,
self.info->outputNodeInfo.node,
&outputDescription,
&self.info->outputNodeInfo.audioUnit)
operation:"Failed to get output audio unit"];
//
// Add a node input callback for the converter node
//
AURenderCallbackStruct converterCallback;
converterCallback.inputProc = EZOutputConverterInputCallback;
converterCallback.inputProcRefCon = (__bridge void *)(self);
[EZAudioUtilities checkResult:AUGraphSetNodeInputCallback(self.info->graph,
self.info->converterNodeInfo.node,
0,
&converterCallback)
operation:"Failed to set render callback on converter node"];
//
// Set stream formats
//
[self setClientFormat:[self defaultClientFormat]];
[self setInputFormat:[self defaultInputFormat]];
#if TARGET_OS_IPHONE
EZAudioDevice *currentOutputDevice = [EZAudioDevice currentOutputDevice];
[self setDevice:currentOutputDevice];
#elif TARGET_OS_MAC
NSArray *outputDevices = [EZAudioDevice outputDevices];
EZAudioDevice *defaultOutput = [outputDevices firstObject];
[self setDevice:defaultOutput];
#endif
//
// Set maximum frames per slice to 4096 to allow playback during
// lock screen (iOS only?)
//
UInt32 maximumFramesPerSlice = EZOutputMaximumFramesPerSlice;
[EZAudioUtilities checkResult:AudioUnitSetProperty(self.info->mixerNodeInfo.audioUnit,
kAudioUnitProperty_MaximumFramesPerSlice,
kAudioUnitScope_Global,
0,
&maximumFramesPerSlice,
sizeof(maximumFramesPerSlice))
operation:"Failed to set maximum frames per slice on mixer node"];
//
// Initialize all the audio units in the graph
//
[EZAudioUtilities checkResult:AUGraphInitialize(self.info->graph)
operation:"Failed to initialize graph"];
//
// Add render callback
//
[EZAudioUtilities checkResult:AudioUnitAddRenderNotify(self.info->mixerNodeInfo.audioUnit,
EZOutputGraphRenderCallback,
(__bridge void *)(self))
operation:"Failed to add render callback"];
}
//------------------------------------------------------------------------------
#pragma mark - Actions
//------------------------------------------------------------------------------
- (void)startPlayback
{
//
// Start the AUGraph
//
[EZAudioUtilities checkResult:AUGraphStart(self.info->graph)
operation:"Failed to start graph"];
//
// Notify delegate
//
if ([self.delegate respondsToSelector:@selector(output:changedPlayingState:)])
{
[self.delegate output:self changedPlayingState:[self isPlaying]];
}
}
//------------------------------------------------------------------------------
- (void)stopPlayback
{
//
// Stop the AUGraph
//
[EZAudioUtilities checkResult:AUGraphStop(self.info->graph)
operation:"Failed to stop graph"];
//
// Notify delegate
//
if ([self.delegate respondsToSelector:@selector(output:changedPlayingState:)])
{
[self.delegate output:self changedPlayingState:[self isPlaying]];
}
}
//------------------------------------------------------------------------------
#pragma mark - Getters
//------------------------------------------------------------------------------
- (AudioStreamBasicDescription)clientFormat
{
return self.info->clientFormat;
}
//------------------------------------------------------------------------------
- (AudioStreamBasicDescription)inputFormat
{
return self.info->inputFormat;
}
//------------------------------------------------------------------------------
- (BOOL)isPlaying
{
Boolean isPlaying;
[EZAudioUtilities checkResult:AUGraphIsRunning(self.info->graph,
&isPlaying)
operation:"Failed to check if graph is running"];
return isPlaying;
}
//------------------------------------------------------------------------------
- (float)pan
{
AudioUnitParameterID param;
#if TARGET_OS_IPHONE
param = kMultiChannelMixerParam_Pan;
#elif TARGET_OS_MAC
param = kStereoMixerParam_Pan;
#endif
AudioUnitParameterValue pan;
[EZAudioUtilities checkResult:AudioUnitGetParameter(self.info->mixerNodeInfo.audioUnit,
param,
kAudioUnitScope_Input,
0,
&pan) operation:"Failed to get pan from mixer unit"];
return pan;
}
//------------------------------------------------------------------------------
- (float)volume
{
AudioUnitParameterID param;
#if TARGET_OS_IPHONE
param = kMultiChannelMixerParam_Volume;
#elif TARGET_OS_MAC
param = kStereoMixerParam_Volume;
#endif
AudioUnitParameterValue volume;
[EZAudioUtilities checkResult:AudioUnitGetParameter(self.info->mixerNodeInfo.audioUnit,
param,
kAudioUnitScope_Input,
0,
&volume)
operation:"Failed to get volume from mixer unit"];
return volume;
}
//------------------------------------------------------------------------------
#pragma mark - Setters
//------------------------------------------------------------------------------
- (void)setClientFormat:(AudioStreamBasicDescription)clientFormat
{
if (self.floatConverter)
{
self.floatConverter = nil;
[EZAudioUtilities freeFloatBuffers:self.info->floatData
numberOfChannels:self.clientFormat.mChannelsPerFrame];
}
self.info->clientFormat = clientFormat;
[EZAudioUtilities checkResult:AudioUnitSetProperty(self.info->converterNodeInfo.audioUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
0,
&self.info->clientFormat,
sizeof(self.info->clientFormat))
operation:"Failed to set output client format on converter audio unit"];
[EZAudioUtilities checkResult:AudioUnitSetProperty(self.info->mixerNodeInfo.audioUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
0,
&self.info->clientFormat,
sizeof(self.info->clientFormat))
operation:"Failed to set input client format on mixer audio unit"];
[EZAudioUtilities checkResult:AudioUnitSetProperty(self.info->mixerNodeInfo.audioUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
0,
&self.info->clientFormat,
sizeof(self.info->clientFormat))
operation:"Failed to set output client format on mixer audio unit"];
self.floatConverter = [[EZAudioFloatConverter alloc] initWithInputFormat:clientFormat];
self.info->floatData = [EZAudioUtilities floatBuffersWithNumberOfFrames:EZOutputMaximumFramesPerSlice
numberOfChannels:clientFormat.mChannelsPerFrame];
}
//------------------------------------------------------------------------------
- (void)setDevice:(EZAudioDevice *)device
{
#if TARGET_OS_IPHONE
// if the devices are equal then ignore
if ([device isEqual:self.device])
{
return;
}
NSError *error;
[[AVAudioSession sharedInstance] setOutputDataSource:device.dataSource error:&error];
if (error)
{
NSLog(@"Error setting output device data source (%@), reason: %@",
device.dataSource,
error.localizedDescription);
}
#elif TARGET_OS_MAC
UInt32 outputEnabled = device.outputChannelCount > 0;
NSAssert(outputEnabled, @"Selected EZAudioDevice does not have any output channels");
NSAssert([self outputAudioUnitSubType] == kAudioUnitSubType_HALOutput,
@"Audio device selection on OSX is only available when using the kAudioUnitSubType_HALOutput output unit subtype");
[EZAudioUtilities checkResult:AudioUnitSetProperty(self.info->outputNodeInfo.audioUnit,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Output,
0,
&outputEnabled,
sizeof(outputEnabled))
operation:"Failed to set flag on device output"];
AudioDeviceID deviceId = device.deviceID;
[EZAudioUtilities checkResult:AudioUnitSetProperty(self.info->outputNodeInfo.audioUnit,
kAudioOutputUnitProperty_CurrentDevice,
kAudioUnitScope_Global,
0,
&deviceId,
sizeof(AudioDeviceID))
operation:"Couldn't set default device on I/O unit"];
#endif
// store device
_device = device;
// notify delegate
if ([self.delegate respondsToSelector:@selector(output:changedDevice:)])
{
[self.delegate output:self changedDevice:device];
}
}
//------------------------------------------------------------------------------
- (void)setInputFormat:(AudioStreamBasicDescription)inputFormat
{
self.info->inputFormat = inputFormat;
[EZAudioUtilities checkResult:AudioUnitSetProperty(self.info->converterNodeInfo.audioUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
0,
&inputFormat,
sizeof(inputFormat))
operation:"Failed to set input format on converter audio unit"];
}
//------------------------------------------------------------------------------
- (void)setPan:(float)pan
{
AudioUnitParameterID param;
#if TARGET_OS_IPHONE
param = kMultiChannelMixerParam_Pan;
#elif TARGET_OS_MAC
param = kStereoMixerParam_Pan;
#endif
[EZAudioUtilities checkResult:AudioUnitSetParameter(self.info->mixerNodeInfo.audioUnit,
param,
kAudioUnitScope_Input,
0,
pan,
0)
operation:"Failed to set volume on mixer unit"];
}
//------------------------------------------------------------------------------
- (void)setVolume:(float)volume
{
AudioUnitParameterID param;
#if TARGET_OS_IPHONE
param = kMultiChannelMixerParam_Volume;
#elif TARGET_OS_MAC
param = kStereoMixerParam_Volume;
#endif
[EZAudioUtilities checkResult:AudioUnitSetParameter(self.info->mixerNodeInfo.audioUnit,
param,
kAudioUnitScope_Input,
0,
volume,
0)
operation:"Failed to set volume on mixer unit"];
}
//------------------------------------------------------------------------------
#pragma mark - Core Audio Properties
//------------------------------------------------------------------------------
- (AUGraph)graph
{
return self.info->graph;
}
//------------------------------------------------------------------------------
- (AudioUnit)converterAudioUnit
{
return self.info->converterNodeInfo.audioUnit;
}
//------------------------------------------------------------------------------
- (AudioUnit)mixerAudioUnit
{
return self.info->mixerNodeInfo.audioUnit;
}
//------------------------------------------------------------------------------
- (AudioUnit)outputAudioUnit
{
return self.info->outputNodeInfo.audioUnit;
}
//------------------------------------------------------------------------------
#pragma mark - Subclass
//------------------------------------------------------------------------------
- (OSStatus)connectOutputOfSourceNode:(AUNode)sourceNode
sourceNodeOutputBus:(UInt32)sourceNodeOutputBus
toDestinationNode:(AUNode)destinationNode
destinationNodeInputBus:(UInt32)destinationNodeInputBus
inGraph:(AUGraph)graph
{
//
// Default implementation is to just connect the source to destination
//
[EZAudioUtilities checkResult:AUGraphConnectNodeInput(graph,
sourceNode,
sourceNodeOutputBus,
destinationNode,
destinationNodeInputBus)
operation:"Failed to connect converter node to mixer node"];
return noErr;
}
//------------------------------------------------------------------------------
- (AudioStreamBasicDescription)defaultClientFormat
{
return [EZAudioUtilities stereoFloatNonInterleavedFormatWithSampleRate:EZOutputDefaultSampleRate];
}
//------------------------------------------------------------------------------
- (AudioStreamBasicDescription)defaultInputFormat
{
return [EZAudioUtilities stereoFloatNonInterleavedFormatWithSampleRate:EZOutputDefaultSampleRate];
}
//------------------------------------------------------------------------------
- (OSType)outputAudioUnitSubType
{
#if TARGET_OS_IPHONE
return kAudioUnitSubType_RemoteIO;
#elif TARGET_OS_MAC
return kAudioUnitSubType_HALOutput;
#endif
}
//------------------------------------------------------------------------------
@end
//------------------------------------------------------------------------------
#pragma mark - Callbacks (Implementation)
//------------------------------------------------------------------------------
OSStatus EZOutputConverterInputCallback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData)
{
EZOutput *output = (__bridge EZOutput *)inRefCon;
//
// Try to ask the data source for audio data to fill out the output's
// buffer list
//
if ([output.dataSource respondsToSelector:@selector(output:shouldFillAudioBufferList:withNumberOfFrames:timestamp:)])
{
return [output.dataSource output:output
shouldFillAudioBufferList:ioData
withNumberOfFrames:inNumberFrames
timestamp:inTimeStamp];
}
else
{
//
// Silence if there is nothing to output
//
for (int i = 0; i < ioData->mNumberBuffers; i++)
{
memset(ioData->mBuffers[i].mData,
0,
ioData->mBuffers[i].mDataByteSize);
}
}
return noErr;
}
//------------------------------------------------------------------------------
OSStatus EZOutputGraphRenderCallback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData)
{
EZOutput *output = (__bridge EZOutput *)inRefCon;
//
// provide the audio received delegate callback
//
if (*ioActionFlags & kAudioUnitRenderAction_PostRender)
{
if ([output.delegate respondsToSelector:@selector(output:playedAudio:withBufferSize:withNumberOfChannels:)])
{
UInt32 frames = ioData->mBuffers[0].mDataByteSize / output.info->clientFormat.mBytesPerFrame;
[output.floatConverter convertDataFromAudioBufferList:ioData
withNumberOfFrames:frames
toFloatBuffers:output.info->floatData];
[output.delegate output:output
playedAudio:output.info->floatData
withBufferSize:inNumberFrames
withNumberOfChannels:output.info->clientFormat.mChannelsPerFrame];
}
}
return noErr;
}
-521
View File
@@ -1,521 +0,0 @@
//
// EZAudio.h
// EZAudio
//
// Created by Syed Haris Ali on 11/21/13.
// Copyright (c) 2015 Syed Haris Ali. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
//------------------------------------------------------------------------------
#pragma mark - Core Components
//------------------------------------------------------------------------------
#import "EZAudioDevice.h"
#import "EZAudioFile.h"
#import "EZMicrophone.h"
#import "EZOutput.h"
#import "EZRecorder.h"
#import "EZAudioPlayer.h"
//------------------------------------------------------------------------------
#pragma mark - Interface Components
//------------------------------------------------------------------------------
#import "EZPlot.h"
#import "EZAudioDisplayLink.h"
#import "EZAudioPlot.h"
#import "EZAudioPlotGL.h"
//------------------------------------------------------------------------------
#pragma mark - Utility Components
//------------------------------------------------------------------------------
#import "EZAudioFloatConverter.h"
#import "EZAudioFloatData.h"
#import "EZAudioUtilities.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.
@warning As of 1.0 these methods have been moved over to `EZAudioUtilities` to allow using specific modules without requiring the whole library.
*/
@interface EZAudio : NSObject
//------------------------------------------------------------------------------
#pragma mark - Debugging
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Debugging EZAudio
///-----------------------------------------------------------
/**
Globally sets whether or not the program should exit if a `checkResult:operation:` operation fails. Currently the behavior on EZAudio is to quit if a `checkResult:operation:` fails, but this is not desirable in any production environment. Internally there are a lot of `checkResult:operation:` operations used on all the core classes. This should only ever be set to NO in production environments since a `checkResult:operation:` failing means something breaking has likely happened.
@param shouldExitOnCheckResultFail A BOOL indicating whether or not the running program should exist due to a `checkResult:operation:` fail.
@deprecated This method is deprecated starting in version 0.1.0.
@note Please use same method in EZAudioUtilities class instead.
*/
+ (void)setShouldExitOnCheckResultFail:(BOOL)shouldExitOnCheckResultFail __attribute__((deprecated));
//------------------------------------------------------------------------------
/**
Provides a flag indicating whether or not the program will exit if a `checkResult:operation:` fails.
@deprecated This method is deprecated starting in version 0.1.0.
@note Please use same method in EZAudioUtilities class instead.
@return A BOOL indicating whether or not the program will exit if a `checkResult:operation:` fails.
*/
+ (BOOL)shouldExitOnCheckResultFail __attribute__((deprecated));
//------------------------------------------------------------------------------
#pragma mark - AudioBufferList Utility
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name AudioBufferList Utility
///-----------------------------------------------------------
/**
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)
@deprecated This method is deprecated starting in version 0.1.0.
@note Please use same method in EZAudioUtilities class instead.
@return An AudioBufferList struct that has been allocated in memory
*/
+ (AudioBufferList *)audioBufferListWithNumberOfFrames:(UInt32)frames
numberOfChannels:(UInt32)channels
interleaved:(BOOL)interleaved __attribute__((deprecated));
//------------------------------------------------------------------------------
/**
Allocates an array of float arrays given the number of frames needed to store in each float array.
@param frames A UInt32 representing the number of frames to store in each float buffer
@param channels A UInt32 representing the number of channels (i.e. the number of float arrays to allocate)
@deprecated This method is deprecated starting in version 0.1.0.
@note Please use same method in EZAudioUtilities class instead.
@return An array of float arrays, each the length of the number of frames specified
*/
+ (float **)floatBuffersWithNumberOfFrames:(UInt32)frames
numberOfChannels:(UInt32)channels __attribute__((deprecated));
//------------------------------------------------------------------------------
/**
Deallocates an AudioBufferList structure from memory.
@param bufferList A pointer to the buffer list you would like to free
@deprecated This method is deprecated starting in version 0.1.0.
@note Please use same method in EZAudioUtilities class instead.
*/
+ (void)freeBufferList:(AudioBufferList *)bufferList __attribute__((deprecated));
//------------------------------------------------------------------------------
/**
Deallocates an array of float buffers
@param buffers An array of float arrays
@param channels A UInt32 representing the number of channels (i.e. the number of float arrays to deallocate)
@deprecated This method is deprecated starting in version 0.1.0.
@note Please use same method in EZAudioUtilities class instead.
*/
+ (void)freeFloatBuffers:(float **)buffers numberOfChannels:(UInt32)channels __attribute__((deprecated));
//------------------------------------------------------------------------------
#pragma mark - AudioStreamBasicDescription Utilties
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Creating An AudioStreamBasicDescription
///-----------------------------------------------------------
/**
Creates a signed-integer, interleaved AudioStreamBasicDescription for the number of channels specified for an AIFF format.
@param channels The desired number of channels
@param sampleRate A float representing the sample rate.
@deprecated This method is deprecated starting in version 0.1.0.
@note Please use same method in EZAudioUtilities class instead.
@return A new AudioStreamBasicDescription with the specified format.
*/
+ (AudioStreamBasicDescription)AIFFFormatWithNumberOfChannels:(UInt32)channels
sampleRate:(float)sampleRate __attribute__((deprecated));
//------------------------------------------------------------------------------
/**
Creates an AudioStreamBasicDescription for the iLBC narrow band speech codec.
@param sampleRate A float representing the sample rate.
@deprecated This method is deprecated starting in version 0.1.0.
@note Please use same method in EZAudioUtilities class instead.
@return A new AudioStreamBasicDescription with the specified format.
*/
+ (AudioStreamBasicDescription)iLBCFormatWithSampleRate:(float)sampleRate __attribute__((deprecated));
//------------------------------------------------------------------------------
/**
Creates a float-based, non-interleaved AudioStreamBasicDescription for the number of channels specified.
@param channels A UInt32 representing the number of channels.
@param sampleRate A float representing the sample rate.
@deprecated This method is deprecated starting in version 0.1.0.
@note Please use same method in EZAudioUtilities class instead.
@return A float-based AudioStreamBasicDescription with the number of channels specified.
*/
+ (AudioStreamBasicDescription)floatFormatWithNumberOfChannels:(UInt32)channels
sampleRate:(float)sampleRate __attribute__((deprecated));
//------------------------------------------------------------------------------
/**
Creates an AudioStreamBasicDescription for an M4A AAC format.
@param channels The desired number of channels
@param sampleRate A float representing the sample rate.
@deprecated This method is deprecated starting in version 0.1.0.
@note Please use same method in EZAudioUtilities class instead.
@return A new AudioStreamBasicDescription with the specified format.
*/
+ (AudioStreamBasicDescription)M4AFormatWithNumberOfChannels:(UInt32)channels
sampleRate:(float)sampleRate __attribute__((deprecated));
//------------------------------------------------------------------------------
/**
Creates a single-channel, float-based AudioStreamBasicDescription.
@param sampleRate A float representing the sample rate.
@deprecated This method is deprecated starting in version 0.1.0.
@note Please use same method in EZAudioUtilities class instead.
@return A new AudioStreamBasicDescription with the specified format.
*/
+ (AudioStreamBasicDescription)monoFloatFormatWithSampleRate:(float)sampleRate __attribute__((deprecated));
//------------------------------------------------------------------------------
/**
Creates a single-channel, float-based AudioStreamBasicDescription (as of 0.0.6 this is the same as `monoFloatFormatWithSampleRate:`).
@param sampleRate A float representing the sample rate.
@return A new AudioStreamBasicDescription with the specified format.
@deprecated This method is deprecated starting in version 0.1.0.
@note Please use same method in EZAudioUtilities class instead.
*/
+ (AudioStreamBasicDescription)monoCanonicalFormatWithSampleRate:(float)sampleRate __attribute__((deprecated));
//------------------------------------------------------------------------------
/**
Creates a two-channel, non-interleaved, float-based AudioStreamBasicDescription (as of 0.0.6 this is the same as `stereoFloatNonInterleavedFormatWithSampleRate:`).
@param sampleRate A float representing the sample rate.
@deprecated This method is deprecated starting in version 0.1.0.
@note Please use same method in EZAudioUtilities class instead.
@return A new AudioStreamBasicDescription with the specified format.
*/
+ (AudioStreamBasicDescription)stereoCanonicalNonInterleavedFormatWithSampleRate:(float)sampleRate __attribute__((deprecated));
//------------------------------------------------------------------------------
/**
Creates a two-channel, interleaved, float-based AudioStreamBasicDescription.
@param sampleRate A float representing the sample rate.
@deprecated This method is deprecated starting in version 0.1.0.
@note Please use same method in EZAudioUtilities class instead.
@return A new AudioStreamBasicDescription with the specified format.
*/
+ (AudioStreamBasicDescription)stereoFloatInterleavedFormatWithSampleRate:(float)sampleRate __attribute__((deprecated));
//------------------------------------------------------------------------------
/**
Creates a two-channel, non-interleaved, float-based AudioStreamBasicDescription.
@param sampleRate A float representing the sample rate.
@deprecated This method is deprecated starting in version 0.1.0.
@note Please use same method in EZAudioUtilities class instead.
@return A new AudioStreamBasicDescription with the specified format.
*/
+ (AudioStreamBasicDescription)stereoFloatNonInterleavedFormatWithSampleRate:(float)sameRate __attribute__((deprecated));
//------------------------------------------------------------------------------
// @name AudioStreamBasicDescription Helper Functions
//------------------------------------------------------------------------------
/**
Checks an AudioStreamBasicDescription to see if it is a float-based format (as opposed to a signed integer based format).
@param asbd A valid AudioStreamBasicDescription
@deprecated This method is deprecated starting in version 0.1.0.
@note Please use same method in EZAudioUtilities class instead.
@return A BOOL indicating whether or not the AudioStreamBasicDescription is a float format.
*/
+ (BOOL)isFloatFormat:(AudioStreamBasicDescription)asbd __attribute__((deprecated));
//------------------------------------------------------------------------------
/**
Checks an AudioStreamBasicDescription to check for an interleaved flag (samples are
stored in one buffer one after another instead of two (or n channels) parallel buffers
@param asbd A valid AudioStreamBasicDescription
@deprecated This method is deprecated starting in version 0.1.0.
@note Please use same method in EZAudioUtilities class instead.
@return A BOOL indicating whether or not the AudioStreamBasicDescription is interleaved
*/
+ (BOOL)isInterleaved:(AudioStreamBasicDescription)asbd __attribute__((deprecated));
//------------------------------------------------------------------------------
/**
Checks an AudioStreamBasicDescription to see if it is a linear PCM format (uncompressed,
1 frame per packet)
@param asbd A valid AudioStreamBasicDescription
@deprecated This method is deprecated starting in version 0.1.0.
@note Please use same method in EZAudioUtilities class instead.
@return A BOOL indicating whether or not the AudioStreamBasicDescription is linear PCM.
*/
+ (BOOL)isLinearPCM:(AudioStreamBasicDescription)asbd __attribute__((deprecated));
///-----------------------------------------------------------
/// @name AudioStreamBasicDescription Utilities
///-----------------------------------------------------------
/**
Nicely logs out the contents of an AudioStreamBasicDescription struct
@param asbd The AudioStreamBasicDescription struct with content to print out
@deprecated This method is deprecated starting in version 0.1.0.
@note Please use same method in EZAudioUtilities class instead.
*/
+ (void)printASBD:(AudioStreamBasicDescription)asbd __attribute__((deprecated));
//------------------------------------------------------------------------------
/**
Converts seconds into a string formatted as MM:SS
@param seconds An NSTimeInterval representing the number of seconds
@deprecated This method is deprecated starting in version 0.1.0.
@note Please use same method in EZAudioUtilities class instead.
@return An NSString instance formatted as MM:SS from the seconds provided.
*/
+ (NSString *)displayTimeStringFromSeconds:(NSTimeInterval)seconds __attribute__((deprecated));
//------------------------------------------------------------------------------
/**
Creates a string to use when logging out the contents of an AudioStreamBasicDescription
@param asbd A valid AudioStreamBasicDescription struct.
@deprecated This method is deprecated starting in version 0.1.0.
@note Please use same method in EZAudioUtilities class instead.
@return An NSString representing the contents of the AudioStreamBasicDescription.
*/
+ (NSString *)stringForAudioStreamBasicDescription:(AudioStreamBasicDescription)asbd __attribute__((deprecated));
//------------------------------------------------------------------------------
/**
Just a wrapper around the setCanonical function provided in the Core Audio Utility C++ class.
@param asbd The AudioStreamBasicDescription structure to modify
@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
@deprecated This method is deprecated starting in version 0.1.0.
@note Please use same method in EZAudioUtilities class instead.
*/
+ (void)setCanonicalAudioStreamBasicDescription:(AudioStreamBasicDescription*)asbd
numberOfChannels:(UInt32)nChannels
interleaved:(BOOL)interleaved __attribute__((deprecated));
//------------------------------------------------------------------------------
#pragma mark - Math Utilities
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Math Utilities
///-----------------------------------------------------------
/**
Appends an array of values to a history buffer and performs an internal shift to add the values to the tail and removes the same number of values from the head.
@param buffer A float array of values to append to the tail of the history buffer
@param bufferLength The length of the float array being appended to the history buffer
@param scrollHistory The target history buffer in which to append the values
@param scrollHistoryLength The length of the target history buffer
@deprecated This method is deprecated starting in version 0.1.0.
@note Please use same method in EZAudioUtilities class instead.
*/
+ (void)appendBufferAndShift:(float*)buffer
withBufferSize:(int)bufferLength
toScrollHistory:(float*)scrollHistory
withScrollHistorySize:(int)scrollHistoryLength __attribute__((deprecated));
//------------------------------------------------------------------------------
/**
Appends a value to a history buffer and performs an internal shift to add the value to the tail and remove the 0th value.
@param value The float value to append to the history array
@param scrollHistory The target history buffer in which to append the values
@param scrollHistoryLength The length of the target history buffer
@deprecated This method is deprecated starting in version 0.1.0.
@note Please use same method in EZAudioUtilities class instead.
*/
+(void) appendValue:(float)value
toScrollHistory:(float*)scrollHistory
withScrollHistorySize:(int)scrollHistoryLength __attribute__((deprecated));
//------------------------------------------------------------------------------
/**
Maps a value from one coordinate system into another one. Takes in the current value to map, the minimum and maximum values of the first coordinate system, and the minimum and maximum values of the second coordinate system and calculates the mapped value in the second coordinate system's constraints.
@param value The value expressed in the first coordinate system
@param leftMin The minimum of the first coordinate system
@param leftMax The maximum of the first coordinate system
@param rightMin The minimum of the second coordindate system
@param rightMax The maximum of the second coordinate system
@deprecated This method is deprecated starting in version 0.1.0.
@note Please use same method in EZAudioUtilities class instead.
@return The mapped value in terms of the second coordinate system
*/
+ (float)MAP:(float)value
leftMin:(float)leftMin
leftMax:(float)leftMax
rightMin:(float)rightMin
rightMax:(float)rightMax __attribute__((deprecated));
//------------------------------------------------------------------------------
/**
Calculates the root mean squared for a buffer.
@param buffer A float buffer array of values whose root mean squared to calculate
@param bufferSize The size of the float buffer
@deprecated This method is deprecated starting in version 0.1.0.
@note Please use same method in EZAudioUtilities class instead.
@return The root mean squared of the buffer
*/
+ (float)RMS:(float*)buffer length:(int)bufferSize __attribute__((deprecated));
//------------------------------------------------------------------------------
/**
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
@deprecated This method is deprecated starting in version 0.1.0.
@note Please use same method in EZAudioUtilities class instead.
@return The float sign value
*/
+ (float)SGN:(float)value __attribute__((deprecated));
//------------------------------------------------------------------------------
#pragma mark - OSStatus Utility
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name OSStatus Utility
///-----------------------------------------------------------
/**
Basic check result function useful for checking each step of the audio setup process
@param result The OSStatus representing the result of an operation
@param operation A string (const char, not NSString) describing the operation taking place (will print if fails)
@deprecated This method is deprecated starting in version 0.1.0.
@note Please use same method in EZAudioUtilities class instead.
*/
+ (void)checkResult:(OSStatus)result operation:(const char *)operation __attribute__((deprecated));
//------------------------------------------------------------------------------
/**
Provides a string representation of the often cryptic Core Audio error codes
@param code A UInt32 representing an error code
@deprecated This method is deprecated starting in version 0.1.0.
@note Please use same method in EZAudioUtilities class instead.
@return An NSString with a human readable version of the error code.
*/
+ (NSString *)stringFromUInt32Code:(UInt32)code __attribute__((deprecated));
//------------------------------------------------------------------------------
#pragma mark - Plot Utility
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Plot Utility
///-----------------------------------------------------------
/**
Given a buffer representing a window of float history data this append the RMS of a buffer of incoming float data...This will likely be deprecated in a future version of EZAudio for a circular buffer based approach.
@param scrollHistory An array of float arrays being used to hold the history values for each channel.
@param scrollHistoryLength An int representing the length of the history window.
@param index An int pointer to the index of the current read index of the history buffer.
@param buffer A float array representing the incoming audio data.
@param bufferSize An int representing the length of the incoming audio data.
@param isChanging A BOOL pointer representing whether the resolution (length of the history window) is currently changing.
@deprecated This method is deprecated starting in version 0.1.0.
@note Please use same method in EZAudioUtilities class instead.
*/
+ (void)updateScrollHistory:(float **)scrollHistory
withLength:(int)scrollHistoryLength
atIndex:(int *)index
withBuffer:(float *)buffer
withBufferSize:(int)bufferSize
isResolutionChanging:(BOOL *)isChanging __attribute__((deprecated));
//------------------------------------------------------------------------------
#pragma mark - TPCircularBuffer Utility
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name TPCircularBuffer Utility
///-----------------------------------------------------------
/**
Appends the data from the audio buffer list to the circular buffer
@param circularBuffer Pointer to the instance of the TPCircularBuffer to add the audio data to
@param audioBufferList Pointer to the instance of the AudioBufferList with the audio data
@deprecated This method is deprecated starting in version 0.1.0.
@note Please use same method in EZAudioUtilities class instead.
*/
+ (void)appendDataToCircularBuffer:(TPCircularBuffer*)circularBuffer
fromAudioBufferList:(AudioBufferList*)audioBufferList __attribute__((deprecated));
//------------------------------------------------------------------------------
/**
Initializes the circular buffer (just a wrapper around the C method)
* @param circularBuffer Pointer to an instance of the TPCircularBuffer
* @param size The length of the TPCircularBuffer (usually 1024)
@deprecated This method is deprecated starting in version 0.1.0.
@note Please use same method in EZAudioUtilities class instead.
*/
+ (void)circularBuffer:(TPCircularBuffer*)circularBuffer
withSize:(int)size __attribute__((deprecated));
//------------------------------------------------------------------------------
/**
Frees a circular buffer
@param circularBuffer Pointer to the circular buffer to clear
@deprecated This method is deprecated starting in version 0.1.0.
@note Please use same method in EZAudioUtilities class instead.
*/
+ (void)freeCircularBuffer:(TPCircularBuffer*)circularBuffer __attribute__((deprecated));
//------------------------------------------------------------------------------
@end
-307
View File
@@ -1,307 +0,0 @@
//
// EZAudio.m
// EZAudioCoreGraphicsWaveformExample
//
// Created by Syed Haris Ali on 5/13/15.
// Copyright (c) 2015 Syed Haris Ali. All rights reserved.
//
#import "EZAudio.h"
@implementation EZAudio
//------------------------------------------------------------------------------
#pragma mark - Debugging
//------------------------------------------------------------------------------
+ (void)setShouldExitOnCheckResultFail:(BOOL)shouldExitOnCheckResultFail
{
[EZAudioUtilities setShouldExitOnCheckResultFail:shouldExitOnCheckResultFail];
}
//------------------------------------------------------------------------------
+ (BOOL)shouldExitOnCheckResultFail
{
return [EZAudioUtilities shouldExitOnCheckResultFail];
}
//------------------------------------------------------------------------------
#pragma mark - AudioBufferList Utility
//------------------------------------------------------------------------------
+ (AudioBufferList *)audioBufferListWithNumberOfFrames:(UInt32)frames
numberOfChannels:(UInt32)channels
interleaved:(BOOL)interleaved
{
return [EZAudioUtilities audioBufferListWithNumberOfFrames:frames
numberOfChannels:channels
interleaved:interleaved];
}
//------------------------------------------------------------------------------
+ (float **)floatBuffersWithNumberOfFrames:(UInt32)frames
numberOfChannels:(UInt32)channels
{
return [EZAudioUtilities floatBuffersWithNumberOfFrames:frames
numberOfChannels:channels];
}
//------------------------------------------------------------------------------
+ (void)freeBufferList:(AudioBufferList *)bufferList
{
[EZAudioUtilities freeBufferList:bufferList];
}
//------------------------------------------------------------------------------
+ (void)freeFloatBuffers:(float **)buffers numberOfChannels:(UInt32)channels
{
[EZAudioUtilities freeFloatBuffers:buffers numberOfChannels:channels];
}
//------------------------------------------------------------------------------
#pragma mark - AudioStreamBasicDescription Utility
//------------------------------------------------------------------------------
+ (AudioStreamBasicDescription)AIFFFormatWithNumberOfChannels:(UInt32)channels
sampleRate:(float)sampleRate
{
return [EZAudioUtilities AIFFFormatWithNumberOfChannels:channels
sampleRate:sampleRate];
}
//------------------------------------------------------------------------------
+ (AudioStreamBasicDescription)iLBCFormatWithSampleRate:(float)sampleRate
{
return [EZAudioUtilities iLBCFormatWithSampleRate:sampleRate];
}
//------------------------------------------------------------------------------
+ (AudioStreamBasicDescription)floatFormatWithNumberOfChannels:(UInt32)channels
sampleRate:(float)sampleRate
{
return [EZAudioUtilities floatFormatWithNumberOfChannels:channels
sampleRate:sampleRate];
}
//------------------------------------------------------------------------------
+ (AudioStreamBasicDescription)M4AFormatWithNumberOfChannels:(UInt32)channels
sampleRate:(float)sampleRate
{
return [EZAudioUtilities M4AFormatWithNumberOfChannels:channels
sampleRate:sampleRate];
}
//------------------------------------------------------------------------------
+ (AudioStreamBasicDescription)monoFloatFormatWithSampleRate:(float)sampleRate
{
return [EZAudioUtilities monoFloatFormatWithSampleRate:sampleRate];
}
//------------------------------------------------------------------------------
+ (AudioStreamBasicDescription)monoCanonicalFormatWithSampleRate:(float)sampleRate
{
return [EZAudioUtilities monoCanonicalFormatWithSampleRate:sampleRate];
}
//------------------------------------------------------------------------------
+ (AudioStreamBasicDescription)stereoCanonicalNonInterleavedFormatWithSampleRate:(float)sampleRate
{
return [EZAudioUtilities stereoCanonicalNonInterleavedFormatWithSampleRate:sampleRate];
}
//------------------------------------------------------------------------------
+ (AudioStreamBasicDescription)stereoFloatInterleavedFormatWithSampleRate:(float)sampleRate
{
return [EZAudioUtilities stereoFloatInterleavedFormatWithSampleRate:sampleRate];
}
//------------------------------------------------------------------------------
+ (AudioStreamBasicDescription)stereoFloatNonInterleavedFormatWithSampleRate:(float)sampleRate
{
return [EZAudioUtilities stereoFloatNonInterleavedFormatWithSampleRate:sampleRate];
}
//------------------------------------------------------------------------------
+ (BOOL)isFloatFormat:(AudioStreamBasicDescription)asbd
{
return [EZAudioUtilities isFloatFormat:asbd];
}
//------------------------------------------------------------------------------
+ (BOOL)isInterleaved:(AudioStreamBasicDescription)asbd
{
return [EZAudioUtilities isInterleaved:asbd];
}
//------------------------------------------------------------------------------
+ (BOOL)isLinearPCM:(AudioStreamBasicDescription)asbd
{
return [EZAudioUtilities isLinearPCM:asbd];
}
//------------------------------------------------------------------------------
+ (void)printASBD:(AudioStreamBasicDescription)asbd
{
[EZAudioUtilities printASBD:asbd];
}
//------------------------------------------------------------------------------
+ (NSString *)displayTimeStringFromSeconds:(NSTimeInterval)seconds
{
return [EZAudioUtilities displayTimeStringFromSeconds:seconds];
}
//------------------------------------------------------------------------------
+ (NSString *)stringForAudioStreamBasicDescription:(AudioStreamBasicDescription)asbd
{
return [EZAudioUtilities stringForAudioStreamBasicDescription:asbd];
}
//------------------------------------------------------------------------------
+ (void)setCanonicalAudioStreamBasicDescription:(AudioStreamBasicDescription*)asbd
numberOfChannels:(UInt32)nChannels
interleaved:(BOOL)interleaved
{
[EZAudioUtilities setCanonicalAudioStreamBasicDescription:asbd
numberOfChannels:nChannels
interleaved:interleaved];
}
//------------------------------------------------------------------------------
#pragma mark - Math Utilities
//------------------------------------------------------------------------------
+ (void)appendBufferAndShift:(float*)buffer
withBufferSize:(int)bufferLength
toScrollHistory:(float*)scrollHistory
withScrollHistorySize:(int)scrollHistoryLength
{
[EZAudioUtilities appendBufferAndShift:buffer
withBufferSize:bufferLength
toScrollHistory:scrollHistory
withScrollHistorySize:scrollHistoryLength];
}
//------------------------------------------------------------------------------
+ (void) appendValue:(float)value
toScrollHistory:(float*)scrollHistory
withScrollHistorySize:(int)scrollHistoryLength
{
[EZAudioUtilities appendValue:value
toScrollHistory:scrollHistory
withScrollHistorySize:scrollHistoryLength];
}
//------------------------------------------------------------------------------
+ (float)MAP:(float)value
leftMin:(float)leftMin
leftMax:(float)leftMax
rightMin:(float)rightMin
rightMax:(float)rightMax
{
return [EZAudioUtilities MAP:value
leftMin:leftMin
leftMax:leftMax
rightMin:rightMin
rightMax:rightMax];
}
//------------------------------------------------------------------------------
+ (float)RMS:(float *)buffer length:(int)bufferSize
{
return [EZAudioUtilities RMS:buffer length:bufferSize];
}
//------------------------------------------------------------------------------
+ (float)SGN:(float)value
{
return [EZAudioUtilities SGN:value];
}
//------------------------------------------------------------------------------
#pragma mark - OSStatus Utility
//------------------------------------------------------------------------------
+ (void)checkResult:(OSStatus)result operation:(const char *)operation
{
[EZAudioUtilities checkResult:result
operation:operation];
}
//------------------------------------------------------------------------------
+ (NSString *)stringFromUInt32Code:(UInt32)code
{
return [EZAudioUtilities stringFromUInt32Code:code];
}
//------------------------------------------------------------------------------
#pragma mark - Plot Utility
//------------------------------------------------------------------------------
+ (void)updateScrollHistory:(float **)scrollHistory
withLength:(int)scrollHistoryLength
atIndex:(int *)index
withBuffer:(float *)buffer
withBufferSize:(int)bufferSize
isResolutionChanging:(BOOL *)isChanging
{
[EZAudioUtilities updateScrollHistory:scrollHistory
withLength:scrollHistoryLength
atIndex:index
withBuffer:buffer
withBufferSize:bufferSize
isResolutionChanging:isChanging];
}
//------------------------------------------------------------------------------
#pragma mark - TPCircularBuffer Utility
//------------------------------------------------------------------------------
+ (void)appendDataToCircularBuffer:(TPCircularBuffer *)circularBuffer
fromAudioBufferList:(AudioBufferList *)audioBufferList
{
[EZAudioUtilities appendDataToCircularBuffer:circularBuffer
fromAudioBufferList:audioBufferList];
}
//------------------------------------------------------------------------------
+ (void)circularBuffer:(TPCircularBuffer *)circularBuffer withSize:(int)size
{
[EZAudioUtilities circularBuffer:circularBuffer withSize:size];
}
//------------------------------------------------------------------------------
+ (void)freeCircularBuffer:(TPCircularBuffer *)circularBuffer
{
[EZAudioUtilities freeCircularBuffer:circularBuffer];
}
//------------------------------------------------------------------------------
@end
@@ -1,94 +0,0 @@
//
// EZAudioDisplayLink.h
// EZAudio
//
// Created by Syed Haris Ali on 6/25/15.
// Copyright (c) 2015 Syed Haris Ali. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
#import <QuartzCore/QuartzCore.h>
@class EZAudioDisplayLink;
//------------------------------------------------------------------------------
#pragma mark - EZAudioDisplayLinkDelegate
//------------------------------------------------------------------------------
/**
The EZAudioDisplayLinkDelegate provides a means for an EZAudioDisplayLink instance to notify a receiver when it should redraw itself.
*/
@protocol EZAudioDisplayLinkDelegate <NSObject>
@required
/**
Required method for an EZAudioDisplayLinkDelegate to implement. This fires at the screen's display rate (typically 60 fps).
@param displayLink An EZAudioDisplayLink instance used by a receiver to draw itself at the screen's refresh rate.
*/
- (void)displayLinkNeedsDisplay:(EZAudioDisplayLink *)displayLink;
@end
//------------------------------------------------------------------------------
#pragma mark - EZAudioDisplayLink
//------------------------------------------------------------------------------
/**
The EZAudioDisplayLink provides a cross-platform (iOS and Mac) abstraction over the CADisplayLink for iOS and CVDisplayLink for Mac. The purpose of this class is to provide an accurate timer for views that need to redraw themselves at 60 fps. This class is used by the EZAudioPlot and, eventually, the EZAudioPlotGL to provide a timer mechanism to draw real-time plots.
*/
@interface EZAudioDisplayLink : NSObject
//------------------------------------------------------------------------------
#pragma mark - Class Methods
//------------------------------------------------------------------------------
/**
Class method to create an EZAudioDisplayLink. The caller should implement the EZAudioDisplayLinkDelegate protocol to receive the `displayLinkNeedsDisplay:` delegate method to know when to redraw itself.
@param delegate An instance that implements the EZAudioDisplayLinkDelegate protocol.
@return An instance of the EZAudioDisplayLink.
*/
+ (instancetype)displayLinkWithDelegate:(id<EZAudioDisplayLinkDelegate>)delegate;
//------------------------------------------------------------------------------
#pragma mark - Properties
//------------------------------------------------------------------------------
/**
The EZAudioDisplayLinkDelegate for which to receive the redraw calls.
*/
@property (nonatomic, weak) id<EZAudioDisplayLinkDelegate> delegate;
//------------------------------------------------------------------------------
#pragma mark - Instance Methods
//------------------------------------------------------------------------------
/**
Method to start the display link and provide the `displayLinkNeedsDisplay:` calls to the `delegate`
*/
- (void)start;
/**
Method to stop the display link from providing the `displayLinkNeedsDisplay:` calls to the `delegate`
*/
- (void)stop;
//------------------------------------------------------------------------------
@end
@@ -1,180 +0,0 @@
//
// EZAudioDisplayLink.m
// EZAudio
//
// Created by Syed Haris Ali on 6/25/15.
// Copyright (c) 2015 Syed Haris Ali. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "EZAudioDisplayLink.h"
//------------------------------------------------------------------------------
#pragma mark - CVDisplayLink Callback (Declaration)
//------------------------------------------------------------------------------
#if TARGET_OS_IPHONE
#elif TARGET_OS_MAC
static CVReturn EZAudioDisplayLinkCallback(CVDisplayLinkRef displayLinkRef,
const CVTimeStamp *now,
const CVTimeStamp *outputTime,
CVOptionFlags flagsIn,
CVOptionFlags *flagsOut,
void *displayLinkContext);
#endif
//------------------------------------------------------------------------------
#pragma mark - EZAudioDisplayLink (Interface Extension)
//------------------------------------------------------------------------------
@interface EZAudioDisplayLink ()
#if TARGET_OS_IPHONE
@property (nonatomic, strong) CADisplayLink *displayLink;
#elif TARGET_OS_MAC
@property (nonatomic, assign) CVDisplayLinkRef displayLink;
#endif
@property (nonatomic, assign) BOOL stopped;
@end
//------------------------------------------------------------------------------
#pragma mark - EZAudioDisplayLink (Implementation)
//------------------------------------------------------------------------------
@implementation EZAudioDisplayLink
//------------------------------------------------------------------------------
#pragma mark - Dealloc
//------------------------------------------------------------------------------
- (void)dealloc
{
#if TARGET_OS_IPHONE
[self.displayLink invalidate];
#elif TARGET_OS_MAC
CVDisplayLinkStop(self.displayLink);
CVDisplayLinkRelease(self.displayLink);
self.displayLink = nil;
#endif
}
//------------------------------------------------------------------------------
#pragma mark - Class Initialization
//------------------------------------------------------------------------------
+ (instancetype)displayLinkWithDelegate:(id<EZAudioDisplayLinkDelegate>)delegate
{
EZAudioDisplayLink *displayLink = [[self alloc] init];
displayLink.delegate = delegate;
return displayLink;
}
//------------------------------------------------------------------------------
#pragma mark - Initialization
//------------------------------------------------------------------------------
- (instancetype) init
{
self = [super init];
if (self)
{
[self setup];
}
return self;
}
//------------------------------------------------------------------------------
#pragma mark - Setup
//------------------------------------------------------------------------------
- (void)setup
{
self.stopped = YES;
#if TARGET_OS_IPHONE
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(update)];
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
#elif TARGET_OS_MAC
CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink);
CVDisplayLinkSetOutputCallback(self.displayLink,
EZAudioDisplayLinkCallback,
(__bridge void *)(self));
CVDisplayLinkStart(self.displayLink);
#endif
}
//------------------------------------------------------------------------------
#pragma mark - Actions
//------------------------------------------------------------------------------
- (void)start
{
#if TARGET_OS_IPHONE
self.displayLink.paused = NO;
#elif TARGET_OS_MAC
CVDisplayLinkStart(self.displayLink);
#endif
self.stopped = NO;
}
//------------------------------------------------------------------------------
- (void)stop
{
#if TARGET_OS_IPHONE
self.displayLink.paused = YES;
#elif TARGET_OS_MAC
CVDisplayLinkStop(self.displayLink);
#endif
self.stopped = YES;
}
//------------------------------------------------------------------------------
- (void)update
{
if (!self.stopped)
{
if ([self.delegate respondsToSelector:@selector(displayLinkNeedsDisplay:)])
{
[self.delegate displayLinkNeedsDisplay:self];
}
}
}
//------------------------------------------------------------------------------
@end
//------------------------------------------------------------------------------
#pragma mark - CVDisplayLink Callback (Implementation)
//------------------------------------------------------------------------------
#if TARGET_OS_IPHONE
#elif TARGET_OS_MAC
static CVReturn EZAudioDisplayLinkCallback(CVDisplayLinkRef displayLinkRef,
const CVTimeStamp *now,
const CVTimeStamp *outputTime,
CVOptionFlags flagsIn,
CVOptionFlags *flagsOut,
void *displayLinkContext)
{
EZAudioDisplayLink *displayLink = (__bridge EZAudioDisplayLink*)displayLinkContext;
[displayLink update];
return kCVReturnSuccess;
}
#endif
-192
View File
@@ -1,192 +0,0 @@
//
// EZAudioPlot.h
// EZAudio
//
// Created by Syed Haris Ali on 9/2/13.
// Copyright (c) 2015 Syed Haris Ali. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <QuartzCore/QuartzCore.h>
#import "EZPlot.h"
@class EZAudio;
//------------------------------------------------------------------------------
#pragma mark - Constants
//------------------------------------------------------------------------------
/**
The default value used for the maximum rolling history buffer length of any EZAudioPlot.
@deprecated This constant is deprecated starting in version 0.2.0.
@note Please use EZAudioPlotDefaultMaxHistoryBufferLength instead.
*/
FOUNDATION_EXPORT UInt32 const kEZAudioPlotMaxHistoryBufferLength __attribute__((deprecated));
/**
The default value used for the default rolling history buffer length of any EZAudioPlot.
@deprecated This constant is deprecated starting in version 0.2.0.
@note Please use EZAudioPlotDefaultHistoryBufferLength instead.
*/
FOUNDATION_EXPORT UInt32 const kEZAudioPlotDefaultHistoryBufferLength __attribute__((deprecated));
/**
The default value used for the default rolling history buffer length of any EZAudioPlot.
*/
FOUNDATION_EXPORT UInt32 const EZAudioPlotDefaultHistoryBufferLength;
/**
The default value used for the maximum rolling history buffer length of any EZAudioPlot.
*/
FOUNDATION_EXPORT UInt32 const EZAudioPlotDefaultMaxHistoryBufferLength;
//------------------------------------------------------------------------------
#pragma mark - EZAudioPlotWaveformLayer
//------------------------------------------------------------------------------
/**
The EZAudioPlotWaveformLayer is a lightweight subclass of the CAShapeLayer that allows implicit animations on the `path` key.
*/
@interface EZAudioPlotWaveformLayer : CAShapeLayer
@end
//------------------------------------------------------------------------------
#pragma mark - EZAudioPlot
//------------------------------------------------------------------------------
/**
`EZAudioPlot`, a subclass of `EZPlot`, is a cross-platform (iOS and OSX) class that plots an audio waveform using Core Graphics.
The caller provides updates a constant stream of updated audio data in the `updateBuffer:withBufferSize:` function, which in turn will be plotted in one of the plot types:
* Buffer (`EZPlotTypeBuffer`) - A plot that only consists of the current buffer and buffer size from the last call to `updateBuffer:withBufferSize:`. This looks similar to the default openFrameworks input audio example.
* Rolling (`EZPlotTypeRolling`) - A plot that consists of a rolling history of values averaged from each buffer. This is the traditional waveform look.
#Parent Methods and Properties#
See EZPlot for full API methods and properties (colors, plot type, update function)
*/
@interface EZAudioPlot : EZPlot
/**
A BOOL that allows optimizing the audio plot's drawing for real-time displays. Since the update function may be updating the plot's data very quickly (over 60 frames per second) this property will throttle the drawing calls to be 60 frames per second (or whatever the screen rate is). Specifically, it disables implicit path change animations on the `waveformLayer` and sets up a display link to render 60 fps (audio updating the plot at 44.1 kHz causes it to re-render 86 fps - far greater than what is needed for a visual display).
*/
@property (nonatomic, assign) BOOL shouldOptimizeForRealtimePlot;
//------------------------------------------------------------------------------
/**
A BOOL indicating whether the plot should center itself vertically.
*/
@property (nonatomic, assign) BOOL shouldCenterYAxis;
//------------------------------------------------------------------------------
/**
An EZAudioPlotWaveformLayer that is used to render the actual waveform. By switching the drawing code to Core Animation layers in version 0.2.0 most work, specifically the compositing step, is now done on the GPU. Hence, multiple EZAudioPlot instances can be used simultaneously with very low CPU overhead so these are now practical for table and collection views.
*/
@property (nonatomic, strong) EZAudioPlotWaveformLayer *waveformLayer;
//------------------------------------------------------------------------------
#pragma mark - Adjust Resolution
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Adjusting The Resolution
///-----------------------------------------------------------
/**
Sets the length of the rolling history buffer (i.e. the number of points in the rolling plot's buffer). Can grow or shrink the display up to the maximum size specified by the `maximumRollingHistoryLength` method. Will return the actual set value, which will be either the given value if smaller than the `maximumRollingHistoryLength` or `maximumRollingHistoryLength` if a larger value is attempted to be set.
@param historyLength The new length of the rolling history buffer.
@return The new value equal to the historyLength or the `maximumRollingHistoryLength`.
*/
-(int)setRollingHistoryLength:(int)historyLength;
//------------------------------------------------------------------------------
/**
Provides the length of the rolling history buffer (i.e. the number of points in the rolling plot's buffer).
* @return An int representing the length of the rolling history buffer
*/
-(int)rollingHistoryLength;
//------------------------------------------------------------------------------
#pragma mark - Subclass Methods
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Subclass Methods
///-----------------------------------------------------------
/**
Main method that handles converting the points created from the `updatedBuffer:withBufferSize:` method into a CGPathRef to store in the `waveformLayer`. In this method you can create any path you'd like using the point array (for instance, maybe mapping the points to a circle instead of the standard 2D plane).
@param points An array of CGPoint structures, with the x values ranging from 0 - (pointCount - 1) and y values containing the last audio data's buffer.
@param pointCount A UInt32 of the length of the point array.
@param rect An EZRect (CGRect on iOS or NSRect on OSX) that the path should be created relative to.
@return A CGPathRef that is the path you'd like to store on the `waveformLayer` to visualize the audio data.
*/
- (CGPathRef)createPathWithPoints:(CGPoint *)points
pointCount:(UInt32)pointCount
inRect:(EZRect)rect;
//------------------------------------------------------------------------------
/**
Provides the default length of the rolling history buffer when the plot is initialized. Default is `EZAudioPlotDefaultHistoryBufferLength` constant.
@return An int describing the initial length of the rolling history buffer.
*/
- (int)defaultRollingHistoryLength;
//------------------------------------------------------------------------------
/**
Provides the default number of points that will be used to initialize the graph's points data structure that holds. Essentially the plot starts off as a flat line of this many points. Default is 100.
@return An int describing the initial number of points the plot should have when flat lined.
*/
- (int)initialPointCount;
//------------------------------------------------------------------------------
/**
Provides the default maximum rolling history length - that is, the maximum amount of points the `setRollingHistoryLength:` method may be set to. If a length higher than this is set then the plot will likely crash because the appropriate resources are only allocated once during the plot's initialization step. Defualt is `EZAudioPlotDefaultMaxHistoryBufferLength` constant.
@return An int describing the maximum length of the absolute rolling history buffer.
*/
- (int)maximumRollingHistoryLength;
//------------------------------------------------------------------------------
/**
Method to cause the waveform layer's path to get recreated and redrawn on screen using the last buffer of data provided. This is the equivalent to the drawRect: method used to normally subclass a view's drawing. This normally don't need to be overrode though - a better approach would be to override the `createPathWithPoints:pointCount:inRect:` method.
*/
- (void)redraw;
//------------------------------------------------------------------------------
/**
Main method used to copy the sample data from the source buffer and update the
plot. Subclasses can overwrite this method for custom behavior.
@param data A float array of the sample data. Subclasses should copy this data to a separate array to avoid threading issues.
@param length The length of the float array as an int.
*/
-(void)setSampleData:(float *)data length:(int)length;
//------------------------------------------------------------------------------
@end
-451
View File
@@ -1,451 +0,0 @@
//
// EZAudioPlot.m
// EZAudio
//
// Created by Syed Haris Ali on 9/2/13.
// Copyright (c) 2015 Syed Haris Ali. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "EZAudioPlot.h"
#import "EZAudioDisplayLink.h"
//------------------------------------------------------------------------------
#pragma mark - Constants
//------------------------------------------------------------------------------
UInt32 const kEZAudioPlotMaxHistoryBufferLength = 8192;
UInt32 const kEZAudioPlotDefaultHistoryBufferLength = 512;
UInt32 const EZAudioPlotDefaultHistoryBufferLength = 512;
UInt32 const EZAudioPlotDefaultMaxHistoryBufferLength = 8192;
//------------------------------------------------------------------------------
#pragma mark - EZAudioPlot (Interface Extension)
//------------------------------------------------------------------------------
@interface EZAudioPlot () <EZAudioDisplayLinkDelegate>
@property (nonatomic, strong) EZAudioDisplayLink *displayLink;
@property (nonatomic, assign) EZPlotHistoryInfo *historyInfo;
@property (nonatomic, assign) CGPoint *points;
@property (nonatomic, assign) UInt32 pointCount;
@end
//------------------------------------------------------------------------------
#pragma mark - EZAudioPlot (Implementation)
//------------------------------------------------------------------------------
@implementation EZAudioPlot
//------------------------------------------------------------------------------
#pragma mark - Dealloc
//------------------------------------------------------------------------------
- (void)dealloc
{
[EZAudioUtilities freeHistoryInfo:self.historyInfo];
free(self.points);
}
//------------------------------------------------------------------------------
#pragma mark - Initialization
//------------------------------------------------------------------------------
- (id)init
{
self = [super init];
if (self)
{
[self initPlot];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self)
{
[self initPlot];
}
return self;
}
#if TARGET_OS_IPHONE
- (id)initWithFrame:(CGRect)frameRect
#elif TARGET_OS_MAC
- (id)initWithFrame:(NSRect)frameRect
#endif
{
self = [super initWithFrame:frameRect];
if (self)
{
[self initPlot];
}
return self;
}
#if TARGET_OS_IPHONE
- (void)layoutSubviews
{
[super layoutSubviews];
[CATransaction begin];
[CATransaction setDisableActions:YES];
self.waveformLayer.frame = self.bounds;
[self redraw];
[CATransaction commit];
}
#elif TARGET_OS_MAC
- (void)layout
{
[super layout];
[CATransaction begin];
[CATransaction setDisableActions:YES];
self.waveformLayer.frame = self.bounds;
[self redraw];
[CATransaction commit];
}
#endif
- (void)initPlot
{
self.shouldCenterYAxis = YES;
self.shouldOptimizeForRealtimePlot = YES;
self.gain = 1.0;
self.plotType = EZPlotTypeBuffer;
self.shouldMirror = NO;
self.shouldFill = NO;
// Setup history window
[self resetHistoryBuffers];
self.waveformLayer = [EZAudioPlotWaveformLayer layer];
self.waveformLayer.frame = self.bounds;
self.waveformLayer.lineWidth = 1.0f;
self.waveformLayer.fillColor = nil;
self.waveformLayer.backgroundColor = nil;
self.waveformLayer.opaque = YES;
#if TARGET_OS_IPHONE
self.color = [UIColor colorWithHue:0 saturation:1.0 brightness:1.0 alpha:1.0];
#elif TARGET_OS_MAC
self.color = [NSColor colorWithCalibratedHue:0 saturation:1.0 brightness:1.0 alpha:1.0];
self.wantsLayer = YES;
self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawOnSetNeedsDisplay;
#endif
self.backgroundColor = nil;
[self.layer insertSublayer:self.waveformLayer atIndex:0];
self.points = calloc(EZAudioPlotDefaultMaxHistoryBufferLength, sizeof(CGPoint));
self.pointCount = [self initialPointCount];
[self redraw];
}
//------------------------------------------------------------------------------
#pragma mark - Setup
//------------------------------------------------------------------------------
- (void)resetHistoryBuffers
{
//
// Clear any existing data
//
if (self.historyInfo)
{
[EZAudioUtilities freeHistoryInfo:self.historyInfo];
}
self.historyInfo = [EZAudioUtilities historyInfoWithDefaultLength:[self defaultRollingHistoryLength]
maximumLength:[self maximumRollingHistoryLength]];
}
//------------------------------------------------------------------------------
#pragma mark - Setters
//------------------------------------------------------------------------------
- (void)setBackgroundColor:(id)backgroundColor
{
[super setBackgroundColor:backgroundColor];
self.layer.backgroundColor = [backgroundColor CGColor];
}
//------------------------------------------------------------------------------
- (void)setColor:(id)color
{
[super setColor:color];
self.waveformLayer.strokeColor = [color CGColor];
if (self.shouldFill)
{
self.waveformLayer.fillColor = [color CGColor];
}
}
//------------------------------------------------------------------------------
- (void)setShouldOptimizeForRealtimePlot:(BOOL)shouldOptimizeForRealtimePlot
{
_shouldOptimizeForRealtimePlot = shouldOptimizeForRealtimePlot;
if (shouldOptimizeForRealtimePlot && !self.displayLink)
{
self.displayLink = [EZAudioDisplayLink displayLinkWithDelegate:self];
[self.displayLink start];
}
else
{
[self.displayLink stop];
self.displayLink = nil;
}
}
//------------------------------------------------------------------------------
- (void)setShouldFill:(BOOL)shouldFill
{
[super setShouldFill:shouldFill];
self.waveformLayer.fillColor = shouldFill ? [self.color CGColor] : nil;
}
//------------------------------------------------------------------------------
#pragma mark - Drawing
//------------------------------------------------------------------------------
- (void)clear
{
if (self.pointCount > 0)
{
[self resetHistoryBuffers];
float data[self.pointCount];
memset(data, 0, self.pointCount * sizeof(float));
[self setSampleData:data length:self.pointCount];
[self redraw];
}
}
//------------------------------------------------------------------------------
- (void)redraw
{
EZRect frame = [self.waveformLayer frame];
CGPathRef path = [self createPathWithPoints:self.points
pointCount:self.pointCount
inRect:frame];
if (self.shouldOptimizeForRealtimePlot)
{
[CATransaction begin];
[CATransaction setDisableActions:YES];
self.waveformLayer.path = path;
[CATransaction commit];
}
else
{
self.waveformLayer.path = path;
}
CGPathRelease(path);
}
//------------------------------------------------------------------------------
- (CGPathRef)createPathWithPoints:(CGPoint *)points
pointCount:(UInt32)pointCount
inRect:(EZRect)rect
{
CGMutablePathRef path = NULL;
if (pointCount > 0)
{
path = CGPathCreateMutable();
double xscale = (rect.size.width) / ((float)self.pointCount);
double halfHeight = floor(rect.size.height / 2.0);
int deviceOriginFlipped = [self isDeviceOriginFlipped] ? -1 : 1;
CGAffineTransform xf = CGAffineTransformIdentity;
CGFloat translateY = 0.0f;
if (!self.shouldCenterYAxis)
{
#if TARGET_OS_IPHONE
translateY = CGRectGetHeight(rect);
#elif TARGET_OS_MAC
translateY = 0.0f;
#endif
}
else
{
translateY = halfHeight + rect.origin.y;
}
xf = CGAffineTransformTranslate(xf, 0.0, translateY);
double yScaleFactor = halfHeight;
if (!self.shouldCenterYAxis)
{
yScaleFactor = 2.0 * halfHeight;
}
xf = CGAffineTransformScale(xf, xscale, deviceOriginFlipped * yScaleFactor);
CGPathAddLines(path, &xf, self.points, self.pointCount);
if (self.shouldMirror)
{
xf = CGAffineTransformScale(xf, 1.0f, -1.0f);
CGPathAddLines(path, &xf, self.points, self.pointCount);
}
if (self.shouldFill)
{
CGPathCloseSubpath(path);
}
}
return path;
}
//------------------------------------------------------------------------------
#pragma mark - Update
//------------------------------------------------------------------------------
- (void)updateBuffer:(float *)buffer withBufferSize:(UInt32)bufferSize
{
// append the buffer to the history
[EZAudioUtilities appendBuffer:buffer
withBufferSize:bufferSize
toHistoryInfo:self.historyInfo];
// copy samples
switch (self.plotType)
{
case EZPlotTypeBuffer:
[self setSampleData:buffer
length:bufferSize];
break;
case EZPlotTypeRolling:
[self setSampleData:self.historyInfo->buffer
length:self.historyInfo->bufferSize];
break;
default:
break;
}
// update drawing
if (!self.shouldOptimizeForRealtimePlot)
{
[self redraw];
}
}
//------------------------------------------------------------------------------
- (void)setSampleData:(float *)data length:(int)length
{
CGPoint *points = self.points;
for (int i = 0; i < length; i++)
{
points[i].x = i;
points[i].y = data[i] * self.gain;
}
points[0].y = points[length - 1].y = 0.0f;
self.pointCount = length;
}
//------------------------------------------------------------------------------
#pragma mark - Adjusting History Resolution
//------------------------------------------------------------------------------
- (int)rollingHistoryLength
{
return self.historyInfo->bufferSize;
}
//------------------------------------------------------------------------------
- (int)setRollingHistoryLength:(int)historyLength
{
self.historyInfo->bufferSize = MIN(EZAudioPlotDefaultMaxHistoryBufferLength, historyLength);
return self.historyInfo->bufferSize;
}
//------------------------------------------------------------------------------
#pragma mark - Subclass
//------------------------------------------------------------------------------
- (int)defaultRollingHistoryLength
{
return EZAudioPlotDefaultHistoryBufferLength;
}
//------------------------------------------------------------------------------
- (int)initialPointCount
{
return 100;
}
//------------------------------------------------------------------------------
- (int)maximumRollingHistoryLength
{
return EZAudioPlotDefaultMaxHistoryBufferLength;
}
//------------------------------------------------------------------------------
#pragma mark - Utility
//------------------------------------------------------------------------------
- (BOOL)isDeviceOriginFlipped
{
BOOL isDeviceOriginFlipped = NO;
#if TARGET_OS_IPHONE
isDeviceOriginFlipped = YES;
#elif TARGET_OS_MAC
#endif
return isDeviceOriginFlipped;
}
//------------------------------------------------------------------------------
#pragma mark - EZAudioDisplayLinkDelegate
//------------------------------------------------------------------------------
- (void)displayLinkNeedsDisplay:(EZAudioDisplayLink *)displayLink
{
[self redraw];
}
//------------------------------------------------------------------------------
@end
////------------------------------------------------------------------------------
#pragma mark - EZAudioPlotWaveformLayer (Implementation)
////------------------------------------------------------------------------------
@implementation EZAudioPlotWaveformLayer
- (id<CAAction>)actionForKey:(NSString *)event
{
if ([event isEqualToString:@"path"])
{
if ([CATransaction disableActions])
{
return nil;
}
else
{
CABasicAnimation *animation = [CABasicAnimation animation];
animation.timingFunction = [CATransaction animationTimingFunction];
animation.duration = [CATransaction animationDuration];
return animation;
}
return nil;
}
return [super actionForKey:event];
}
@end
@@ -1,221 +0,0 @@
//
// EZAudioPlotGL.h
// EZAudio
//
// Created by Syed Haris Ali on 11/22/13.
// Copyright (c) 2015 Syed Haris Ali. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <GLKit/GLKit.h>
#import "EZPlot.h"
#if !TARGET_OS_IPHONE
#import <OpenGL/OpenGL.h>
#endif
//------------------------------------------------------------------------------
#pragma mark - Data Structures
//------------------------------------------------------------------------------
typedef struct
{
GLfloat x;
GLfloat y;
} EZAudioPlotGLPoint;
//------------------------------------------------------------------------------
#pragma mark - EZAudioPlotGL
//------------------------------------------------------------------------------
/**
EZAudioPlotGL is a subclass of either a GLKView on iOS or an NSOpenGLView on OSX. As of 0.6.0 this class no longer depends on an embedded GLKViewController for iOS as the display link is just manually managed within this single view instead. The EZAudioPlotGL provides the same kind of audio plot as the EZAudioPlot, but uses OpenGL to GPU-accelerate the drawing of the points, which means you can fit a lot more points and complex geometries.
*/
#if TARGET_OS_IPHONE
@interface EZAudioPlotGL : GLKView
#elif TARGET_OS_MAC
@interface EZAudioPlotGL : NSOpenGLView
#endif
//------------------------------------------------------------------------------
#pragma mark - Properties
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Customizing The Plot's Appearance
///-----------------------------------------------------------
/**
The default background color of the plot. For iOS the color is specified as a UIColor while for OSX the color is an NSColor. The default value on both platforms is a sweet looking green.
@warning On OSX, if you set the background to a value where the alpha component is 0 then the EZAudioPlotGL will automatically set its superview to be layer-backed.
*/
@property (nonatomic, strong) id backgroundColor;
//------------------------------------------------------------------------------
/**
The default color of the plot's data (i.e. waveform, y-axis values). For iOS the color is specified as a UIColor while for OSX the color is an NSColor. The default value on both platforms is white.
*/
@property (nonatomic, strong) id color;
//------------------------------------------------------------------------------
/**
The plot's gain value, which controls the scale of the y-axis values. The default value of the gain is 1.0f and should always be greater than 0.0f.
*/
@property (nonatomic, assign) float gain;
//------------------------------------------------------------------------------
/**
The type of plot as specified by the `EZPlotType` enumeration (i.e. a buffer or rolling plot type). Default is EZPlotTypeBuffer.
*/
@property (nonatomic, assign) EZPlotType plotType;
//------------------------------------------------------------------------------
/**
A BOOL indicating whether or not to fill in the graph. A value of YES will make a filled graph (filling in the space between the x-axis and the y-value), while a value of NO will create a stroked graph (connecting the points along the y-axis). Default is NO.
*/
@property (nonatomic, assign) BOOL shouldFill;
//------------------------------------------------------------------------------
/**
A boolean indicating whether the graph should be rotated along the x-axis to give a mirrored reflection. This is typical for audio plots to produce the classic waveform look. A value of YES will produce a mirrored reflection of the y-values about the x-axis, while a value of NO will only plot the y-values. Default is NO.
*/
@property (nonatomic, assign) BOOL shouldMirror;
//------------------------------------------------------------------------------
#pragma mark - Updating The Plot
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Updating The Plot
///-----------------------------------------------------------
/**
Updates the plot with the new buffer data and tells the view to redraw itself. Caller will provide a float array with the values they expect to see on the y-axis. The plot will internally handle mapping the x-axis and y-axis to the current view port, any interpolation for fills effects, and mirroring.
@param buffer A float array of values to map to the y-axis.
@param bufferSize The size of the float array that will be mapped to the y-axis.
*/
-(void)updateBuffer:(float *)buffer withBufferSize:(UInt32)bufferSize;
//------------------------------------------------------------------------------
#pragma mark - Adjust Resolution
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Adjusting The Resolution
///-----------------------------------------------------------
/**
Sets the length of the rolling history buffer (i.e. the number of points in the rolling plot's buffer). Can grow or shrink the display up to the maximum size specified by the `maximumRollingHistoryLength` method. Will return the actual set value, which will be either the given value if smaller than the `maximumRollingHistoryLength` or `maximumRollingHistoryLength` if a larger value is attempted to be set.
@param historyLength The new length of the rolling history buffer.
@return The new value equal to the historyLength or the `maximumRollingHistoryLength`.
*/
-(int)setRollingHistoryLength:(int)historyLength;
//------------------------------------------------------------------------------
/**
Provides the length of the rolling history buffer (i.e. the number of points in the rolling plot's buffer).
* @return An int representing the length of the rolling history buffer
*/
-(int)rollingHistoryLength;
//------------------------------------------------------------------------------
#pragma mark - Clearing The Plot
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Clearing The Plot
///-----------------------------------------------------------
/**
Clears all data from the audio plot (includes both EZPlotTypeBuffer and EZPlotTypeRolling)
*/
-(void)clear;
//------------------------------------------------------------------------------
#pragma mark - Start/Stop Display Link
//------------------------------------------------------------------------------
/**
Call this method to tell the EZAudioDisplayLink to stop drawing temporarily.
*/
- (void)pauseDrawing;
//------------------------------------------------------------------------------
/**
Call this method to manually tell the EZAudioDisplayLink to start drawing again.
*/
- (void)resumeDrawing;
//------------------------------------------------------------------------------
#pragma mark - Subclass
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Customizing The Drawing
///-----------------------------------------------------------
/**
This method is used to perform the actual OpenGL drawing code to clear the background and draw the lines representing the 2D audio plot. Subclasses can use the current implementation as an example and implement their own custom geometries. This is the analogy of overriding the drawRect: method in an NSView or UIView.
@param points An array of EZAudioPlotGLPoint structures representing the mapped audio data to x,y coordinates. The x-axis goes from 0 to the number of points (pointCount) while the y-axis goes from -1 to 1. Check out the implementation of this method to see how the model view matrix of the base effect is transformed to map this properly to the viewport.
@param pointCount A UInt32 representing the number of points contained in the points array.
@param baseEffect An optional GLKBaseEffect to use as a default shader. Call prepareToDraw on the base effect before any glDrawArrays call.
@param vbo The Vertex Buffer Object used to buffer the point data.
@param vab The Vertex Array Buffer used to bind the Vertex Buffer Object. This is a Mac only thing, you can ignore this completely on iOS.
@param interpolated A BOOL indicating whether the data has been interpolated. This means the point data is twice as long, where every other point is 0 on the y-axis to allow drawing triangle stripes for filled in waveforms. Typically if the point data is interpolated you will be using the GL_TRIANGLE_STRIP drawing mode, while non-interpolated plots will just use a GL_LINE_STRIP drawing mode.
@param mirrored A BOOL indicating whether the plot should be mirrored about the y-axis (or whatever geometry you come up with).
@param gain A float representing a gain that should be used to influence the height or intensity of your geometry's shape. A gain of 0.0 means silence, a gain of 1.0 means full volume (you're welcome to boost this to whatever you want).
*/
- (void)redrawWithPoints:(EZAudioPlotGLPoint *)points
pointCount:(UInt32)pointCount
baseEffect:(GLKBaseEffect *)baseEffect
vertexBufferObject:(GLuint)vbo
vertexArrayBuffer:(GLuint)vab
interpolated:(BOOL)interpolated
mirrored:(BOOL)mirrored
gain:(float)gain;
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Subclass Methods
///-----------------------------------------------------------
/**
Provides the default length of the rolling history buffer when the plot is initialized. Default is `EZAudioPlotDefaultHistoryBufferLength` constant.
@return An int describing the initial length of the rolling history buffer.
*/
- (int)defaultRollingHistoryLength;
//------------------------------------------------------------------------------
/**
Provides the default maximum rolling history length - that is, the maximum amount of points the `setRollingHistoryLength:` method may be set to. If a length higher than this is set then the plot will likely crash because the appropriate resources are only allocated once during the plot's initialization step. Defualt is `EZAudioPlotDefaultMaxHistoryBufferLength` constant.
@return An int describing the maximum length of the absolute rolling history buffer.
*/
- (int)maximumRollingHistoryLength;
//------------------------------------------------------------------------------
@end
@@ -1,533 +0,0 @@
//
// EZAudioPlotGL.m
// EZAudio
//
// Created by Syed Haris Ali on 11/22/13.
// Copyright (c) 2015 Syed Haris Ali. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "EZAudioPlotGL.h"
#import "EZAudioDisplayLink.h"
#import "EZAudioUtilities.h"
#import "EZAudioPlot.h"
//------------------------------------------------------------------------------
#pragma mark - Data Structures
//------------------------------------------------------------------------------
typedef struct
{
BOOL interpolated;
EZPlotHistoryInfo *historyInfo;
EZAudioPlotGLPoint *points;
UInt32 pointCount;
GLuint vbo;
GLuint vab;
} EZAudioPlotGLInfo;
//------------------------------------------------------------------------------
#pragma mark - EZAudioPlotGL (Interface Extension)
//------------------------------------------------------------------------------
@interface EZAudioPlotGL () <EZAudioDisplayLinkDelegate>
@property (nonatomic, strong) GLKBaseEffect *baseEffect;
@property (nonatomic, strong) EZAudioDisplayLink *displayLink;
@property (nonatomic, assign) EZAudioPlotGLInfo *info;
@end
//------------------------------------------------------------------------------
#pragma mark - EZAudioPlotGL (Implementation)
//------------------------------------------------------------------------------
@implementation EZAudioPlotGL
//------------------------------------------------------------------------------
#pragma mark - Dealloc
//------------------------------------------------------------------------------
- (void)dealloc
{
[self.displayLink stop];
self.displayLink = nil;
[EZAudioUtilities freeHistoryInfo:self.info->historyInfo];
#if !TARGET_OS_IPHONE
glDeleteVertexArrays(1, &self.info->vab);
#endif
glDeleteBuffers(1, &self.info->vbo);
free(self.info->points);
free(self.info);
self.baseEffect = nil;
}
//------------------------------------------------------------------------------
#pragma mark - Initialization
//------------------------------------------------------------------------------
- (instancetype)init
{
self = [super init];
if (self)
{
[self setup];
}
return self;
}
//------------------------------------------------------------------------------
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self)
{
[self setup];
}
return self;
}
//------------------------------------------------------------------------------
- (instancetype)initWithFrame:(EZRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
[self setup];
}
return self;
}
//------------------------------------------------------------------------------
#if TARGET_OS_IPHONE
- (instancetype)initWithFrame:(CGRect)frame
context:(EAGLContext *)context
{
self = [super initWithFrame:frame context:context];
if (self)
{
[self setup];
}
return self;
}
#elif TARGET_OS_MAC
- (instancetype)initWithFrame:(NSRect)frameRect
pixelFormat:(NSOpenGLPixelFormat *)format
{
self = [super initWithFrame:frameRect pixelFormat:format];
if (self)
{
[self setup];
}
return self;
}
#endif
//------------------------------------------------------------------------------
#pragma mark - Setup
//------------------------------------------------------------------------------
- (void)setup
{
//
// Setup info data structure
//
self.info = (EZAudioPlotGLInfo *)malloc(sizeof(EZAudioPlotGLInfo));
memset(self.info, 0, sizeof(EZAudioPlotGLInfo));
//
// Create points array
//
UInt32 pointCount = [self maximumRollingHistoryLength];
self.info->points = (EZAudioPlotGLPoint *)calloc(sizeof(EZAudioPlotGLPoint), pointCount);
self.info->pointCount = pointCount;
//
// Create the history data structure to hold the rolling data
//
self.info->historyInfo = [EZAudioUtilities historyInfoWithDefaultLength:[self defaultRollingHistoryLength]
maximumLength:[self maximumRollingHistoryLength]];
//
// Setup OpenGL specific stuff
//
[self setupOpenGL];
//
// Setup view properties
//
self.gain = 1.0f;
#if TARGET_OS_IPHONE
self.backgroundColor = [UIColor colorWithRed:0.569f green:0.82f blue:0.478f alpha:1.0f];
self.color = [UIColor colorWithRed:1.0f green:1.0f blue:1.0f alpha:1.0f];
#elif TARGET_OS_MAC
self.backgroundColor = [NSColor colorWithCalibratedRed:0.569f green:0.82f blue:0.478f alpha:1.0f];
self.color = [NSColor colorWithCalibratedRed:1.0f green:1.0f blue:1.0f alpha:1.0f];
#endif
//
// Create the display link
//
self.displayLink = [EZAudioDisplayLink displayLinkWithDelegate:self];
[self.displayLink start];
}
//------------------------------------------------------------------------------
- (void)setupOpenGL
{
self.baseEffect = [[GLKBaseEffect alloc] init];
self.baseEffect.useConstantColor = YES;
#if TARGET_OS_IPHONE
if (!self.context)
{
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
}
[EAGLContext setCurrentContext:self.context];
self.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
self.drawableDepthFormat = GLKViewDrawableDepthFormat24;
self.drawableStencilFormat = GLKViewDrawableStencilFormat8;
self.drawableMultisample = GLKViewDrawableMultisample4X;
self.opaque = NO;
self.enableSetNeedsDisplay = NO;
#elif TARGET_OS_MAC
self.wantsBestResolutionOpenGLSurface = YES;
self.wantsLayer = YES;
self.layer.opaque = YES;
self.layer.backgroundColor = [NSColor clearColor].CGColor;
if (!self.pixelFormat)
{
NSOpenGLPixelFormatAttribute attrs[] =
{
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAMultisample,
NSOpenGLPFASampleBuffers, 1,
NSOpenGLPFASamples, 4,
NSOpenGLPFADepthSize, 24,
NSOpenGLPFAOpenGLProfile,
NSOpenGLProfileVersion3_2Core, 0
};
self.pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
}
#if DEBUG
NSAssert(self.pixelFormat, @"Could not create OpenGL pixel format so context is not valid");
#endif
self.openGLContext = [[NSOpenGLContext alloc] initWithFormat:self.pixelFormat
shareContext:nil];
GLint swapInt = 1; GLint surfaceOpacity = 0;
[self.openGLContext setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
[self.openGLContext setValues:&surfaceOpacity forParameter:NSOpenGLCPSurfaceOpacity];
[self.openGLContext lock];
glGenVertexArrays(1, &self.info->vab);
glBindVertexArray(self.info->vab);
#endif
glGenBuffers(1, &self.info->vbo);
glBindBuffer(GL_ARRAY_BUFFER, self.info->vbo);
glBufferData(GL_ARRAY_BUFFER,
self.info->pointCount * sizeof(EZAudioPlotGLPoint),
self.info->points,
GL_STREAM_DRAW);
#if !TARGET_OS_IPHONE
[self.openGLContext unlock];
#endif
}
//------------------------------------------------------------------------------
#pragma mark - Updating The Plot
//------------------------------------------------------------------------------
- (void)updateBuffer:(float *)buffer withBufferSize:(UInt32)bufferSize
{
//
// Update history
//
[EZAudioUtilities appendBuffer:buffer
withBufferSize:bufferSize
toHistoryInfo:self.info->historyInfo];
//
// Convert this data to point data
//
switch (self.plotType)
{
case EZPlotTypeBuffer:
[self setSampleData:buffer
length:bufferSize];
break;
case EZPlotTypeRolling:
[self setSampleData:self.info->historyInfo->buffer
length:self.info->historyInfo->bufferSize];
break;
default:
break;
}
}
//------------------------------------------------------------------------------
- (void)setSampleData:(float *)data length:(int)length
{
int pointCount = self.shouldFill ? length * 2 : length;
EZAudioPlotGLPoint *points = self.info->points;
for (int i = 0; i < length; i++)
{
if (self.shouldFill)
{
points[i * 2].x = points[i * 2 + 1].x = i;
points[i * 2].y = data[i];
points[i * 2 + 1].y = 0.0f;
}
else
{
points[i].x = i;
points[i].y = data[i];
}
}
points[0].y = points[pointCount - 1].y = 0.0f;
self.info->pointCount = pointCount;
self.info->interpolated = self.shouldFill;
#if !TARGET_OS_IPHONE
[self.openGLContext lock];
glBindVertexArray(self.info->vab);
#endif
glBindBuffer(GL_ARRAY_BUFFER, self.info->vbo);
glBufferSubData(GL_ARRAY_BUFFER,
0,
pointCount * sizeof(EZAudioPlotGLPoint),
self.info->points);
#if !TARGET_OS_IPHONE
[self.openGLContext unlock];
#endif
}
//------------------------------------------------------------------------------
#pragma mark - Adjusting History Resolution
//------------------------------------------------------------------------------
- (int)rollingHistoryLength
{
return self.info->historyInfo->bufferSize;
}
//------------------------------------------------------------------------------
- (int)setRollingHistoryLength:(int)historyLength
{
self.info->historyInfo->bufferSize = MIN(EZAudioPlotDefaultMaxHistoryBufferLength, historyLength);
return self.info->historyInfo->bufferSize;
}
//------------------------------------------------------------------------------
#pragma mark - Clearing The Plot
//------------------------------------------------------------------------------
- (void)clear
{
float emptyBuffer[1];
emptyBuffer[0] = 0.0f;
[self setSampleData:emptyBuffer length:1];
[EZAudioUtilities clearHistoryInfo:self.info->historyInfo];
#if TARGET_OS_IPHONE
[self display];
#elif TARGET_OS_MAC
[self redraw];
#endif
}
//------------------------------------------------------------------------------
#pragma mark - Start/Stop Display Link
//------------------------------------------------------------------------------
- (void)pauseDrawing
{
[self.displayLink stop];
}
//------------------------------------------------------------------------------
- (void)resumeDrawing
{
[self.displayLink start];
}
//------------------------------------------------------------------------------
#pragma mark - Setters
//------------------------------------------------------------------------------
- (void)setBackgroundColor:(id)backgroundColor
{
_backgroundColor = backgroundColor;
if (backgroundColor)
{
CGColorRef colorRef = [backgroundColor CGColor];
CGFloat red; CGFloat green; CGFloat blue; CGFloat alpha;
[EZAudioUtilities getColorComponentsFromCGColor:colorRef
red:&red
green:&green
blue:&blue
alpha:&alpha];
//
// Note! If you set the alpha to be 0 on mac for a transparent view
// the EZAudioPlotGL will make the superview layer-backed to make
// sure there is a surface to display itself on (or else you will get
// some pretty weird drawing glitches
//
#if !TARGET_OS_IPHONE
if (alpha == 0.0f)
{
[self.superview setWantsLayer:YES];
}
#endif
glClearColor(red, green, blue, alpha);
}
else
{
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
}
}
//------------------------------------------------------------------------------
- (void)setColor:(id)color
{
_color = color;
if (color)
{
CGColorRef colorRef = [color CGColor];
CGFloat red; CGFloat green; CGFloat blue; CGFloat alpha;
[EZAudioUtilities getColorComponentsFromCGColor:colorRef
red:&red
green:&green
blue:&blue
alpha:&alpha];
self.baseEffect.constantColor = GLKVector4Make(red, green, blue, alpha);
}
else
{
self.baseEffect.constantColor = GLKVector4Make(0.0f, 0.0f, 0.0f, 0.0f);
}
}
//------------------------------------------------------------------------------
#pragma mark - Drawing
//------------------------------------------------------------------------------
- (void)drawRect:(EZRect)rect
{
[self redraw];
}
//------------------------------------------------------------------------------
- (void)redraw
{
#if !TARGET_OS_IPHONE
[self.openGLContext makeCurrentContext];
[self.openGLContext lock];
#endif
[self redrawWithPoints:self.info->points
pointCount:self.info->pointCount
baseEffect:self.baseEffect
vertexBufferObject:self.info->vbo
vertexArrayBuffer:self.info->vab
interpolated:self.info->interpolated
mirrored:self.shouldMirror
gain:self.gain];
#if !TARGET_OS_IPHONE
[self.openGLContext flushBuffer];
[self.openGLContext unlock];
#endif
}
//------------------------------------------------------------------------------
- (void)redrawWithPoints:(EZAudioPlotGLPoint *)points
pointCount:(UInt32)pointCount
baseEffect:(GLKBaseEffect *)baseEffect
vertexBufferObject:(GLuint)vbo
vertexArrayBuffer:(GLuint)vab
interpolated:(BOOL)interpolated
mirrored:(BOOL)mirrored
gain:(float)gain
{
glClear(GL_COLOR_BUFFER_BIT);
GLenum mode = interpolated ? GL_TRIANGLE_STRIP : GL_LINE_STRIP;
float interpolatedFactor = interpolated ? 2.0f : 1.0f;
float xscale = 2.0f / ((float)pointCount / interpolatedFactor);
float yscale = 1.0f * gain;
GLKMatrix4 transform = GLKMatrix4MakeTranslation(-1.0f, 0.0f, 0.0f);
transform = GLKMatrix4Scale(transform, xscale, yscale, 1.0f);
baseEffect.transform.modelviewMatrix = transform;
#if !TARGET_OS_IPHONE
glBindVertexArray(vab);
#endif
glBindBuffer(GL_ARRAY_BUFFER, vbo);
[baseEffect prepareToDraw];
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition,
2,
GL_FLOAT,
GL_FALSE,
sizeof(EZAudioPlotGLPoint),
NULL);
glDrawArrays(mode, 0, pointCount);
if (mirrored)
{
baseEffect.transform.modelviewMatrix = GLKMatrix4Rotate(transform, M_PI, 1.0f, 0.0f, 0.0f);
[baseEffect prepareToDraw];
glDrawArrays(mode, 0, pointCount);
}
}
//------------------------------------------------------------------------------
#pragma mark - Subclass
//------------------------------------------------------------------------------
- (int)defaultRollingHistoryLength
{
return EZAudioPlotDefaultHistoryBufferLength;
}
//------------------------------------------------------------------------------
- (int)maximumRollingHistoryLength
{
return EZAudioPlotDefaultMaxHistoryBufferLength;
}
//------------------------------------------------------------------------------
#pragma mark - EZAudioDisplayLinkDelegate
//------------------------------------------------------------------------------
- (void)displayLinkNeedsDisplay:(EZAudioDisplayLink *)displayLink
{
#if TARGET_OS_IPHONE
if ([[UIApplication sharedApplication] applicationState] == UIApplicationStateActive)
{
[self display];
}
#elif TARGET_OS_MAC
[self redraw];
#endif
}
//------------------------------------------------------------------------------
@end
@@ -1,75 +0,0 @@
//
// EZAudioFloatConverter.h
// EZAudio
//
// Created by Syed Haris Ali on 6/23/15.
// Copyright (c) 2015 Syed Haris Ali. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
//------------------------------------------------------------------------------
#pragma mark - Constants
//------------------------------------------------------------------------------
FOUNDATION_EXPORT UInt32 const EZAudioFloatConverterDefaultPacketSize;
//------------------------------------------------------------------------------
#pragma mark - EZAudioFloatConverter
//------------------------------------------------------------------------------
@interface EZAudioFloatConverter : NSObject
//------------------------------------------------------------------------------
#pragma mark - Class Methods
//------------------------------------------------------------------------------
+ (instancetype)converterWithInputFormat:(AudioStreamBasicDescription)inputFormat;
//------------------------------------------------------------------------------
#pragma mark - Properties
//------------------------------------------------------------------------------
@property (nonatomic, assign, readonly) AudioStreamBasicDescription inputFormat;
@property (nonatomic, assign, readonly) AudioStreamBasicDescription floatFormat;
//------------------------------------------------------------------------------
#pragma mark - Instance Methods
//------------------------------------------------------------------------------
- (instancetype)initWithInputFormat:(AudioStreamBasicDescription)inputFormat;
//------------------------------------------------------------------------------
- (void)convertDataFromAudioBufferList:(AudioBufferList *)audioBufferList
withNumberOfFrames:(UInt32)frames
toFloatBuffers:(float **)buffers;
//------------------------------------------------------------------------------
- (void)convertDataFromAudioBufferList:(AudioBufferList *)audioBufferList
withNumberOfFrames:(UInt32)frames
toFloatBuffers:(float **)buffers
packetDescriptions:(AudioStreamPacketDescription *)packetDescriptions;
//------------------------------------------------------------------------------
@end
@@ -1,224 +0,0 @@
//
// EZAudioFloatConverter.m
// EZAudio
//
// Created by Syed Haris Ali on 6/23/15.
// Copyright (c) 2015 Syed Haris Ali. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "EZAudioFloatConverter.h"
#import "EZAudioUtilities.h"
//------------------------------------------------------------------------------
#pragma mark - Constants
//------------------------------------------------------------------------------
static UInt32 EZAudioFloatConverterDefaultOutputBufferSize = 128 * 32;
UInt32 const EZAudioFloatConverterDefaultPacketSize = 2048;
//------------------------------------------------------------------------------
#pragma mark - Data Structures
//------------------------------------------------------------------------------
typedef struct
{
AudioConverterRef converterRef;
AudioBufferList *floatAudioBufferList;
AudioStreamBasicDescription inputFormat;
AudioStreamBasicDescription outputFormat;
AudioStreamPacketDescription *packetDescriptions;
UInt32 packetsPerBuffer;
} EZAudioFloatConverterInfo;
//------------------------------------------------------------------------------
#pragma mark - Callbacks
//------------------------------------------------------------------------------
OSStatus EZAudioFloatConverterCallback(AudioConverterRef inAudioConverter,
UInt32 *ioNumberDataPackets,
AudioBufferList *ioData,
AudioStreamPacketDescription **outDataPacketDescription,
void *inUserData)
{
AudioBufferList *sourceBuffer = (AudioBufferList *)inUserData;
memcpy(ioData,
sourceBuffer,
sizeof(AudioBufferList) + (sourceBuffer->mNumberBuffers - 1) * sizeof(AudioBuffer));
return noErr;
}
//------------------------------------------------------------------------------
#pragma mark - EZAudioFloatConverter (Interface Extension)
//------------------------------------------------------------------------------
@interface EZAudioFloatConverter ()
@property (nonatomic, assign) EZAudioFloatConverterInfo *info;
@end
//------------------------------------------------------------------------------
#pragma mark - EZAudioFloatConverter (Implementation)
//------------------------------------------------------------------------------
@implementation EZAudioFloatConverter
//------------------------------------------------------------------------------
#pragma mark - Class Methods
//------------------------------------------------------------------------------
+ (instancetype)converterWithInputFormat:(AudioStreamBasicDescription)inputFormat
{
return [[self alloc] initWithInputFormat:inputFormat];
}
//------------------------------------------------------------------------------
#pragma mark - Dealloc
//------------------------------------------------------------------------------
- (void)dealloc
{
AudioConverterDispose(self.info->converterRef);
[EZAudioUtilities freeBufferList:self.info->floatAudioBufferList];
free(self.info->packetDescriptions);
free(self.info);
}
//------------------------------------------------------------------------------
#pragma mark - Initialization
//------------------------------------------------------------------------------
- (instancetype)initWithInputFormat:(AudioStreamBasicDescription)inputFormat
{
self = [super init];
if (self)
{
self.info = (EZAudioFloatConverterInfo *)malloc(sizeof(EZAudioFloatConverterInfo));
memset(self.info, 0, sizeof(EZAudioFloatConverterInfo));
self.info->inputFormat = inputFormat;
[self setup];
}
return self;
}
//------------------------------------------------------------------------------
#pragma mark - Setup
//------------------------------------------------------------------------------
- (void)setup
{
// create output format
self.info->outputFormat = [EZAudioUtilities floatFormatWithNumberOfChannels:self.info->inputFormat.mChannelsPerFrame
sampleRate:self.info->inputFormat.mSampleRate];
// create a new instance of the audio converter
[EZAudioUtilities checkResult:AudioConverterNew(&self.info->inputFormat,
&self.info->outputFormat,
&self.info->converterRef)
operation:"Failed to create new audio converter"];
// get max packets per buffer so you can allocate a proper AudioBufferList
UInt32 packetsPerBuffer = 0;
UInt32 outputBufferSize = EZAudioFloatConverterDefaultOutputBufferSize;
UInt32 sizePerPacket = self.info->inputFormat.mBytesPerPacket;
BOOL isVBR = sizePerPacket == 0;
// VBR
if (isVBR)
{
// determine the max output buffer size
UInt32 maxOutputPacketSize;
UInt32 propSize = sizeof(maxOutputPacketSize);
OSStatus result = AudioConverterGetProperty(self.info->converterRef,
kAudioConverterPropertyMaximumOutputPacketSize,
&propSize,
&maxOutputPacketSize);
if (result != noErr)
{
maxOutputPacketSize = EZAudioFloatConverterDefaultPacketSize;
}
// set the output buffer size to at least the max output size
if (maxOutputPacketSize > outputBufferSize)
{
outputBufferSize = maxOutputPacketSize;
}
packetsPerBuffer = outputBufferSize / maxOutputPacketSize;
// allocate memory for the packet descriptions
self.info->packetDescriptions = (AudioStreamPacketDescription *)malloc(sizeof(AudioStreamPacketDescription) * packetsPerBuffer);
}
else
{
packetsPerBuffer = outputBufferSize / sizePerPacket;
}
self.info->packetsPerBuffer = packetsPerBuffer;
// allocate the AudioBufferList to hold the float values
BOOL isInterleaved = [EZAudioUtilities isInterleaved:self.info->outputFormat];
self.info->floatAudioBufferList = [EZAudioUtilities audioBufferListWithNumberOfFrames:packetsPerBuffer
numberOfChannels:self.info->outputFormat.mChannelsPerFrame
interleaved:isInterleaved];
}
//------------------------------------------------------------------------------
#pragma mark - Events
//------------------------------------------------------------------------------
- (void)convertDataFromAudioBufferList:(AudioBufferList *)audioBufferList
withNumberOfFrames:(UInt32)frames
toFloatBuffers:(float **)buffers
{
[self convertDataFromAudioBufferList:audioBufferList
withNumberOfFrames:frames
toFloatBuffers:buffers
packetDescriptions:self.info->packetDescriptions];
}
//------------------------------------------------------------------------------
- (void)convertDataFromAudioBufferList:(AudioBufferList *)audioBufferList
withNumberOfFrames:(UInt32)frames
toFloatBuffers:(float **)buffers
packetDescriptions:(AudioStreamPacketDescription *)packetDescriptions
{
if (frames == 0)
{
}
else
{
[EZAudioUtilities checkResult:AudioConverterFillComplexBuffer(self.info->converterRef,
EZAudioFloatConverterCallback,
audioBufferList,
&frames,
self.info->floatAudioBufferList,
packetDescriptions ? packetDescriptions : self.info->packetDescriptions)
operation:"Failed to fill complex buffer in float converter"];
for (int i = 0; i < self.info->floatAudioBufferList->mNumberBuffers; i++)
{
memcpy(buffers[i],
self.info->floatAudioBufferList->mBuffers[i].mData,
self.info->floatAudioBufferList->mBuffers[i].mDataByteSize);
}
}
}
//------------------------------------------------------------------------------
@end
@@ -1,52 +0,0 @@
//
// EZAudioFloatData.h
// EZAudio
//
// Created by Syed Haris Ali on 6/23/15.
// Copyright (c) 2015 Syed Haris Ali. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
//------------------------------------------------------------------------------
#pragma mark - EZAudioFloatData
//------------------------------------------------------------------------------
@interface EZAudioFloatData : 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
@@ -1,85 +0,0 @@
//
// EZAudioFloatData.m
// EZAudio
//
// Created by Syed Haris Ali on 6/23/15.
// Copyright (c) 2015 Syed Haris Ali. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "EZAudioFloatData.h"
#import "EZAudioUtilities.h"
//------------------------------------------------------------------------------
#pragma mark - EZAudioFloatData
//------------------------------------------------------------------------------
@interface EZAudioFloatData ()
@property (nonatomic, assign, readwrite) int numberOfChannels;
@property (nonatomic, assign, readwrite) float **buffers;
@property (nonatomic, assign, readwrite) UInt32 bufferSize;
@end
//------------------------------------------------------------------------------
@implementation EZAudioFloatData
//------------------------------------------------------------------------------
- (void)dealloc
{
[EZAudioUtilities freeFloatBuffers:self.buffers
numberOfChannels:self.numberOfChannels];
}
//------------------------------------------------------------------------------
+ (instancetype)dataWithNumberOfChannels:(int)numberOfChannels
buffers:(float **)buffers
bufferSize:(UInt32)bufferSize
{
id data = [[self alloc] init];
size_t size = sizeof(float) * bufferSize;
float **buffersCopy = [EZAudioUtilities floatBuffersWithNumberOfFrames:bufferSize
numberOfChannels:numberOfChannels];
for (int i = 0; i < numberOfChannels; i++)
{
memcpy(buffersCopy[i], buffers[i], size);
}
((EZAudioFloatData *)data).buffers = buffersCopy;
((EZAudioFloatData *)data).bufferSize = bufferSize;
((EZAudioFloatData *)data).numberOfChannels = numberOfChannels;
return data;
}
//------------------------------------------------------------------------------
- (float *)bufferForChannel:(int)channel
{
float *buffer = NULL;
if (channel < self.numberOfChannels)
{
buffer = self.buffers[channel];
}
return buffer;
}
//------------------------------------------------------------------------------
@end
@@ -1,532 +0,0 @@
//
// EZAudioUtilities.h
// EZAudio
//
// Created by Syed Haris Ali on 6/23/15.
// Copyright (c) 2015 Syed Haris Ali. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
#import <TargetConditionals.h>
#import "TPCircularBuffer.h"
#if TARGET_OS_IPHONE
#import <AVFoundation/AVFoundation.h>
#import <UIKit/UIKit.h>
#elif TARGET_OS_MAC
#import <AppKit/AppKit.h>
#endif
//------------------------------------------------------------------------------
#pragma mark - Data Structures
//------------------------------------------------------------------------------
/**
A data structure that holds information about audio data over time. It contains a circular buffer to incrementally write the audio data to and a scratch buffer to hold a window of audio data relative to the whole circular buffer. In use, this will provide a way to continuously append data while having an adjustable viewable window described by the bufferSize.
*/
typedef struct
{
float *buffer;
int bufferSize;
TPCircularBuffer circularBuffer;
} EZPlotHistoryInfo;
//------------------------------------------------------------------------------
/**
A data structure that holds information about a node in the context of an AUGraph.
*/
typedef struct
{
AudioUnit audioUnit;
AUNode node;
} EZAudioNodeInfo;
//------------------------------------------------------------------------------
#pragma mark - Types
//------------------------------------------------------------------------------
#if TARGET_OS_IPHONE
typedef CGRect EZRect;
#elif TARGET_OS_MAC
typedef NSRect EZRect;
#endif
//------------------------------------------------------------------------------
#pragma mark - EZAudioUtilities
//------------------------------------------------------------------------------
/**
The EZAudioUtilities class provides a set of class-level utility methods used throughout EZAudio to handle common operations such as allocating audio buffers and structures, creating various types of AudioStreamBasicDescription structures, string helpers for formatting and debugging, various math utilities, a very handy check result function (used everywhere!), and helpers for dealing with circular buffers. These were previously on the EZAudio class, but as of the 0.1.0 release have been moved here so the whole EZAudio is not needed when using only certain modules.
*/
@interface EZAudioUtilities : NSObject
//------------------------------------------------------------------------------
#pragma mark - Debugging
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Debugging EZAudio
///-----------------------------------------------------------
/**
Globally sets whether or not the program should exit if a `checkResult:operation:` operation fails. Currently the behavior on EZAudio is to quit if a `checkResult:operation:` fails, but this is not desirable in any production environment. Internally there are a lot of `checkResult:operation:` operations used on all the core classes. This should only ever be set to NO in production environments since a `checkResult:operation:` failing means something breaking has likely happened.
@param shouldExitOnCheckResultFail A BOOL indicating whether or not the running program should exist due to a `checkResult:operation:` fail.
*/
+ (void)setShouldExitOnCheckResultFail:(BOOL)shouldExitOnCheckResultFail;
//------------------------------------------------------------------------------
/**
Provides a flag indicating whether or not the program will exit if a `checkResult:operation:` fails.
@return A BOOL indicating whether or not the program will exit if a `checkResult:operation:` fails.
*/
+ (BOOL)shouldExitOnCheckResultFail;
//------------------------------------------------------------------------------
#pragma mark - AudioBufferList Utility
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name AudioBufferList Utility
///-----------------------------------------------------------
/**
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 *)audioBufferListWithNumberOfFrames:(UInt32)frames
numberOfChannels:(UInt32)channels
interleaved:(BOOL)interleaved;
//------------------------------------------------------------------------------
/**
Allocates an array of float arrays given the number of frames needed to store in each float array.
@param frames A UInt32 representing the number of frames to store in each float buffer
@param channels A UInt32 representing the number of channels (i.e. the number of float arrays to allocate)
@return An array of float arrays, each the length of the number of frames specified
*/
+ (float **)floatBuffersWithNumberOfFrames:(UInt32)frames
numberOfChannels:(UInt32)channels;
//------------------------------------------------------------------------------
/**
Deallocates an AudioBufferList structure from memory.
@param bufferList A pointer to the buffer list you would like to free
*/
+ (void)freeBufferList:(AudioBufferList *)bufferList;
//------------------------------------------------------------------------------
/**
Deallocates an array of float buffers
@param buffers An array of float arrays
@param channels A UInt32 representing the number of channels (i.e. the number of float arrays to deallocate)
*/
+ (void)freeFloatBuffers:(float **)buffers numberOfChannels:(UInt32)channels;
//------------------------------------------------------------------------------
#pragma mark - AudioStreamBasicDescription Utilties
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Creating An AudioStreamBasicDescription
///-----------------------------------------------------------
/**
Creates a signed-integer, interleaved AudioStreamBasicDescription for the number of channels specified for an AIFF format.
@param channels The desired number of channels
@param sampleRate A float representing the sample rate.
@return A new AudioStreamBasicDescription with the specified format.
*/
+ (AudioStreamBasicDescription)AIFFFormatWithNumberOfChannels:(UInt32)channels
sampleRate:(float)sampleRate;
//------------------------------------------------------------------------------
/**
Creates an AudioStreamBasicDescription for the iLBC narrow band speech codec.
@param sampleRate A float representing the sample rate.
@return A new AudioStreamBasicDescription with the specified format.
*/
+ (AudioStreamBasicDescription)iLBCFormatWithSampleRate:(float)sampleRate;
//------------------------------------------------------------------------------
/**
Creates a float-based, non-interleaved AudioStreamBasicDescription for the number of channels specified.
@param channels A UInt32 representing the number of channels.
@param sampleRate A float representing the sample rate.
@return A float-based AudioStreamBasicDescription with the number of channels specified.
*/
+ (AudioStreamBasicDescription)floatFormatWithNumberOfChannels:(UInt32)channels
sampleRate:(float)sampleRate;
//------------------------------------------------------------------------------
/**
Creates an AudioStreamBasicDescription for an M4A AAC format.
@param channels The desired number of channels
@param sampleRate A float representing the sample rate.
@return A new AudioStreamBasicDescription with the specified format.
*/
+ (AudioStreamBasicDescription)M4AFormatWithNumberOfChannels:(UInt32)channels
sampleRate:(float)sampleRate;
//------------------------------------------------------------------------------
/**
Creates a single-channel, float-based AudioStreamBasicDescription.
@param sampleRate A float representing the sample rate.
@return A new AudioStreamBasicDescription with the specified format.
*/
+ (AudioStreamBasicDescription)monoFloatFormatWithSampleRate:(float)sampleRate;
//------------------------------------------------------------------------------
/**
Creates a single-channel, float-based AudioStreamBasicDescription (as of 0.0.6 this is the same as `monoFloatFormatWithSampleRate:`).
@param sampleRate A float representing the sample rate.
@return A new AudioStreamBasicDescription with the specified format.
*/
+ (AudioStreamBasicDescription)monoCanonicalFormatWithSampleRate:(float)sampleRate;
//------------------------------------------------------------------------------
/**
Creates a two-channel, non-interleaved, float-based AudioStreamBasicDescription (as of 0.0.6 this is the same as `stereoFloatNonInterleavedFormatWithSampleRate:`).
@param sampleRate A float representing the sample rate.
@return A new AudioStreamBasicDescription with the specified format.
*/
+ (AudioStreamBasicDescription)stereoCanonicalNonInterleavedFormatWithSampleRate:(float)sampleRate;
//------------------------------------------------------------------------------
/**
Creates a two-channel, interleaved, float-based AudioStreamBasicDescription.
@param sampleRate A float representing the sample rate.
@return A new AudioStreamBasicDescription with the specified format.
*/
+ (AudioStreamBasicDescription)stereoFloatInterleavedFormatWithSampleRate:(float)sampleRate;
//------------------------------------------------------------------------------
/**
Creates a two-channel, non-interleaved, float-based AudioStreamBasicDescription.
@param sampleRate A float representing the sample rate.
@return A new AudioStreamBasicDescription with the specified format.
*/
+ (AudioStreamBasicDescription)stereoFloatNonInterleavedFormatWithSampleRate:(float)sameRate;
//------------------------------------------------------------------------------
// @name AudioStreamBasicDescription Helper Functions
//------------------------------------------------------------------------------
/**
Checks an AudioStreamBasicDescription to see if it is a float-based format (as opposed to a signed integer based format).
@param asbd A valid AudioStreamBasicDescription
@return A BOOL indicating whether or not the AudioStreamBasicDescription is a float format.
*/
+ (BOOL)isFloatFormat:(AudioStreamBasicDescription)asbd;
//------------------------------------------------------------------------------
/**
Checks an AudioStreamBasicDescription to check for an interleaved flag (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 a linear PCM format (uncompressed,
1 frame per packet)
@param asbd A valid AudioStreamBasicDescription
@return A BOOL indicating whether or not the AudioStreamBasicDescription is linear PCM.
*/
+ (BOOL)isLinearPCM:(AudioStreamBasicDescription)asbd;
///-----------------------------------------------------------
/// @name AudioStreamBasicDescription Utilities
///-----------------------------------------------------------
/**
Nicely logs out the contents of an AudioStreamBasicDescription struct
@param asbd The AudioStreamBasicDescription struct with content to print out
*/
+ (void)printASBD:(AudioStreamBasicDescription)asbd;
//------------------------------------------------------------------------------
/**
Converts seconds into a string formatted as MM:SS
@param seconds An NSTimeInterval representing the number of seconds
@return An NSString instance formatted as MM:SS from the seconds provided.
*/
+ (NSString *)displayTimeStringFromSeconds:(NSTimeInterval)seconds;
//------------------------------------------------------------------------------
/**
Creates a string to use when logging out the contents of an AudioStreamBasicDescription
@param asbd A valid AudioStreamBasicDescription struct.
@return An NSString representing the contents of the AudioStreamBasicDescription.
*/
+ (NSString *)stringForAudioStreamBasicDescription:(AudioStreamBasicDescription)asbd;
//------------------------------------------------------------------------------
/**
Just a wrapper around the setCanonical function provided in the Core Audio Utility C++ class.
@param asbd The AudioStreamBasicDescription structure to modify
@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
numberOfChannels:(UInt32)nChannels
interleaved:(BOOL)interleaved;
//------------------------------------------------------------------------------
#pragma mark - Math Utilities
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Math Utilities
///-----------------------------------------------------------
/**
Appends an array of values to a history buffer and performs an internal shift to add the values to the tail and removes the same number of values from the head.
@param buffer A float array of values to append to the tail of the history buffer
@param bufferLength The length of the float array being appended to the history buffer
@param scrollHistory The target history buffer in which to append the values
@param scrollHistoryLength The length of the target history buffer
*/
+ (void)appendBufferAndShift:(float*)buffer
withBufferSize:(int)bufferLength
toScrollHistory:(float*)scrollHistory
withScrollHistorySize:(int)scrollHistoryLength;
//------------------------------------------------------------------------------
/**
Appends a value to a history buffer and performs an internal shift to add the value to the tail and remove the 0th value.
@param value The float value to append to the history array
@param scrollHistory The target history buffer in which to append the values
@param scrollHistoryLength The length of the target history buffer
*/
+(void) appendValue:(float)value
toScrollHistory:(float*)scrollHistory
withScrollHistorySize:(int)scrollHistoryLength;
//------------------------------------------------------------------------------
/**
Maps a value from one coordinate system into another one. Takes in the current value to map, the minimum and maximum values of the first coordinate system, and the minimum and maximum values of the second coordinate system and calculates the mapped value in the second coordinate system's constraints.
@param value The value expressed in the first coordinate system
@param leftMin The minimum of the first coordinate system
@param leftMax The maximum of the first coordinate system
@param rightMin The minimum of the second coordindate system
@param rightMax The maximum of the second coordinate system
@return The mapped value in terms of the second coordinate system
*/
+ (float)MAP:(float)value
leftMin:(float)leftMin
leftMax:(float)leftMax
rightMin:(float)rightMin
rightMax:(float)rightMax;
//------------------------------------------------------------------------------
/**
Calculates the root mean squared for a buffer.
@param buffer A float buffer array of values whose root mean squared to calculate
@param bufferSize The size of the float buffer
@return The root mean squared of the buffer
*/
+ (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
///-----------------------------------------------------------
/**
Basic check result function useful for checking each step of the audio setup process
@param result The OSStatus representing the result of an operation
@param operation A string (const char, not NSString) describing the operation taking place (will print if fails)
*/
+ (void)checkResult:(OSStatus)result operation:(const char *)operation;
//------------------------------------------------------------------------------
/**
Provides a string representation of the often cryptic Core Audio error codes
@param code A UInt32 representing an error code
@return An NSString with a human readable version of the error code.
*/
+ (NSString *)stringFromUInt32Code:(UInt32)code;
//------------------------------------------------------------------------------
#pragma mark - Color Utility
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Color Utility
///-----------------------------------------------------------
/**
Helper function to get the color components from a CGColorRef in the RGBA colorspace.
@param color A CGColorRef that represents a color.
@param red A pointer to a CGFloat to hold the value of the red component. This value will be between 0 and 1.
@param green A pointer to a CGFloat to hold the value of the green component. This value will be between 0 and 1.
@param blue A pointer to a CGFloat to hold the value of the blue component. This value will be between 0 and 1.
@param alpha A pointer to a CGFloat to hold the value of the alpha component. This value will be between 0 and 1.
*/
+ (void)getColorComponentsFromCGColor:(CGColorRef)color
red:(CGFloat *)red
green:(CGFloat *)green
blue:(CGFloat *)blue
alpha:(CGFloat *)alpha;
//------------------------------------------------------------------------------
#pragma mark - Plot Utility
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Plot Utility
///-----------------------------------------------------------
/**
Given a buffer representing a window of float history data this append the RMS of a buffer of incoming float data...This will likely be deprecated in a future version of EZAudio for a circular buffer based approach.
@param scrollHistory An array of float arrays being used to hold the history values for each channel.
@param scrollHistoryLength An int representing the length of the history window.
@param index An int pointer to the index of the current read index of the history buffer.
@param buffer A float array representing the incoming audio data.
@param bufferSize An int representing the length of the incoming audio data.
@param isChanging A BOOL pointer representing whether the resolution (length of the history window) is currently changing.
*/
+ (void)updateScrollHistory:(float **)scrollHistory
withLength:(int)scrollHistoryLength
atIndex:(int *)index
withBuffer:(float *)buffer
withBufferSize:(int)bufferSize
isResolutionChanging:(BOOL *)isChanging;
//------------------------------------------------------------------------------
#pragma mark - TPCircularBuffer Utility
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name TPCircularBuffer Utility
///-----------------------------------------------------------
/**
Appends the data from the audio buffer list to the circular buffer
@param circularBuffer Pointer to the instance of the TPCircularBuffer to add the audio data to
@param audioBufferList Pointer to the instance of the AudioBufferList with the audio data
*/
+ (void)appendDataToCircularBuffer:(TPCircularBuffer*)circularBuffer
fromAudioBufferList:(AudioBufferList*)audioBufferList;
//------------------------------------------------------------------------------
/**
Initializes the circular buffer (just a wrapper around the C method)
* @param circularBuffer Pointer to an instance of the TPCircularBuffer
* @param size The length of the TPCircularBuffer (usually 1024)
*/
+ (void)circularBuffer:(TPCircularBuffer*)circularBuffer
withSize:(int)size;
//------------------------------------------------------------------------------
/**
Frees a circular buffer
@param circularBuffer Pointer to the circular buffer to clear
*/
+ (void)freeCircularBuffer:(TPCircularBuffer*)circularBuffer;
//------------------------------------------------------------------------------
#pragma mark - EZPlotHistoryInfo Utility
//------------------------------------------------------------------------------
/**
Calculates the RMS of a float array containing audio data and appends it to the tail of a EZPlotHistoryInfo data structure. Thread-safe.
@param buffer A float array containing the incoming audio buffer to append to the history buffer
@param bufferSize A UInt32 representing the length of the incoming audio buffer
@param historyInfo A pointer to a EZPlotHistoryInfo structure to use for managing the history buffers
*/
+ (void)appendBuffer:(float *)buffer
withBufferSize:(UInt32)bufferSize
toHistoryInfo:(EZPlotHistoryInfo *)historyInfo;
//------------------------------------------------------------------------------
/**
Zeroes out a EZPlotHistoryInfo data structure without freeing the resources.
@param historyInfo A pointer to a EZPlotHistoryInfo data structure
*/
+ (void)clearHistoryInfo:(EZPlotHistoryInfo *)historyInfo;
//------------------------------------------------------------------------------
/**
Frees a EZPlotHistoryInfo data structure
@param historyInfo A pointer to a EZPlotHistoryInfo data structure
*/
+ (void)freeHistoryInfo:(EZPlotHistoryInfo *)historyInfo;
//------------------------------------------------------------------------------
/**
Creates an EZPlotHistoryInfo data structure with a default length for the window buffer and a maximum length capacity for the internal circular buffer that holds all the audio data.
@param defaultLength An int representing the default length (i.e. the number of points that will be displayed on screen) of the history window.
@param maximumLength An int representing the default maximum length that is the absolute maximum amount of values that can be held in the history's circular buffer.
@return A pointer to the EZPlotHistoryInfo created. The caller is responsible for freeing this structure using the `freeHistoryInfo` method above.
*/
+ (EZPlotHistoryInfo *)historyInfoWithDefaultLength:(int)defaultLength
maximumLength:(int)maximumLength;
//------------------------------------------------------------------------------
@end
@@ -1,667 +0,0 @@
//
// EZAudioUtilities.m
// EZAudio
//
// Created by Syed Haris Ali on 6/23/15.
// Copyright (c) 2015 Syed Haris Ali. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "EZAudioUtilities.h"
BOOL __shouldExitOnCheckResultFail = YES;
@implementation EZAudioUtilities
//------------------------------------------------------------------------------
#pragma mark - Debugging
//------------------------------------------------------------------------------
+ (void)setShouldExitOnCheckResultFail:(BOOL)shouldExitOnCheckResultFail
{
__shouldExitOnCheckResultFail = shouldExitOnCheckResultFail;
}
//------------------------------------------------------------------------------
+ (BOOL)shouldExitOnCheckResultFail
{
return __shouldExitOnCheckResultFail;
}
//------------------------------------------------------------------------------
#pragma mark - AudioBufferList Utility
//------------------------------------------------------------------------------
+ (AudioBufferList *)audioBufferListWithNumberOfFrames:(UInt32)frames
numberOfChannels:(UInt32)channels
interleaved:(BOOL)interleaved
{
AudioBufferList *audioBufferList = (AudioBufferList*)malloc(sizeof(AudioBufferList) + sizeof(AudioBuffer) * (channels-1));
UInt32 outputBufferSize = 32 * frames; // 32 KB
audioBufferList->mNumberBuffers = interleaved ? 1 : channels;
for(int i = 0; i < audioBufferList->mNumberBuffers; i++)
{
audioBufferList->mBuffers[i].mNumberChannels = channels;
audioBufferList->mBuffers[i].mDataByteSize = channels * outputBufferSize;
audioBufferList->mBuffers[i].mData = (float *)malloc(channels * sizeof(float) *outputBufferSize);
memset(audioBufferList->mBuffers[i].mData, 0, frames);
}
return audioBufferList;
}
//------------------------------------------------------------------------------
+ (float **)floatBuffersWithNumberOfFrames:(UInt32)frames
numberOfChannels:(UInt32)channels
{
size_t size = sizeof(float *) * channels;
float **buffers = (float **)malloc(size);
for (int i = 0; i < channels; i++)
{
size = sizeof(float) * frames;
buffers[i] = (float *)malloc(size);
}
return buffers;
}
//------------------------------------------------------------------------------
+ (void)freeBufferList:(AudioBufferList *)bufferList
{
if (bufferList)
{
if (bufferList->mNumberBuffers)
{
for( int i = 0; i < bufferList->mNumberBuffers; i++)
{
if (bufferList->mBuffers[i].mData)
{
free(bufferList->mBuffers[i].mData);
}
}
}
free(bufferList);
}
bufferList = NULL;
}
//------------------------------------------------------------------------------
+ (void)freeFloatBuffers:(float **)buffers numberOfChannels:(UInt32)channels
{
for (int i = 0; i < channels; i++)
{
free(buffers[i]);
}
free(buffers);
}
//------------------------------------------------------------------------------
#pragma mark - AudioStreamBasicDescription Utility
//------------------------------------------------------------------------------
+ (AudioStreamBasicDescription)AIFFFormatWithNumberOfChannels:(UInt32)channels
sampleRate:(float)sampleRate
{
AudioStreamBasicDescription asbd;
memset(&asbd, 0, sizeof(asbd));
asbd.mFormatID = kAudioFormatLinearPCM;
asbd.mFormatFlags = kAudioFormatFlagIsBigEndian|kAudioFormatFlagIsPacked|kAudioFormatFlagIsSignedInteger;
asbd.mSampleRate = sampleRate;
asbd.mChannelsPerFrame = channels;
asbd.mBitsPerChannel = 32;
asbd.mBytesPerPacket = (asbd.mBitsPerChannel / 8) * asbd.mChannelsPerFrame;
asbd.mFramesPerPacket = 1;
asbd.mBytesPerFrame = (asbd.mBitsPerChannel / 8) * asbd.mChannelsPerFrame;
return asbd;
}
//------------------------------------------------------------------------------
+ (AudioStreamBasicDescription)iLBCFormatWithSampleRate:(float)sampleRate
{
AudioStreamBasicDescription asbd;
memset(&asbd, 0, sizeof(asbd));
asbd.mFormatID = kAudioFormatiLBC;
asbd.mChannelsPerFrame = 1;
asbd.mSampleRate = sampleRate;
// Fill in the rest of the descriptions using the Audio Format API
UInt32 propSize = sizeof(asbd);
[EZAudioUtilities checkResult:AudioFormatGetProperty(kAudioFormatProperty_FormatInfo,
0,
NULL,
&propSize,
&asbd)
operation:"Failed to fill out the rest of the m4a AudioStreamBasicDescription"];
return asbd;
}
//------------------------------------------------------------------------------
+ (AudioStreamBasicDescription)floatFormatWithNumberOfChannels:(UInt32)channels
sampleRate:(float)sampleRate
{
AudioStreamBasicDescription asbd;
UInt32 floatByteSize = sizeof(float);
asbd.mBitsPerChannel = 8 * floatByteSize;
asbd.mBytesPerFrame = floatByteSize;
asbd.mBytesPerPacket = floatByteSize;
asbd.mChannelsPerFrame = channels;
asbd.mFormatFlags = kAudioFormatFlagIsFloat|kAudioFormatFlagIsNonInterleaved;
asbd.mFormatID = kAudioFormatLinearPCM;
asbd.mFramesPerPacket = 1;
asbd.mSampleRate = sampleRate;
return asbd;
}
//------------------------------------------------------------------------------
+ (AudioStreamBasicDescription)M4AFormatWithNumberOfChannels:(UInt32)channels
sampleRate:(float)sampleRate
{
AudioStreamBasicDescription asbd;
memset(&asbd, 0, sizeof(asbd));
asbd.mFormatID = kAudioFormatMPEG4AAC;
asbd.mChannelsPerFrame = channels;
asbd.mSampleRate = sampleRate;
// Fill in the rest of the descriptions using the Audio Format API
UInt32 propSize = sizeof(asbd);
[EZAudioUtilities checkResult:AudioFormatGetProperty(kAudioFormatProperty_FormatInfo,
0,
NULL,
&propSize,
&asbd)
operation:"Failed to fill out the rest of the m4a AudioStreamBasicDescription"];
return asbd;
}
//------------------------------------------------------------------------------
+ (AudioStreamBasicDescription)monoFloatFormatWithSampleRate:(float)sampleRate
{
AudioStreamBasicDescription asbd;
UInt32 byteSize = sizeof(float);
asbd.mBitsPerChannel = 8 * byteSize;
asbd.mBytesPerFrame = byteSize;
asbd.mBytesPerPacket = byteSize;
asbd.mChannelsPerFrame = 1;
asbd.mFormatFlags = kAudioFormatFlagIsPacked|kAudioFormatFlagIsFloat;
asbd.mFormatID = kAudioFormatLinearPCM;
asbd.mFramesPerPacket = 1;
asbd.mSampleRate = sampleRate;
return asbd;
}
//------------------------------------------------------------------------------
+ (AudioStreamBasicDescription)monoCanonicalFormatWithSampleRate:(float)sampleRate
{
AudioStreamBasicDescription asbd;
UInt32 byteSize = sizeof(float);
asbd.mBitsPerChannel = 8 * byteSize;
asbd.mBytesPerFrame = byteSize;
asbd.mBytesPerPacket = byteSize;
asbd.mChannelsPerFrame = 1;
asbd.mFormatFlags = kAudioFormatFlagsNativeFloatPacked|kAudioFormatFlagIsNonInterleaved;
asbd.mFormatID = kAudioFormatLinearPCM;
asbd.mFramesPerPacket = 1;
asbd.mSampleRate = sampleRate;
return asbd;
}
//------------------------------------------------------------------------------
+ (AudioStreamBasicDescription)stereoCanonicalNonInterleavedFormatWithSampleRate:(float)sampleRate
{
AudioStreamBasicDescription asbd;
UInt32 byteSize = sizeof(float);
asbd.mBitsPerChannel = 8 * byteSize;
asbd.mBytesPerFrame = byteSize;
asbd.mBytesPerPacket = byteSize;
asbd.mChannelsPerFrame = 2;
asbd.mFormatFlags = kAudioFormatFlagsNativeFloatPacked|kAudioFormatFlagIsNonInterleaved;
asbd.mFormatID = kAudioFormatLinearPCM;
asbd.mFramesPerPacket = 1;
asbd.mSampleRate = sampleRate;
return asbd;
}
//------------------------------------------------------------------------------
+ (AudioStreamBasicDescription)stereoFloatInterleavedFormatWithSampleRate:(float)sampleRate
{
AudioStreamBasicDescription asbd;
UInt32 floatByteSize = sizeof(float);
asbd.mChannelsPerFrame = 2;
asbd.mBitsPerChannel = 8 * floatByteSize;
asbd.mBytesPerFrame = asbd.mChannelsPerFrame * floatByteSize;
asbd.mFramesPerPacket = 1;
asbd.mBytesPerPacket = asbd.mFramesPerPacket * asbd.mBytesPerFrame;
asbd.mFormatFlags = kAudioFormatFlagIsFloat;
asbd.mFormatID = kAudioFormatLinearPCM;
asbd.mSampleRate = sampleRate;
asbd.mReserved = 0;
return asbd;
}
//------------------------------------------------------------------------------
+ (AudioStreamBasicDescription)stereoFloatNonInterleavedFormatWithSampleRate:(float)sampleRate
{
AudioStreamBasicDescription asbd;
UInt32 floatByteSize = sizeof(float);
asbd.mBitsPerChannel = 8 * floatByteSize;
asbd.mBytesPerFrame = floatByteSize;
asbd.mChannelsPerFrame = 2;
asbd.mFormatFlags = kAudioFormatFlagIsFloat|kAudioFormatFlagIsNonInterleaved;
asbd.mFormatID = kAudioFormatLinearPCM;
asbd.mFramesPerPacket = 1;
asbd.mBytesPerPacket = asbd.mFramesPerPacket * asbd.mBytesPerFrame;
asbd.mSampleRate = sampleRate;
return asbd;
}
//------------------------------------------------------------------------------
+ (BOOL)isFloatFormat:(AudioStreamBasicDescription)asbd
{
return asbd.mFormatFlags & kAudioFormatFlagIsFloat;
}
//------------------------------------------------------------------------------
+ (BOOL)isInterleaved:(AudioStreamBasicDescription)asbd
{
return !(asbd.mFormatFlags & kAudioFormatFlagIsNonInterleaved);
}
//------------------------------------------------------------------------------
+ (BOOL)isLinearPCM:(AudioStreamBasicDescription)asbd
{
return asbd.mFormatID == kAudioFormatLinearPCM;
}
//------------------------------------------------------------------------------
+ (void)printASBD:(AudioStreamBasicDescription)asbd
{
char formatIDString[5];
UInt32 formatID = CFSwapInt32HostToBig(asbd.mFormatID);
bcopy (&formatID, formatIDString, 4);
formatIDString[4] = '\0';
NSLog (@" Sample Rate: %10.0f", asbd.mSampleRate);
NSLog (@" Format ID: %10s", formatIDString);
NSLog (@" Format Flags: %10X", (unsigned int)asbd.mFormatFlags);
NSLog (@" Bytes per Packet: %10d", (unsigned int)asbd.mBytesPerPacket);
NSLog (@" Frames per Packet: %10d", (unsigned int)asbd.mFramesPerPacket);
NSLog (@" Bytes per Frame: %10d", (unsigned int)asbd.mBytesPerFrame);
NSLog (@" Channels per Frame: %10d", (unsigned int)asbd.mChannelsPerFrame);
NSLog (@" Bits per Channel: %10d", (unsigned int)asbd.mBitsPerChannel);
}
//------------------------------------------------------------------------------
+ (NSString *)displayTimeStringFromSeconds:(NSTimeInterval)seconds
{
int totalSeconds = (int)ceil(seconds);
int secondsComponent = totalSeconds % 60;
int minutesComponent = (totalSeconds / 60) % 60;
return [NSString stringWithFormat:@"%02d:%02d", minutesComponent, secondsComponent];
}
//------------------------------------------------------------------------------
+ (NSString *)stringForAudioStreamBasicDescription:(AudioStreamBasicDescription)asbd
{
char formatIDString[5];
UInt32 formatID = CFSwapInt32HostToBig(asbd.mFormatID);
bcopy (&formatID, formatIDString, 4);
formatIDString[4] = '\0';
return [NSString stringWithFormat:
@"\nSample Rate: %10.0f,\n"
@"Format ID: %10s,\n"
@"Format Flags: %10X,\n"
@"Bytes per Packet: %10d,\n"
@"Frames per Packet: %10d,\n"
@"Bytes per Frame: %10d,\n"
@"Channels per Frame: %10d,\n"
@"Bits per Channel: %10d,\n"
@"IsInterleaved: %i,\n"
@"IsFloat: %i,",
asbd.mSampleRate,
formatIDString,
(unsigned int)asbd.mFormatFlags,
(unsigned int)asbd.mBytesPerPacket,
(unsigned int)asbd.mFramesPerPacket,
(unsigned int)asbd.mBytesPerFrame,
(unsigned int)asbd.mChannelsPerFrame,
(unsigned int)asbd.mBitsPerChannel,
[self isInterleaved:asbd],
[self isFloatFormat:asbd]];
}
//------------------------------------------------------------------------------
+ (void)setCanonicalAudioStreamBasicDescription:(AudioStreamBasicDescription*)asbd
numberOfChannels:(UInt32)nChannels
interleaved:(BOOL)interleaved
{
asbd->mFormatID = kAudioFormatLinearPCM;
#if TARGET_OS_IPHONE
int sampleSize = sizeof(float);
asbd->mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
#elif TARGET_OS_MAC
int sampleSize = sizeof(Float32);
asbd->mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
#endif
asbd->mBitsPerChannel = 8 * sampleSize;
asbd->mChannelsPerFrame = nChannels;
asbd->mFramesPerPacket = 1;
if (interleaved)
asbd->mBytesPerPacket = asbd->mBytesPerFrame = nChannels * sampleSize;
else {
asbd->mBytesPerPacket = asbd->mBytesPerFrame = sampleSize;
asbd->mFormatFlags |= kAudioFormatFlagIsNonInterleaved;
}
}
//------------------------------------------------------------------------------
#pragma mark - Math Utilities
//------------------------------------------------------------------------------
+ (void)appendBufferAndShift:(float*)buffer
withBufferSize:(int)bufferLength
toScrollHistory:(float*)scrollHistory
withScrollHistorySize:(int)scrollHistoryLength
{
int shiftLength = scrollHistoryLength - bufferLength;
size_t floatByteSize = sizeof(float);
size_t shiftByteSize = shiftLength * floatByteSize;
size_t bufferByteSize = bufferLength * floatByteSize;
memmove(&scrollHistory[0],
&scrollHistory[bufferLength],
shiftByteSize);
memmove(&scrollHistory[shiftLength],
&buffer[0],
bufferByteSize);
}
//------------------------------------------------------------------------------
+ (void) appendValue:(float)value
toScrollHistory:(float*)scrollHistory
withScrollHistorySize:(int)scrollHistoryLength
{
float val[1]; val[0] = value;
[self appendBufferAndShift:val
withBufferSize:1
toScrollHistory:scrollHistory
withScrollHistorySize:scrollHistoryLength];
}
//------------------------------------------------------------------------------
+(float)MAP:(float)value
leftMin:(float)leftMin
leftMax:(float)leftMax
rightMin:(float)rightMin
rightMax:(float)rightMax
{
float leftSpan = leftMax - leftMin;
float rightSpan = rightMax - rightMin;
float valueScaled = ( value - leftMin) / leftSpan;
return rightMin + (valueScaled * rightSpan);
}
//------------------------------------------------------------------------------
+(float)RMS:(float *)buffer
length:(int)bufferSize
{
float sum = 0.0;
for(int i = 0; i < bufferSize; i++)
sum += buffer[i] * buffer[i];
return sqrtf( sum / bufferSize);
}
//------------------------------------------------------------------------------
+(float)SGN:(float)value
{
return value < 0 ? -1.0f : ( value > 0 ? 1.0f : 0.0f);
}
//------------------------------------------------------------------------------
#pragma mark - OSStatus Utility
//------------------------------------------------------------------------------
+ (void)checkResult:(OSStatus)result operation:(const char *)operation
{
if (result == noErr) return;
char errorString[20];
// see if it appears to be a 4-char-code
*(UInt32 *)(errorString + 1) = CFSwapInt32HostToBig(result);
if (isprint(errorString[1]) && isprint(errorString[2]) && isprint(errorString[3]) && isprint(errorString[4]))
{
errorString[0] = errorString[5] = '\'';
errorString[6] = '\0';
} else
// no, format it as an integer
sprintf(errorString, "%d", (int)result);
fprintf(stderr, "Error: %s (%s)\n", operation, errorString);
if (__shouldExitOnCheckResultFail)
{
exit(-1);
}
}
//------------------------------------------------------------------------------
+ (NSString *)stringFromUInt32Code:(UInt32)code
{
char errorString[20];
// see if it appears to be a 4-char-code
*(UInt32 *)(errorString + 1) = CFSwapInt32HostToBig(code);
if (isprint(errorString[1]) &&
isprint(errorString[2]) &&
isprint(errorString[3]) &&
isprint(errorString[4]))
{
errorString[0] = errorString[5] = '\'';
errorString[6] = '\0';
}
return [NSString stringWithUTF8String:errorString];
}
//------------------------------------------------------------------------------
#pragma mark - Plot Utility
//------------------------------------------------------------------------------
+ (void)updateScrollHistory:(float **)scrollHistory
withLength:(int)scrollHistoryLength
atIndex:(int *)index
withBuffer:(float *)buffer
withBufferSize:(int)bufferSize
isResolutionChanging:(BOOL *)isChanging
{
//
size_t floatByteSize = sizeof(float);
if(*scrollHistory == NULL)
{
// Create the history buffer
*scrollHistory = (float *)calloc(8192, floatByteSize);
}
//
if(!*isChanging)
{
float rms = [EZAudioUtilities RMS:buffer length:bufferSize];
if(*index < scrollHistoryLength)
{
float *hist = *scrollHistory;
hist[*index] = rms;
(*index)++;
}
else
{
[EZAudioUtilities appendValue:rms
toScrollHistory:*scrollHistory
withScrollHistorySize:scrollHistoryLength];
}
}
}
//------------------------------------------------------------------------------
#pragma mark - Color Utility
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Color Utility
///-----------------------------------------------------------
+ (void)getColorComponentsFromCGColor:(CGColorRef)color
red:(CGFloat *)red
green:(CGFloat *)green
blue:(CGFloat *)blue
alpha:(CGFloat *)alpha
{
size_t componentCount = CGColorGetNumberOfComponents(color);
if (componentCount == 4)
{
const CGFloat *components = CGColorGetComponents(color);
*red = components[0];
*green = components[1];
*blue = components[2];
*alpha = components[3];
}
}
//------------------------------------------------------------------------------
#pragma mark - TPCircularBuffer Utility
//------------------------------------------------------------------------------
+ (void)appendDataToCircularBuffer:(TPCircularBuffer *)circularBuffer
fromAudioBufferList:(AudioBufferList *)audioBufferList
{
TPCircularBufferProduceBytes(circularBuffer,
audioBufferList->mBuffers[0].mData,
audioBufferList->mBuffers[0].mDataByteSize);
}
//------------------------------------------------------------------------------
+ (void)circularBuffer:(TPCircularBuffer *)circularBuffer withSize:(int)size
{
TPCircularBufferInit(circularBuffer, size);
}
//------------------------------------------------------------------------------
+ (void)freeCircularBuffer:(TPCircularBuffer *)circularBuffer
{
TPCircularBufferClear(circularBuffer);
TPCircularBufferCleanup(circularBuffer);
}
//------------------------------------------------------------------------------
#pragma mark - EZPlotHistoryInfo Utility
//------------------------------------------------------------------------------
+ (void)appendBuffer:(float *)buffer
withBufferSize:(UInt32)bufferSize
toHistoryInfo:(EZPlotHistoryInfo *)historyInfo
{
//
// Do nothing if there is no buffer
//
if (bufferSize == 0)
{
return;
}
//
// Update the scroll history datasource
//
float rms = [EZAudioUtilities RMS:buffer length:bufferSize];
float src[1];
src[0] = isnan(rms) ? 0.0 : rms;
TPCircularBufferProduceBytes(&historyInfo->circularBuffer, src, sizeof(src));
int32_t targetBytes = historyInfo->bufferSize * sizeof(float);
int32_t availableBytes = 0;
float *historyBuffer = TPCircularBufferTail(&historyInfo->circularBuffer, &availableBytes);
int32_t bytes = MIN(targetBytes, availableBytes);
memmove(historyInfo->buffer, historyBuffer, bytes);
if (targetBytes <= availableBytes)
{
TPCircularBufferConsume(&historyInfo->circularBuffer, availableBytes - targetBytes);
}
}
//------------------------------------------------------------------------------
+ (void)clearHistoryInfo:(EZPlotHistoryInfo *)historyInfo
{
memset(historyInfo->buffer, 0, historyInfo->bufferSize * sizeof(float));
TPCircularBufferClear(&historyInfo->circularBuffer);
}
//------------------------------------------------------------------------------
+ (void)freeHistoryInfo:(EZPlotHistoryInfo *)historyInfo
{
free(historyInfo->buffer);
free(historyInfo);
TPCircularBufferCleanup(&historyInfo->circularBuffer);
}
//------------------------------------------------------------------------------
+ (EZPlotHistoryInfo *)historyInfoWithDefaultLength:(int)defaultLength
maximumLength:(int)maximumLength
{
//
// Setup buffers
//
EZPlotHistoryInfo *historyInfo = (EZPlotHistoryInfo *)malloc(sizeof(EZPlotHistoryInfo));
historyInfo->bufferSize = defaultLength;
historyInfo->buffer = calloc(maximumLength, sizeof(float));
TPCircularBufferInit(&historyInfo->circularBuffer, maximumLength);
//
// Zero out circular buffer
//
float emptyBuffer[maximumLength];
memset(emptyBuffer, 0, sizeof(emptyBuffer));
TPCircularBufferProduceBytes(&historyInfo->circularBuffer,
emptyBuffer,
(int32_t)sizeof(emptyBuffer));
return historyInfo;
}
//------------------------------------------------------------------------------
@end
+16 -26
View File
@@ -1,27 +1,17 @@
Pod::Spec.new do |s|
s.name = "EZAudio"
s.version = "0.7.1"
s.summary = "A simple, intuitive audio framework for iOS and OSX useful for anyone doing audio processing and/or audio-based visualizations."
s.homepage = "https://github.com/syedhali/EZAudio"
s.screenshots = "https://s3-us-west-1.amazonaws.com/ezaudio-media/EZAudioSummary.png"
s.license = { :type => 'MIT', :file => 'LICENSE' }
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 => s.version }
s.exclude_files = [ 'EZAudio/VERSION', 'EZAudio/TPCircularBuffer.{h,c}' ]
s.ios.frameworks = 'AudioToolbox','AVFoundation','GLKit'
s.osx.frameworks = 'AudioToolbox','AudioUnit','CoreAudio','QuartzCore','OpenGL','GLKit'
s.requires_arc = true;
s.default_subspec = 'Full'
s.subspec 'Core' do |core|
core.source_files = 'EZAudio/*.{h,m,c}'
end
s.subspec 'Full' do |full|
full.dependency 'TPCircularBuffer', '~> 0.0'
full.dependency 'EZAudio/Core'
end
end
s.name = "EZAudio"
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"
s.license = { :type => 'MIT', :file => 'LICENSE' }
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.6" }
s.source_files = 'EZAudio/*.{h,m,c}'
s.exclude_files = 'EZAudio/VERSION'
s.ios.frameworks = 'AudioToolbox','AVFoundation','GLKit'
s.osx.frameworks = 'AudioToolbox','AudioUnit','CoreAudio','QuartzCore','OpenGL','GLKit'
s.requires_arc = true;
end
+291
View File
@@ -0,0 +1,291 @@
//
// EZAudio.h
// EZAudio
//
// Created by Syed Haris Ali on 11/21/13.
// Copyright (c) 2013 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 <AudioToolbox/AudioToolbox.h>
#pragma mark - 3rd Party Utilties
#import "TPCircularBuffer.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
#pragma mark - AudioBufferList Utility
///-----------------------------------------------------------
/// @name AudioBufferList Utility
///-----------------------------------------------------------
/**
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 *)audioBufferListWithNumberOfFrames:(UInt32)frames
numberOfChannels:(UInt32)channels
interleaved:(BOOL)interleaved;
/**
Deallocates an AudioBufferList structure from memory.
@param bufferList A pointer to the buffer list you would like to free
*/
+(void)freeBufferList:(AudioBufferList*)bufferList;
#pragma mark - AudioStreamBasicDescription Utilties
///-----------------------------------------------------------
/// @name Creating An AudioStreamBasicDescription
///-----------------------------------------------------------
/**
@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
@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
@return A new AudioStreamBasicDescription with the specified format.
*/
+(AudioStreamBasicDescription)monoCanonicalFormatWithSampleRate:(float)sampleRate;
/**
@param sampleRate The desired sample rate
@return A new AudioStreamBasicDescription with the specified format.
*/
+(AudioStreamBasicDescription)stereoCanonicalNonInterleavedFormatWithSampleRate:(float)sampleRate;
/**
@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
///-----------------------------------------------------------
/**
Nicely logs out the contents of an AudioStreamBasicDescription struct
@param asbd The AudioStreamBasicDescription struct with content to print out
*/
+(void)printASBD:(AudioStreamBasicDescription)asbd;
/**
Just a wrapper around the setCanonical function provided in the Core Audio Utility C++ class.
@param asbd The AudioStreamBasicDescription structure to modify
@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
numberOfChannels:(UInt32)nChannels
interleaved:(BOOL)interleaved;
#pragma mark - Math Utilities
///-----------------------------------------------------------
/// @name Math Utilities
///-----------------------------------------------------------
/**
Appends an array of values to a history buffer and performs an internal shift to add the values to the tail and removes the same number of values from the head.
@param buffer A float array of values to append to the tail of the history buffer
@param bufferLength The length of the float array being appended to the history buffer
@param scrollHistory The target history buffer in which to append the values
@param scrollHistoryLength The length of the target history buffer
*/
+(void)appendBufferAndShift:(float*)buffer
withBufferSize:(int)bufferLength
toScrollHistory:(float*)scrollHistory
withScrollHistorySize:(int)scrollHistoryLength;
/**
Appends a value to a history buffer and performs an internal shift to add the value to the tail and remove the 0th value.
@param value The float value to append to the history array
@param scrollHistory The target history buffer in which to append the values
@param scrollHistoryLength The length of the target history buffer
*/
+(void) appendValue:(float)value
toScrollHistory:(float*)scrollHistory
withScrollHistorySize:(int)scrollHistoryLength;
/**
Maps a value from one coordinate system into another one. Takes in the current value to map, the minimum and maximum values of the first coordinate system, and the minimum and maximum values of the second coordinate system and calculates the mapped value in the second coordinate system's constraints.
@param value The value expressed in the first coordinate system
@param leftMin The minimum of the first coordinate system
@param leftMax The maximum of the first coordinate system
@param rightMin The minimum of the second coordindate system
@param rightMax The maximum of the second coordinate system
@return The mapped value in terms of the second coordinate system
*/
+(float)MAP:(float)value
leftMin:(float)leftMin
leftMax:(float)leftMax
rightMin:(float)rightMin
rightMax:(float)rightMax;
/**
Calculates the root mean squared for a buffer.
@param buffer A float buffer array of values whose root mean squared to calculate
@param bufferSize The size of the float buffer
@return The root mean squared of the buffer
*/
+(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
///-----------------------------------------------------------
/**
Basic check result function useful for checking each step of the audio setup process
@param result The OSStatus representing the result of an operation
@param operation A string (const char, not NSString) describing the operation taking place (will print if fails)
*/
+(void)checkResult:(OSStatus)result
operation:(const char*)operation;
#pragma mark - Plot Utility
///-----------------------------------------------------------
/// @name Plot Utility
///-----------------------------------------------------------
+(void)updateScrollHistory:(float**)scrollHistory
withLength:(int)scrollHistoryLength
atIndex:(int*)index
withBuffer:(float*)buffer
withBufferSize:(int)bufferSize
isResolutionChanging:(BOOL*)isChanging;
#pragma mark - TPCircularBuffer Utility
///-----------------------------------------------------------
/// @name TPCircularBuffer Utility
///-----------------------------------------------------------
/**
Appends the data from the audio buffer list to the circular buffer
@param circularBuffer Pointer to the instance of the TPCircularBuffer to add the audio data to
@param audioBufferList Pointer to the instance of the AudioBufferList with the audio data
*/
+(void)appendDataToCircularBuffer:(TPCircularBuffer*)circularBuffer
fromAudioBufferList:(AudioBufferList*)audioBufferList;
/**
Initializes the circular buffer (just a wrapper around the C method)
* @param circularBuffer Pointer to an instance of the TPCircularBuffer
* @param size The length of the TPCircularBuffer (usually 1024)
*/
+(void)circularBuffer:(TPCircularBuffer*)circularBuffer
withSize:(int)size;
/**
Frees a circular buffer
@param circularBuffer Pointer to the circular buffer to clear
*/
+(void)freeCircularBuffer:(TPCircularBuffer*)circularBuffer;
@end
+369
View File
@@ -0,0 +1,369 @@
//
// EZAudio.m
// EZAudio
//
// Created by Syed Haris Ali on 11/21/13.
// Copyright (c) 2013 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 "EZAudio.h"
@implementation EZAudio
#pragma mark - AudioBufferList Utility
+(AudioBufferList *)audioBufferListWithNumberOfFrames:(UInt32)frames
numberOfChannels:(UInt32)channels
interleaved:(BOOL)interleaved
{
AudioBufferList *audioBufferList = (AudioBufferList*)malloc(sizeof(AudioBufferList) + sizeof(AudioBuffer)*(channels-1));
UInt32 outputBufferSize = 32 * frames; // 32 KB
audioBufferList->mNumberBuffers = interleaved ? 1 : channels;
for( int i = 0; i < audioBufferList->mNumberBuffers; i++ )
{
audioBufferList->mBuffers[i].mNumberChannels = channels;
audioBufferList->mBuffers[i].mDataByteSize = channels * outputBufferSize;
audioBufferList->mBuffers[i].mData = (float*)malloc(channels * sizeof(float) * outputBufferSize);
}
return audioBufferList;
}
+(void)freeBufferList:(AudioBufferList *)bufferList
{
if( bufferList )
{
if( bufferList->mNumberBuffers )
{
for( int i = 0; i < bufferList->mNumberBuffers; i++ )
{
if( bufferList->mBuffers[i].mData )
{
free(bufferList->mBuffers[i].mData);
}
}
}
free(bufferList);
}
bufferList = NULL;
}
#pragma mark - AudioStreamBasicDescription Utility
+(AudioStreamBasicDescription)AIFFFormatWithNumberOfChannels:(UInt32)channels
sampleRate:(float)sampleRate
{
AudioStreamBasicDescription asbd;
memset(&asbd, 0, sizeof(asbd));
asbd.mFormatID = kAudioFormatLinearPCM;
asbd.mFormatFlags = kAudioFormatFlagIsBigEndian|kAudioFormatFlagIsPacked|kAudioFormatFlagIsSignedInteger;
asbd.mSampleRate = sampleRate;
asbd.mChannelsPerFrame = channels;
asbd.mBitsPerChannel = 32;
asbd.mBytesPerPacket = (asbd.mBitsPerChannel / 8) * asbd.mChannelsPerFrame;
asbd.mFramesPerPacket = 1;
asbd.mBytesPerFrame = (asbd.mBitsPerChannel / 8) * asbd.mChannelsPerFrame;
return asbd;
}
+(AudioStreamBasicDescription)iLBCFormatWithSampleRate:(float)sampleRate
{
AudioStreamBasicDescription asbd;
memset(&asbd, 0, sizeof(asbd));
asbd.mFormatID = kAudioFormatiLBC;
asbd.mChannelsPerFrame = 1;
asbd.mSampleRate = sampleRate;
// Fill in the rest of the descriptions using the Audio Format API
UInt32 propSize = sizeof(asbd);
[EZAudio checkResult:AudioFormatGetProperty(kAudioFormatProperty_FormatInfo,
0,
NULL,
&propSize,
&asbd)
operation:"Failed to fill out the rest of the m4a AudioStreamBasicDescription"];
return asbd;
}
+ (BOOL)isInterleaved:(AudioStreamBasicDescription)asbd
{
return !(asbd.mFormatFlags & kAudioFormatFlagIsNonInterleaved);
}
+ (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);
}
+(void)setCanonicalAudioStreamBasicDescription:(AudioStreamBasicDescription*)asbd
numberOfChannels:(UInt32)nChannels
interleaved:(BOOL)interleaved {
asbd->mFormatID = kAudioFormatLinearPCM;
#if TARGET_OS_IPHONE
int sampleSize = sizeof(float);
asbd->mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
#elif TARGET_OS_MAC
int sampleSize = sizeof(Float32);
asbd->mFormatFlags = kAudioFormatFlagsNativeFloatPacked;
#endif
asbd->mBitsPerChannel = 8 * sampleSize;
asbd->mChannelsPerFrame = nChannels;
asbd->mFramesPerPacket = 1;
if (interleaved)
asbd->mBytesPerPacket = asbd->mBytesPerFrame = nChannels * sampleSize;
else {
asbd->mBytesPerPacket = asbd->mBytesPerFrame = sampleSize;
asbd->mFormatFlags |= kAudioFormatFlagIsNonInterleaved;
}
}
#pragma mark - OSStatus Utility
+(void)checkResult:(OSStatus)result
operation:(const char *)operation {
if (result == noErr) return;
char errorString[20];
// see if it appears to be a 4-char-code
*(UInt32 *)(errorString + 1) = CFSwapInt32HostToBig(result);
if (isprint(errorString[1]) && isprint(errorString[2]) && isprint(errorString[3]) && isprint(errorString[4])) {
errorString[0] = errorString[5] = '\'';
errorString[6] = '\0';
} else
// no, format it as an integer
sprintf(errorString, "%d", (int)result);
fprintf(stderr, "Error: %s (%s)\n", operation, errorString);
exit(1);
}
#pragma mark - Math Utility
+(void)appendBufferAndShift:(float*)buffer
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);
}
+(void) appendValue:(float)value
toScrollHistory:(float*)scrollHistory
withScrollHistorySize:(int)scrollHistoryLength {
float val[1]; val[0] = value;
[self appendBufferAndShift:val
withBufferSize:1
toScrollHistory:scrollHistory
withScrollHistorySize:scrollHistoryLength];
}
+(float)MAP:(float)value
leftMin:(float)leftMin
leftMax:(float)leftMax
rightMin:(float)rightMin
rightMax:(float)rightMax {
float leftSpan = leftMax - leftMin;
float rightSpan = rightMax - rightMin;
float valueScaled = ( value - leftMin ) / leftSpan;
return rightMin + (valueScaled * rightSpan);
}
+(float)RMS:(float *)buffer
length:(int)bufferSize {
float sum = 0.0;
for(int i = 0; i < bufferSize; i++)
sum += buffer[i] * buffer[i];
return sqrtf( sum / bufferSize );
}
+(float)SGN:(float)value
{
return value < 0 ? -1.0f : ( value > 0 ? 1.0f : 0.0f );
}
#pragma mark - Plot Utility
+(void)updateScrollHistory:(float **)scrollHistory
withLength:(int)scrollHistoryLength
atIndex:(int*)index
withBuffer:(float *)buffer
withBufferSize:(int)bufferSize
isResolutionChanging:(BOOL*)isChanging {
//
size_t floatByteSize = sizeof(float);
//
if( *scrollHistory == NULL ){
// Create the history buffer
*scrollHistory = (float*)calloc(1024,floatByteSize);
}
//
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);
}
+(void)appendDataToCircularBuffer:(TPCircularBuffer*)circularBuffer
fromAudioBufferList:(AudioBufferList*)audioBufferList {
TPCircularBufferProduceBytes(circularBuffer,
audioBufferList->mBuffers[0].mData,
audioBufferList->mBuffers[0].mDataByteSize);
}
+(void)freeCircularBuffer:(TPCircularBuffer *)circularBuffer {
TPCircularBufferClear(circularBuffer);
TPCircularBufferCleanup(circularBuffer);
}
@end
-10
View File
@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:OSX/EZAudio.xcodeproj">
</FileRef>
<FileRef
location = "group:iOS/EZAudio.xcodeproj">
</FileRef>
</Workspace>
+20
View File
@@ -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
+38
View File
@@ -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
@@ -25,22 +25,22 @@
#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
#import "EZAudioFloatData.h"
#import "EZAudioWaveformData.h"
//------------------------------------------------------------------------------
@class EZAudio;
@class EZAudioConverter;
@class EZAudioFile;
//------------------------------------------------------------------------------
#pragma mark - Blocks
//------------------------------------------------------------------------------
/**
A block used when returning back the waveform data. The waveform data itself will be an array of float arrays, one for each channel, and the length indicates the total length of each float array.
@param waveformData An array of float arrays, each representing a channel of audio data from the file
@param length An int representing the length of each channel of float audio data
*/
typedef void (^EZAudioWaveformDataCompletionBlock)(float **waveformData, int length);
typedef NS_ENUM(NSUInteger, EZAudioFilePermission)
{
EZAudioFilePermissionRead = kAudioFileReadPermission,
EZAudioFilePermissionWrite = kAudioFileWritePermission,
EZAudioFilePermissionReadWrite = kAudioFileReadWritePermission,
};
//------------------------------------------------------------------------------
#pragma mark - EZAudioFileDelegate
@@ -58,8 +58,8 @@ typedef void (^EZAudioWaveformDataCompletionBlock)(float **waveformData, int len
@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
- (void) audioFile:(EZAudioFile*)audioFile
readAudio:(float**)buffer
withBufferSize:(UInt32)bufferSize
withNumberOfChannels:(UInt32)numberOfChannels;
@@ -70,7 +70,8 @@ typedef void (^EZAudioWaveformDataCompletionBlock)(float **waveformData, int len
@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
@@ -80,7 +81,17 @@ typedef void (^EZAudioWaveformDataCompletionBlock)(float **waveformData, int len
/**
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 <NSCopying>
@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)(EZAudioWaveformData *waveformData);
//------------------------------------------------------------------------------
#pragma mark - Properties
@@ -98,32 +109,55 @@ typedef void (^EZAudioWaveformDataCompletionBlock)(float **waveformData, int len
*/
/**
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. nil if the file path does not exist.
*/
- (instancetype)initWithURL:(NSURL *)url;
/**
Creates a new instance of the EZAudioFile using a file path URL with a delegate conforming to the EZAudioFileDelegate protocol.
@param delegate The audio file delegate that receives events specified by the EZAudioFileDelegate protocol
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.
*/
- (instancetype)initWithURL:(NSURL *)url
delegate:(id<EZAudioFileDelegate>)delegate;
- (instancetype)initWithURL:(NSURL*)url;
//------------------------------------------------------------------------------
/**
Creates a new instance of the EZAudioFile using a file path URL with a delegate conforming to the EZAudioFileDelegate protocol and a client format.
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)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.
*/
- (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
- (instancetype)initWithURL:(NSURL*)url
delegate:(id<EZAudioFileDelegate>)delegate
permission:(EZAudioFilePermission)permission
fileFormat:(AudioStreamBasicDescription)fileFormat
clientFormat:(AudioStreamBasicDescription)clientFormat;
//------------------------------------------------------------------------------
@@ -138,31 +172,52 @@ typedef void (^EZAudioWaveformDataCompletionBlock)(float **waveformData, int len
@param url The file path reference of the audio file as an NSURL.
@return The newly created EZAudioFile instance.
*/
+ (instancetype)audioFileWithURL:(NSURL *)url;
+ (instancetype)audioFileWithURL:(NSURL*)url;
//------------------------------------------------------------------------------
/**
Class method that creates a new instance of the EZAudioFile using a file path URL with a delegate conforming to the EZAudioFileDelegate protocol.
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 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.
*/
+ (instancetype)audioFileWithURL:(NSURL *)url
delegate:(id<EZAudioFileDelegate>)delegate;
+ (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 with a delegate conforming to the EZAudioFileDelegate protocol and a client format.
@param url The file path reference of the audio file as an NSURL.
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 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, interleaved MP3 file format, but your app wants to read linear PCM samples at a sample rate of 44.1 kHz so it can be read in the context of other components sharing a common stream format (like a audio processing graph). If this initializer is not used then the `defaultClientFormat` will be used as teh default value for the client format.
@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
+ (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
clientFormat:(AudioStreamBasicDescription)clientFormat;
permission:(EZAudioFilePermission)permission
fileFormat:(AudioStreamBasicDescription)fileFormat
clientFormat:(AudioStreamBasicDescription)clientFormat;;
//------------------------------------------------------------------------------
#pragma mark - Class Methods
@@ -172,21 +227,11 @@ typedef void (^EZAudioWaveformDataCompletionBlock)(float **waveformData, int len
*/
/**
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 done automatically within the EZAudioFile `readFrames:audioBufferList:bufferSize:eof:` method. Default is stereo, non-interleaved, 44.1 kHz.
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;
//------------------------------------------------------------------------------
/**
A class method that subclasses can override to specify the default sample rate that will be used in the `defaultClientFormat` method. Default is 44100.0 (44.1 kHz).
@return A Float64 representing the sample rate that should be used in the default client format.
*/
+ (Float64)defaultClientFormatSampleRate;
//------------------------------------------------------------------------------
/**
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.
@@ -197,7 +242,7 @@ typedef void (^EZAudioWaveformDataCompletionBlock)(float **waveformData, int len
#pragma mark - Events
//------------------------------------------------------------------------------
/**
@name Reading From The Audio File
@name Reading The Audio File
*/
/**
@@ -207,13 +252,15 @@ typedef void (^EZAudioWaveformDataCompletionBlock)(float **waveformData, int len
@param bufferSize A pointer to a UInt32 in which to store the read buffersize
@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;
-(void)readFrames:(UInt32)frames
audioBufferList:(AudioBufferList *)audioBufferList
bufferSize:(UInt32 *)bufferSize
eof:(BOOL *)eof;
//------------------------------------------------------------------------------
#pragma mark - Seeking Through The Audio File
//------------------------------------------------------------------------------
/**
@name Seeking Through The Audio File
*/
@@ -232,26 +279,10 @@ typedef void (^EZAudioWaveformDataCompletionBlock)(float **waveformData, int len
*/
/**
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. 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 done automatically within the EZAudioFile `readFrames:audioBufferList:bufferSize:eof:` method. Default is stereo, non-interleaved, 44.1 kHz.
@warning This must be a linear PCM format!
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.
*/
@property (readwrite) AudioStreamBasicDescription clientFormat;
//------------------------------------------------------------------------------
/**
Provides the current offset in the audio file as an NSTimeInterval (i.e. in seconds). When setting this it will determine the correct frame offset and perform a `seekToFrame` to the new time offset.
@warning Make sure the new current time offset is less than the `duration` or you will receive an invalid seek assertion.
*/
@property (nonatomic, readwrite) NSTimeInterval currentTime;
//------------------------------------------------------------------------------
/**
Provides the duration of the audio file in seconds.
*/
@property (readonly) NSTimeInterval duration;
- (AudioStreamBasicDescription)clientFormat;
//------------------------------------------------------------------------------
@@ -259,29 +290,15 @@ typedef void (^EZAudioWaveformDataCompletionBlock)(float **waveformData, int len
Provides the AudioStreamBasicDescription structure containing the format of the file.
@return An AudioStreamBasicDescription structure describing the format of the audio file.
*/
@property (readonly) AudioStreamBasicDescription fileFormat;
- (AudioStreamBasicDescription)fileFormat;
//------------------------------------------------------------------------------
/**
Provides the current time as an NSString with the time format MM:SS.
*/
@property (readonly) NSString *formattedCurrentTime;
//------------------------------------------------------------------------------
/**
Provides the duration as an NSString with the time format MM:SS.
*/
@property (readonly) NSString *formattedDuration;
//------------------------------------------------------------------------------
/**
Provides the frame index (a.k.a the seek positon) within the audio file as SInt64. This can be helpful when seeking through the audio file.
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.
*/
@property (readonly) SInt64 frameIndex;
- (SInt64)frameIndex;
//------------------------------------------------------------------------------
@@ -289,17 +306,15 @@ typedef void (^EZAudioWaveformDataCompletionBlock)(float **waveformData, int len
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.
*/
@property (readonly) NSDictionary *metadata;
- (NSDictionary *)metadata;
//------------------------------------------------------------------------------
/**
Provides the total duration of the audio file in seconds.
@deprecated This property is deprecated starting in version 0.3.0.
@note Please use `duration` property instead.
@return The total duration of the audio file as a Float32.
*/
@property (readonly) NSTimeInterval totalDuration __attribute__((deprecated));;
- (NSTimeInterval)totalDuration;
//------------------------------------------------------------------------------
@@ -307,7 +322,7 @@ typedef void (^EZAudioWaveformDataCompletionBlock)(float **waveformData, int len
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.
*/
@property (readonly) SInt64 totalClientFrames;
- (SInt64)totalClientFrames;
//------------------------------------------------------------------------------
@@ -315,7 +330,7 @@ typedef void (^EZAudioWaveformDataCompletionBlock)(float **waveformData, int len
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.
*/
@property (readonly) SInt64 totalFrames;
- (SInt64)totalFrames;
//------------------------------------------------------------------------------
@@ -323,7 +338,17 @@ typedef void (^EZAudioWaveformDataCompletionBlock)(float **waveformData, int len
Provides the NSURL for the audio file.
@return An NSURL representing the path of the EZAudioFile instance.
*/
@property (nonatomic, copy, readonly) NSURL *url;
- (NSURL*)url;
//------------------------------------------------------------------------------
#pragma mark - Setters
//------------------------------------------------------------------------------
/**
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.
*/
- (void)setClientFormat:(AudioStreamBasicDescription)clientFormat;
//------------------------------------------------------------------------------
#pragma mark - Helpers
@@ -332,36 +357,36 @@ typedef void (^EZAudioWaveformDataCompletionBlock)(float **waveformData, int len
/**
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 EZAudioFloatData instance containing the audio data for all channels of the audio.
@return A EZAudioWaveformData instance containing the audio data for all channels of the audio.
*/
- (EZAudioFloatData *)getWaveformData;
- (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 EZAudioFloatData instance containing the audio data for all channels of the audio.
@return A EZAudioWaveformData instance containing the audio data for all channels of the audio.
*/
- (EZAudioFloatData *)getWaveformDataWithNumberOfPoints:(UInt32)numberOfPoints;
- (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 EZAudioWaveformDataCompletionBlock that executes when the waveform data has been extracted. Provides a `EZAudioFloatData` instance containing the waveform data for all audio channels.
@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:(EZAudioWaveformDataCompletionBlock)completion;
- (void)getWaveformDataWithCompletionBlock:(WaveformDataCompletionBlock)completion;
//------------------------------------------------------------------------------
/**
Asynchronously 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.
@param completion A EZAudioWaveformDataCompletionBlock that executes when the waveform data has been extracted. Provides a `EZAudioFloatData` instance containing the waveform data for all audio channels.
@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)getWaveformDataWithNumberOfPoints:(UInt32)numberOfPoints
completion:(EZAudioWaveformDataCompletionBlock)completion;
completion:(WaveformDataCompletionBlock)completion;
//------------------------------------------------------------------------------
+624
View File
@@ -0,0 +1,624 @@
//
// EZAudioFile.m
// EZAudio
//
// Created by Syed Haris Ali on 12/1/13.
// Copyright (c) 2013 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 "EZAudioFile.h"
//------------------------------------------------------------------------------
#import "EZAudio.h"
#import "EZAudioConverter.h"
#import "EZAudioWaveformData.h"
#include <pthread.h>
//------------------------------------------------------------------------------
// 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
//------------------------------------------------------------------------------
#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;
}
//------------------------------------------------------------------------------
- (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
//------------------------------------------------------------------------------
+ (instancetype)audioFileWithURL:(NSURL*)url
{
return [[self alloc] initWithURL:url];
}
//------------------------------------------------------------------------------
+ (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
//------------------------------------------------------------------------------
+ (AudioStreamBasicDescription)defaultClientFormat
{
return [EZAudio stereoFloatInterleavedFormatWithSampleRate:44100];
}
//------------------------------------------------------------------------------
+ (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"];
// 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
{
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
{
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
//------------------------------------------------------------------------------
- (EZAudioWaveformData *)getWaveformData
{
return [self getWaveformDataWithNumberOfPoints:EZAudioFileWaveformDefaultResolution];
}
//------------------------------------------------------------------------------
- (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);
}
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 self.info.clientFormat;
}
//------------------------------------------------------------------------------
- (AudioStreamBasicDescription)fileFormat
{
return self.info.fileFormat;
}
//------------------------------------------------------------------------------
- (SInt64)frameIndex
{
SInt64 frameIndex;
[EZAudio checkResult:ExtAudioFileTell(self.info.extAudioFileRef, &frameIndex)
operation:"Failed to get frame index"];
return frameIndex;
}
//------------------------------------------------------------------------------
- (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;
}
//------------------------------------------------------------------------------
- (NSTimeInterval)totalDuration
{
SInt64 totalFrames = [self totalFrames];
return (NSTimeInterval) totalFrames / self.info.fileFormat.mSampleRate;
}
//------------------------------------------------------------------------------
- (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;
}
//------------------------------------------------------------------------------
- (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;
}
//------------------------------------------------------------------------------
- (NSURL*)url
{
return (__bridge NSURL*)self.info.sourceURL;
}
//------------------------------------------------------------------------------
#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
+300
View File
@@ -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
+296
View File
@@ -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
+82
View File
@@ -0,0 +1,82 @@
//
// EZAudioPlot.h
// EZAudio
//
// Created by Syed Haris Ali on 9/2/13.
// Copyright (c) 2013 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 "TargetConditionals.h"
#import "EZPlot.h"
@class EZAudio;
#define kEZAudioPlotMaxHistoryBufferLength (8192)
#define kEZAudioPlotDefaultHistoryBufferLength (1024)
/**
`EZAudioPlot`, a subclass of `EZPlot`, is a cross-platform (iOS and OSX) class that plots an audio waveform using Core Graphics.
The caller provides updates a constant stream of updated audio data in the `updateBuffer:withBufferSize:` function, which in turn will be plotted in one of the plot types:
* Buffer (`EZPlotTypeBuffer`) - A plot that only consists of the current buffer and buffer size from the last call to `updateBuffer:withBufferSize:`. This looks similar to the default openFrameworks input audio example.
* Rolling (`EZPlotTypeRolling`) - A plot that consists of a rolling history of values averaged from each buffer. This is the traditional waveform look.
#Parent Methods and Properties#
See EZPlot for full API methods and properties (colors, plot type, update function)
*/
@interface EZAudioPlot : EZPlot
{
CGPoint *plotData;
UInt32 plotLength;
}
#pragma mark - Adjust Resolution
///-----------------------------------------------------------
/// @name Adjusting The Resolution
///-----------------------------------------------------------
/**
Sets the length of the rolling history display. Can grow or shrink the display up to the maximum size specified by the kEZAudioPlotMaxHistoryBufferLength macro. Will return the actual set value, which will be either the given value if smaller than the kEZAudioPlotMaxHistoryBufferLength or kEZAudioPlotMaxHistoryBufferLength if a larger value is attempted to be set.
@param historyLength The new length of the rolling history buffer.
@return The new value equal to the historyLength or the kEZAudioPlotMaxHistoryBufferLength.
*/
-(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
+298
View File
@@ -0,0 +1,298 @@
//
// EZAudioPlot.m
// EZAudio
//
// Created by Syed Haris Ali on 9/2/13.
// Copyright (c) 2013 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 "EZAudioPlot.h"
#import "EZAudio.h"
@interface EZAudioPlot () {
// BOOL _hasData;
// TPCircularBuffer _historyBuffer;
// Rolling History
BOOL _setMaxLength;
float *_scrollHistory;
int _scrollHistoryIndex;
UInt32 _scrollHistoryLength;
BOOL _changingHistorySize;
}
@end
@implementation EZAudioPlot
@synthesize backgroundColor = _backgroundColor;
@synthesize color = _color;
@synthesize gain = _gain;
@synthesize plotType = _plotType;
@synthesize shouldFill = _shouldFill;
@synthesize shouldMirror = _shouldMirror;
#pragma mark - Initialization
-(id)init {
self = [super init];
if(self){
[self initPlot];
}
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if(self){
[self initPlot];
}
return self;
}
#if TARGET_OS_IPHONE
-(id)initWithFrame:(CGRect)frameRect {
#elif TARGET_OS_MAC
-(id)initWithFrame:(NSRect)frameRect {
#endif
self = [super initWithFrame:frameRect];
if(self){
[self initPlot];
}
return self;
}
-(void)initPlot {
#if TARGET_OS_IPHONE
self.backgroundColor = [UIColor blackColor];
self.color = [UIColor colorWithHue:0 saturation:1.0 brightness:1.0 alpha:1.0];
#elif TARGET_OS_MAC
self.backgroundColor = [NSColor blackColor];
self.color = [NSColor colorWithCalibratedHue:0 saturation:1.0 brightness:1.0 alpha:1.0];
#endif
self.gain = 1.0;
self.plotType = EZPlotTypeRolling;
self.shouldMirror = NO;
self.shouldFill = NO;
plotData = NULL;
_scrollHistory = NULL;
_scrollHistoryLength = kEZAudioPlotDefaultHistoryBufferLength;
}
#pragma mark - Setters
-(void)setBackgroundColor:(id)backgroundColor {
_backgroundColor = backgroundColor;
[self _refreshDisplay];
}
-(void)setColor:(id)color {
_color = color;
[self _refreshDisplay];
}
-(void)setGain:(float)gain {
_gain = gain;
[self _refreshDisplay];
}
-(void)setPlotType:(EZPlotType)plotType {
_plotType = plotType;
[self _refreshDisplay];
}
-(void)setShouldFill:(BOOL)shouldFill {
_shouldFill = shouldFill;
[self _refreshDisplay];
}
-(void)setShouldMirror:(BOOL)shouldMirror {
_shouldMirror = shouldMirror;
[self _refreshDisplay];
}
-(void)_refreshDisplay {
#if TARGET_OS_IPHONE
[self setNeedsDisplay];
#elif TARGET_OS_MAC
[self setNeedsDisplay:YES];
#endif
}
#pragma mark - Get Data
-(void)setSampleData:(float *)data
length:(int)length {
if( plotData != nil ){
free(plotData);
}
plotData = (CGPoint *)calloc(sizeof(CGPoint),length);
plotLength = length;
for(int i = 0; i < length; i++) {
data[i] = i == 0 ? 0 : data[i];
plotData[i] = CGPointMake(i,data[i] * _gain);
}
[self _refreshDisplay];
}
#pragma mark - Update
-(void)updateBuffer:(float *)buffer withBufferSize:(UInt32)bufferSize {
if( _plotType == EZPlotTypeRolling ){
// Update the scroll history datasource
[EZAudio updateScrollHistory:&_scrollHistory
withLength:_scrollHistoryLength
atIndex:&_scrollHistoryIndex
withBuffer:buffer
withBufferSize:bufferSize
isResolutionChanging:&_changingHistorySize];
//
[self setSampleData:_scrollHistory
length:(!_setMaxLength?kEZAudioPlotMaxHistoryBufferLength:_scrollHistoryLength)];
_setMaxLength = YES;
}
else if( _plotType == EZPlotTypeBuffer ){
[self setSampleData:buffer
length:bufferSize];
}
else {
// Unknown plot type
}
}
#if TARGET_OS_IPHONE
- (void)drawRect:(CGRect)rect
{
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSaveGState(ctx);
CGRect frame = self.bounds;
#elif TARGET_OS_MAC
- (void)drawRect:(NSRect)dirtyRect
{
[[NSGraphicsContext currentContext] saveGraphicsState];
NSGraphicsContext * nsGraphicsContext = [NSGraphicsContext currentContext];
CGContextRef ctx = (CGContextRef) [nsGraphicsContext graphicsPort];
NSRect frame = self.bounds;
#endif
#if TARGET_OS_IPHONE
// Set the background color
[(UIColor*)self.backgroundColor set];
UIRectFill(frame);
// Set the waveform line color
[(UIColor*)self.color set];
#elif TARGET_OS_MAC
[(NSColor*)self.backgroundColor set];
NSRectFill(frame);
[(NSColor*)self.color set];
#endif
if(plotLength > 0) {
plotData[plotLength-1] = CGPointMake(plotLength-1,0.0f);
CGMutablePathRef halfPath = CGPathCreateMutable();
CGPathAddLines(halfPath,
NULL,
plotData,
plotLength);
CGMutablePathRef path = CGPathCreateMutable();
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
int deviceOriginFlipped = 1;
#if TARGET_OS_IPHONE
deviceOriginFlipped = -1;
#elif TARGET_OS_MAC
deviceOriginFlipped = 1;
#endif
CGAffineTransform xf = CGAffineTransformIdentity;
xf = CGAffineTransformTranslate( xf, frame.origin.x , halfHeight + frame.origin.y );
xf = CGAffineTransformScale( xf, xscale, deviceOriginFlipped*halfHeight );
CGPathAddPath( path, &xf, halfPath );
if( self.shouldMirror ){
xf = CGAffineTransformIdentity;
xf = CGAffineTransformTranslate( xf, frame.origin.x , halfHeight + frame.origin.y);
xf = CGAffineTransformScale( xf, xscale, -deviceOriginFlipped*(halfHeight));
CGPathAddPath( path, &xf, halfPath );
}
CGPathRelease( halfPath );
// Now, path contains the full waveform path.
CGContextAddPath(ctx, path);
// Make this color customizable
if( self.shouldFill ){
CGContextFillPath(ctx);
}
else {
CGContextStrokePath(ctx);
}
CGPathRelease(path);
}
#if TARGET_OS_IPHONE
CGContextRestoreGState(ctx);
#elif TARGET_OS_MAC
[[NSGraphicsContext currentContext] restoreGraphicsState];
#endif
}
#pragma mark - Adjust Resolution
-(int)setRollingHistoryLength:(int)historyLength {
historyLength = MIN(historyLength,kEZAudioPlotMaxHistoryBufferLength);
size_t floatByteSize = sizeof(float);
_changingHistorySize = YES;
if( _scrollHistoryLength != historyLength ){
_scrollHistoryLength = historyLength;
}
_scrollHistory = realloc(_scrollHistory,_scrollHistoryLength*floatByteSize);
if( _scrollHistoryIndex < _scrollHistoryLength ){
memset(&_scrollHistory[_scrollHistoryIndex],
0,
(_scrollHistoryLength-_scrollHistoryIndex)*floatByteSize);
}
else {
_scrollHistoryIndex = _scrollHistoryLength;
}
_changingHistorySize = NO;
return historyLength;
}
-(int)rollingHistoryLength {
return _scrollHistoryLength;
}
-(void)dealloc {
if( plotData ){
free(plotData);
}
}
@end
+183
View File
@@ -0,0 +1,183 @@
//
// EZAudioPlotGL.h
// EZAudio
//
// Created by Syed Haris Ali on 11/22/13.
// Copyright (c) 2013 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 "TargetConditionals.h"
#import "EZPlot.h"
#if TARGET_OS_IPHONE
#import <GLKit/GLKit.h>
@class EZAudioPlotGLKViewController;
#elif TARGET_OS_MAC
#import <Cocoa/Cocoa.h>
#import <GLKit/GLKit.h>
#import <OpenGL/gl3.h>
#import <QuartzCore/CVDisplayLink.h>
#endif
#pragma mark - Enumerations
/**
Constant drawing types wrapping around the OpenGL equivalents. In the audio drawings the line strip will be the stroked graph while the triangle will provide the filled equivalent.
*/
typedef NS_ENUM(NSUInteger,EZAudioPlotGLDrawType){
/**
* Maps to the OpenGL constant for a line strip, which for the audio graph will correspond to a stroked drawing (no fill).
*/
EZAudioPlotGLDrawTypeLineStrip = GL_LINE_STRIP,
/**
* Maps to the OpenGL constant for a triangle strip, which for the audio graph will correspond to a filled drawing.
*/
EZAudioPlotGLDrawTypeTriangleStrip = GL_TRIANGLE_STRIP
};
#pragma mark - Structures
/**
A structure describing a 2D point (x,y) in space for an audio plot.
*/
typedef struct {
GLfloat x;
GLfloat y;
} EZAudioPlotGLPoint;
/**
EZAudioPlotGL is a subclass of either the EZPlot on iOS or an NSOpenGLView on OSX. I apologize ahead of time for the weirdness in the docs for this class, but I had to do a bit of hackery to get a universal namespace for something works on both iOS and OSX without any additional components. The EZAudioPlotGL provides an the same utilities and interface as the EZAudioPlot with the added benefit of being GPU-accelerated. This is the recommended plot to use on iOS devices to get super fast real-time drawings of audio streams. For the methods and properties below I've included notes on the bottom just indicating which OS they correspond to. In most (if not all) use cases you can just refer to the EZPlot documentation to see which custom properties can be setup. There update function is the same as the EZPlot as well: `updateBuffer:withBufferSize:`
*/
#if TARGET_OS_IPHONE
@interface EZAudioPlotGL : EZPlot
#elif TARGET_OS_MAC
@interface EZAudioPlotGL : NSOpenGLView
#endif
#if TARGET_OS_IPHONE
// Inherited from EZPlot
#elif TARGET_OS_MAC
#pragma mark - Properties
///-----------------------------------------------------------
/// @name Customizing The Plot's Appearance
///-----------------------------------------------------------
/**
The default background color of the plot. For iOS the color is specified as a UIColor while for OSX the color is an NSColor. The default value on both platforms is black.
*/
@property (nonatomic,strong) id backgroundColor;
/**
The default color of the plot's data (i.e. waveform, y-axis values). For iOS the color is specified as a UIColor while for OSX the color is an NSColor. The default value on both platforms is red.
*/
@property (nonatomic,strong) id color;
/**
The plot's gain value, which controls the scale of the y-axis values. The default value of the gain is 1.0f and should always be greater than 0.0f.
*/
@property (nonatomic,assign,setter=setGain:) float gain;
/**
The type of plot as specified by the `EZPlotType` enumeration (i.e. a buffer or rolling plot type).
*/
@property (nonatomic,assign,setter=setPlotType:) EZPlotType plotType;
/**
A BOOL indicating whether or not to fill in the graph. A value of YES will make a filled graph (filling in the space between the x-axis and the y-value), while a value of NO will create a stroked graph (connecting the points along the y-axis).
*/
@property (nonatomic,assign,setter=setShouldFill:) BOOL shouldFill;
/**
A boolean indicating whether the graph should be rotated along the x-axis to give a mirrored reflection. This is typical for audio plots to produce the classic waveform look. A value of YES will produce a mirrored reflection of the y-values about the x-axis, while a value of NO will only plot the y-values.
*/
@property (nonatomic,assign,setter=setShouldMirror:) BOOL shouldMirror;
#pragma mark - Get Samples
///-----------------------------------------------------------
/// @name Updating The Plot
///-----------------------------------------------------------
/**
Updates the plot with the new buffer data and tells the view to redraw itself. Caller will provide a float array with the values they expect to see on the y-axis. The plot will internally handle mapping the x-axis and y-axis to the current view port, any interpolation for fills effects, and mirroring.
@param buffer A float array of values to map to the y-axis.
@param bufferSize The size of the float array that will be mapped to the y-axis.
@warning The bufferSize is expected to be the same, constant value once initial triggered. For plots using OpenGL a vertex buffer object will be allocated with a maximum buffersize of (2 * the initial given buffer size) to account for any interpolation necessary for filling in the graph. Updates use the glBufferSubData(...) function, which will crash if the buffersize exceeds the initial maximum allocated size.
*/
-(void)updateBuffer:(float *)buffer
withBufferSize:(UInt32)bufferSize;
#endif
#pragma mark - Adjust Resolution
///-----------------------------------------------------------
/// @name Adjusting The Resolution
///-----------------------------------------------------------
/**
Sets the length of the rolling history display. Can grow or shrink the display up to the maximum size specified by the kEZAudioPlotMaxHistoryBufferLength macro. Will return the actual set value, which will be either the given value if smaller than the kEZAudioPlotMaxHistoryBufferLength or kEZAudioPlotMaxHistoryBufferLength if a larger value is attempted to be set.
@param historyLength The new length of the rolling history buffer.
@return The new value equal to the historyLength or the kEZAudioPlotMaxHistoryBufferLength.
*/
-(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
///-----------------------------------------------------------
/**
Converts a float array to an array of EZAudioPlotGLPoint structures that hold the (x,y) values the OpenGL buffer needs to properly plot its points.
@param graph A pointer to the array that should hold the EZAudioPlotGLPoint structures.
@param graphSize The size (or length) of the array with the EZAudioPlotGLPoint structures.
@param drawingType The EZAudioPlotGLDrawType constant defining whether the plot should interpolate between points for a triangle strip (filled waveform) or not for a line strip (stroked waveform)
@param buffer The float array holding the audio data
@param bufferSize The size of the float array holding the audio data
@param gain The gain (always greater than 0.0) to apply to the amplitudes (y-values) of the graph. Y-values can only range from -1.0 to 1.0 so any value that's greater will be rounded to -1.0 or 1.0.
*/
+(void)fillGraph:(EZAudioPlotGLPoint*)graph
withGraphSize:(UInt32)graphSize
forDrawingType:(EZAudioPlotGLDrawType)drawingType
withBuffer:(float*)buffer
withBufferSize:(UInt32)bufferSize
withGain:(float)gain;
/**
Determines the proper size of a graph given a EZAudioPlotGLDrawType (line strip or triangle strip) and the size of the incoming buffer. Triangle strips require interpolating between points so the buffer becomes 2*bufferSize
@param drawingType The EZAudioPlotGLDraw type (line strip or triangle strip)
@param bufferSize The size of the float array holding the audio data coming in.
@return A Int32 representing the proper graph size that should be used to account for any necessary interpolating between points.
*/
+(UInt32)graphSizeForDrawingType:(EZAudioPlotGLDrawType)drawingType
withBufferSize:(UInt32)bufferSize;
@end
+771
View File
@@ -0,0 +1,771 @@
//
// EZAudioPlotGL.m
// EZAudio
//
// Created by Syed Haris Ali on 11/22/13.
// Copyright (c) 2013 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 "EZAudioPlotGL.h"
#import "EZAudio.h"
#import "EZAudioPlot.h"
#if TARGET_OS_IPHONE
#import "EZAudioPlotGLKViewController.h"
@interface EZAudioPlotGL ()
@property (nonatomic,strong,readonly) EZAudioPlotGLKViewController *glViewController;
@end
#elif TARGET_OS_MAC
@interface EZAudioPlotGL (){
// Flags indicating whether the plots have been instantiated
BOOL _hasBufferPlotData;
BOOL _hasRollingPlotData;
// Vertex Array Buffers
GLuint _bufferPlotVAB;
GLuint _rollingPlotVAB;
// Vertex Buffer Objects
GLuint _bufferPlotVBO;
GLuint _rollingPlotVBO;
// Display Link
CVDisplayLinkRef _displayLink;
// Buffers size
UInt32 _bufferPlotGraphSize;
UInt32 _rollingPlotGraphSize;
// Rolling History
BOOL _setMaxLength;
float *_scrollHistory;
int _scrollHistoryIndex;
UInt32 _scrollHistoryLength;
BOOL _changingHistorySize;
// Copied buffer data
float *_copiedBuffer;
UInt32 _copiedBufferSize;
}
@property (nonatomic,assign,readonly) EZAudioPlotGLDrawType drawingType;
@property (nonatomic,strong) GLKBaseEffect *baseEffect;
@end
#endif
@implementation EZAudioPlotGL
#if TARGET_OS_IPHONE
@synthesize glViewController = _glViewController;
#elif TARGET_OS_MAC
@synthesize baseEffect = _baseEffect;
#endif
@synthesize backgroundColor = _backgroundColor;
@synthesize color = _color;
@synthesize gain = _gain;
@synthesize plotType = _plotType;
@synthesize shouldFill = _shouldFill;
@synthesize shouldMirror = _shouldMirror;
#pragma mark - Initialization
-(id)init
{
self = [super init];
if (self) {
[self initializeView];
}
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if(self){
[self initializeView];
}
return self;
}
-(id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self initializeView];
}
return self;
}
#pragma mark - Initialize Properties Here
-(void)initializeView {
#if TARGET_OS_IPHONE
// Initialize the subview controller
_glViewController = [[EZAudioPlotGLKViewController alloc] init];
_glViewController.view.frame = self.bounds;
[self insertSubview:self.glViewController.view atIndex:0];
#elif TARGET_OS_MAC
_copiedBuffer = NULL;
#endif
// Set the default properties
self.gain = 1.0;
self.plotType = EZPlotTypeBuffer;
#if TARGET_OS_IPHONE
self.backgroundColor = [UIColor colorWithRed:0.796 green:0.749 blue:0.663 alpha:1];
self.color = [UIColor colorWithRed:0.481 green:0.548 blue:0.637 alpha:1];
#elif TARGET_OS_MAC
_scrollHistory = NULL;
_scrollHistoryLength = kEZAudioPlotDefaultHistoryBufferLength;
#endif
self.shouldFill = NO;
self.shouldMirror = NO;
}
#pragma mark - Setters
-(void)setBackgroundColor:(id)backgroundColor {
_backgroundColor = backgroundColor;
#if TARGET_OS_IPHONE
self.glViewController.backgroundColor = backgroundColor;
#elif TARGET_OS_MAC
[self _refreshWithBackgroundColor:backgroundColor];
#endif
}
-(void)setColor:(id)color {
_color = color;
#if TARGET_OS_IPHONE
self.glViewController.color = color;
#elif TARGET_OS_MAC
[self _refreshWithColor:color];
#endif
}
#if TARGET_OS_IPHONE
#elif TARGET_OS_MAC
-(void)setDrawingType:(EZAudioPlotGLDrawType)drawingType {
CGLLockContext([[self openGLContext] CGLContextObj]);
_drawingType = drawingType;
CGLUnlockContext([[self openGLContext] CGLContextObj]);
}
#endif
#if TARGET_OS_IPHONE
-(void)setGain:(float)gain {
_gain = gain;
self.glViewController.gain = gain;
}
#elif TARGET_OS_MAC
// Gain changed mac
#endif
#if TARGET_OS_IPHONE
-(void)setPlotType:(EZPlotType)plotType {
_plotType = plotType;
self.glViewController.plotType = plotType;
}
#elif TARGET_OS_MAC
// Plot type changed mac
#endif
-(void)setShouldFill:(BOOL)shouldFill {
_shouldFill = shouldFill;
#if TARGET_OS_IPHONE
self.glViewController.drawingType = shouldFill ? EZAudioPlotGLDrawTypeTriangleStrip : EZAudioPlotGLDrawTypeLineStrip;
#elif TARGET_OS_MAC
// Fill flag changed mac
self.drawingType = shouldFill ? EZAudioPlotGLDrawTypeTriangleStrip : EZAudioPlotGLDrawTypeLineStrip;
#endif
}
#if TARGET_OS_IPHONE
-(void)setShouldMirror:(BOOL)shouldMirror {
_shouldMirror = shouldMirror;
self.glViewController.shouldMirror = shouldMirror;
}
#elif TARGET_OS_MAC
// Mirror flag changed mac
#endif
#pragma mark - Get Samples
-(void)updateBuffer:(float *)buffer
withBufferSize:(UInt32)bufferSize {
#if TARGET_OS_IPHONE
[self.glViewController updateBuffer:buffer
withBufferSize:bufferSize];
#elif TARGET_OS_MAC
if( _copiedBuffer == NULL ){
_copiedBuffer = (float*)malloc(bufferSize*sizeof(float));
}
_copiedBufferSize = bufferSize;
// Copy the buffer
memcpy(_copiedBuffer,
buffer,
bufferSize*sizeof(float));
// Draw based on plot type
switch(_plotType) {
case EZPlotTypeBuffer:
[self _updateBufferPlotBufferWithAudioReceived:_copiedBuffer
withBufferSize:_copiedBufferSize];
break;
case EZPlotTypeRolling:
[self _updateRollingPlotBufferWithAudioReceived:_copiedBuffer
withBufferSize:_copiedBufferSize];
break;
default:
break;
}
#endif
}
#pragma mark - OSX Specific GL Implementation
#if TARGET_OS_IPHONE
// Handled by the embedded GLKViewController
#elif TARGET_OS_MAC
#pragma mark - Awake
-(void)awakeFromNib {
// Setup the base effect
[self _setupBaseEffect];
// Setup the OpenGL Pixel Format and Context
[self _setupProfile];
// Setup view
[self _setupView];
}
-(void)_setupBaseEffect {
self.baseEffect = [[GLKBaseEffect alloc] init];
self.baseEffect.useConstantColor = GL_TRUE;
self.baseEffect.constantColor = GLKVector4Make(0.489, 0.34, 0.185, 1.0);
}
-(void)_setupProfile {
NSOpenGLPixelFormatAttribute attrs[] =
{
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAMultisample,
NSOpenGLPFASampleBuffers, 1,
NSOpenGLPFASamples, 4,
NSOpenGLPFADepthSize, 24,
NSOpenGLPFAOpenGLProfile,
NSOpenGLProfileVersion3_2Core, 0
};
NSOpenGLPixelFormat *pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
if (!pf)
{
NSLog(@"No OpenGL pixel format");
}
NSOpenGLContext* context = [[NSOpenGLContext alloc] initWithFormat:pf shareContext:nil];
// Debug only
CGLEnable([context CGLContextObj], kCGLCECrashOnRemovedFunctions);
self.pixelFormat = pf;
self.openGLContext = context;
}
-(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];
}
#pragma mark - Prepare
-(void)prepareOpenGL {
[super prepareOpenGL];
GLint swapInt = 1;
[self.openGLContext setValues:&swapInt forParameter:NSOpenGLCPSwapInterval];
////////////////////////////////////////////////////////////////////////////
// Setup VABs and VBOs //
////////////////////////////////////////////////////////////////////////////
// Buffer
glGenVertexArrays(1,&_bufferPlotVAB);
glBindVertexArray(_bufferPlotVAB);
glGenBuffers(1,&_bufferPlotVBO);
glBindBuffer(GL_ARRAY_BUFFER,_bufferPlotVBO);
// Rolling
glGenVertexArrays(1,&_rollingPlotVAB);
glBindVertexArray(_rollingPlotVAB);
glGenBuffers(1,&_rollingPlotVBO);
glBindBuffer(GL_ARRAY_BUFFER,_rollingPlotVBO);
if( self.shouldFill ){
glBindVertexArray(_rollingPlotVAB);
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];
// Setup the display link (rendering loop)
[self _setupDisplayLink];
}
-(void)_setupDisplayLink {
// Create a display link capable of being used with all active displays
CVDisplayLinkCreateWithActiveCGDisplays(&_displayLink);
// Set the renderer output callback function
CVDisplayLinkSetOutputCallback(_displayLink, &DisplayLinkCallback, (__bridge void *)(self));
// Set the display link for the current renderer
CGLContextObj cglContext = self.openGLContext.CGLContextObj;
CGLPixelFormatObj cglPixelFormat = self.pixelFormat.CGLPixelFormatObj;
CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(_displayLink, cglContext, cglPixelFormat);
// Activate the display link
CVDisplayLinkStart(_displayLink);
// Register to be notified when the window closes so we can stop the displaylink
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(windowWillClose:)
name:NSWindowWillCloseNotification
object:[self window]];
}
- (void) windowWillClose:(NSNotification*)notification
{
// Stop the display link when the window is closing because default
// OpenGL render buffers will be destroyed. If display link continues to
// fire without renderbuffers, OpenGL draw calls will set errors.
CVDisplayLinkStop(_displayLink);
}
#pragma mark - Display Link Callback
// This is the renderer output callback function
static CVReturn DisplayLinkCallback(CVDisplayLinkRef displayLink,
const CVTimeStamp* now,
const CVTimeStamp* outputTime,
CVOptionFlags flagsIn,
CVOptionFlags* flagsOut,
void* displayLinkContext)
{
CVReturn result = [(__bridge EZAudioPlotGL*)displayLinkContext getFrameForTime:outputTime];
return result;
}
- (CVReturn)getFrameForTime:(const CVTimeStamp*)outputTime
{
@autoreleasepool {
[self drawFrame];
}
return kCVReturnSuccess;
}
#pragma mark - Buffer Updating By Type
-(void)_updateBufferPlotBufferWithAudioReceived:(float*)buffer
withBufferSize:(UInt32)bufferSize {
// Lock
CGLLockContext([[self openGLContext] CGLContextObj]);
// Bind to buffer VBO
glBindVertexArray(_bufferPlotVAB);
glBindBuffer(GL_ARRAY_BUFFER,_bufferPlotVBO);
// 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( !_hasBufferPlotData && _drawingType == EZAudioPlotGLDrawTypeLineStrip ){
EZAudioPlotGLPoint maxGraph[2*bufferSize];
glBufferData(GL_ARRAY_BUFFER, sizeof(maxGraph), maxGraph, GL_STREAM_DRAW );
_hasBufferPlotData = YES;
}
// Setup the buffer plot's graph size
_bufferPlotGraphSize = [EZAudioPlotGL graphSizeForDrawingType:_drawingType
withBufferSize:bufferSize];
// Setup the graph
EZAudioPlotGLPoint graph[_bufferPlotGraphSize];
// Fill in graph data
[EZAudioPlotGL fillGraph:graph
withGraphSize:_bufferPlotGraphSize
forDrawingType:_drawingType
withBuffer:buffer
withBufferSize:bufferSize
withGain:self.gain];
// Update the drawing
if( !_hasBufferPlotData ){
glBufferData(GL_ARRAY_BUFFER, sizeof(graph) , graph, GL_STREAM_DRAW);
_hasBufferPlotData = YES;
}
else {
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(graph), graph);
}
// Unlock
CGLUnlockContext([[self openGLContext] CGLContextObj]);
}
-(void)_updateRollingPlotBufferWithAudioReceived:(float*)buffer
withBufferSize:(UInt32)bufferSize {
// Lock
CGLLockContext([[self openGLContext] CGLContextObj]);
// Bind to rolling VBO
glBindVertexArray(_rollingPlotVAB);
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 ){
EZAudioPlotGLPoint maxGraph[2*kEZAudioPlotMaxHistoryBufferLength];
glBufferData(GL_ARRAY_BUFFER, sizeof(maxGraph), maxGraph, GL_STREAM_DRAW );
_hasRollingPlotData = YES;
}
// Setup the plot
_rollingPlotGraphSize = [EZAudioPlotGL graphSizeForDrawingType:_drawingType
withBufferSize:_scrollHistoryLength];
// Fill the graph with data
EZAudioPlotGLPoint graph[_rollingPlotGraphSize];
// Update the scroll history datasource
[EZAudio updateScrollHistory:&_scrollHistory
withLength:_scrollHistoryLength
atIndex:&_scrollHistoryIndex
withBuffer:buffer
withBufferSize:bufferSize
isResolutionChanging:&_changingHistorySize];
// Fill in graph data
[EZAudioPlotGL fillGraph:graph
withGraphSize:_rollingPlotGraphSize
forDrawingType:_drawingType
withBuffer:_scrollHistory
withBufferSize:_scrollHistoryLength
withGain:self.gain];
// Update the drawing
if( !_hasRollingPlotData ){
glBufferData(GL_ARRAY_BUFFER, sizeof(graph), graph, GL_STREAM_DRAW);
_hasRollingPlotData = YES;
}
else {
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(graph), graph);
}
// Unlock
CGLUnlockContext([[self openGLContext] CGLContextObj]);
}
//#pragma mark - Render
-(void)drawFrame {
// Avoid flickering during resize by drawing
[[self openGLContext] makeCurrentContext];
// Lock
CGLLockContext([[self openGLContext] CGLContextObj]);
// Draw frame
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
if( _hasBufferPlotData || _hasRollingPlotData ){
// Plot either a buffer plot or a rolling plot
switch(_plotType) {
case EZPlotTypeBuffer:
[self _drawBufferPlot];
break;
case EZPlotTypeRolling:
[self _drawRollingPlot];
break;
default:
break;
}
}
// Flush and unlock
CGLFlushDrawable([[self openGLContext] CGLContextObj]);
CGLUnlockContext([[self openGLContext] CGLContextObj]);
}
-(void)_drawBufferPlot {
glBindVertexArray(_bufferPlotVAB);
glBindBuffer(GL_ARRAY_BUFFER,_bufferPlotVBO);
[self.baseEffect prepareToDraw];
self.baseEffect.transform.modelviewMatrix = GLKMatrix4MakeXRotation(0);
// Enable the vertex data
glEnableVertexAttribArray(GLKVertexAttribPosition);
// Define the vertex data size & layout
glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, sizeof(EZAudioPlotGLPoint), NULL);
// Draw the triangle
glDrawArrays(_drawingType,0,_bufferPlotGraphSize);
// Mirrored
if( self.shouldMirror ){
[self.baseEffect prepareToDraw];
self.baseEffect.transform.modelviewMatrix = GLKMatrix4MakeXRotation(M_PI);
// Enable the vertex data
glEnableVertexAttribArray(GLKVertexAttribPosition);
// Define the vertex data size & layout
glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, sizeof(EZAudioPlotGLPoint), NULL);
// Draw the triangle
glDrawArrays(_drawingType, 0, _bufferPlotGraphSize);
}
}
-(void)_drawRollingPlot {
glBindVertexArray(_rollingPlotVAB);
glBindBuffer(GL_ARRAY_BUFFER,_rollingPlotVBO);
[self.baseEffect prepareToDraw];
self.baseEffect.transform.modelviewMatrix = GLKMatrix4MakeXRotation(0);
// Enable the vertex data
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);
// Mirrored
if( self.shouldMirror ){
[self.baseEffect prepareToDraw];
self.baseEffect.transform.modelviewMatrix = GLKMatrix4MakeXRotation(M_PI);
// Enable the vertex data
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);
}
}
-(void)drawRect:(NSRect)dirtyRect {
[self drawFrame];
}
#pragma mark - Reshape
-(void)reshape {
[super reshape];
// We draw on a secondary thread through the display link. However, when
// resizing the view, -drawRect is called on the main thread.
// Add a mutex around to avoid the threads accessing the context
// simultaneously when resizing.
CGLLockContext([[self openGLContext] CGLContextObj]);
// Get the view size in Points
NSRect viewRectPoints = [self bounds];
NSRect viewRectPixels = [self convertRectToBacking:viewRectPoints];
// Set the new dimensions in our renderer
glViewport(0, 0, viewRectPixels.size.width, viewRectPixels.size.height);
CGLUnlockContext([[self openGLContext] CGLContextObj]);
}
#pragma mark - Private Setters
-(void)_refreshWithBackgroundColor:(NSColor*)backgroundColor {
CGLLockContext([[self openGLContext] CGLContextObj]);
// Extract colors
CGFloat red; CGFloat green; CGFloat blue; CGFloat alpha;
[backgroundColor getRed:&red
green:&green
blue:&blue
alpha:&alpha];
// Set them on the context
glClearColor((GLclampf)red,(GLclampf)green,(GLclampf)blue,(GLclampf)alpha);
CGLUnlockContext([[self openGLContext] CGLContextObj]);
}
-(void)_refreshWithColor:(NSColor*)color {
CGLLockContext([[self openGLContext] CGLContextObj]);
// Extract colors
CGFloat red; CGFloat green; CGFloat blue; CGFloat alpha;
[color getRed:&red
green:&green
blue:&blue
alpha:&alpha];
// Set them on the base shader
self.baseEffect.constantColor = GLKVector4Make((GLclampf)red,(GLclampf)green,(GLclampf)blue,(GLclampf)alpha);
CGLUnlockContext([[self openGLContext] CGLContextObj]);
}
#pragma mark - Cleanup
- (void) dealloc
{
// Stop the display link BEFORE releasing anything in the view
// otherwise the display link thread may call into the view and crash
// when it encounters something that has been release
CVDisplayLinkStop(_displayLink);
CVDisplayLinkRelease(_displayLink);
if( _copiedBuffer != NULL ){
free( _copiedBuffer );
}
}
#endif
#pragma mark - Adjust Resolution
-(int)setRollingHistoryLength:(int)historyLength {
#if TARGET_OS_IPHONE
int result = [self.glViewController setRollingHistoryLength:historyLength];
return result;
#elif TARGET_OS_MAC
historyLength = MIN(historyLength,kEZAudioPlotMaxHistoryBufferLength);
size_t floatByteSize = sizeof(float);
_changingHistorySize = YES;
if( _scrollHistoryLength != historyLength ){
_scrollHistoryLength = historyLength;
}
_scrollHistory = realloc(_scrollHistory,_scrollHistoryLength*floatByteSize);
if( _scrollHistoryIndex < _scrollHistoryLength ){
memset(&_scrollHistory[_scrollHistoryIndex],
0,
(_scrollHistoryLength-_scrollHistoryIndex)*floatByteSize);
}
else {
_scrollHistoryIndex = _scrollHistoryLength;
}
_changingHistorySize = NO;
return historyLength;
#endif
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
forDrawingType:(EZAudioPlotGLDrawType)drawingType
withBuffer:(float*)buffer
withBufferSize:(UInt32)bufferSize
withGain:(float)gain {
if( drawingType == EZAudioPlotGLDrawTypeLineStrip ){
// graph size = buffer size to stroke waveform
for(int i = 0; i < graphSize; i++){
float x = [EZAudio MAP:i
leftMin:0
leftMax:bufferSize
rightMin:-1.0
rightMax:1.0];
graph[i].x = x;
graph[i].y = gain*buffer[i];
}
}
else if( drawingType == EZAudioPlotGLDrawTypeTriangleStrip ) {
// graph size = 2 * buffer size to draw triangles and fill regions properly
for(int i = 0; i < graphSize; i+=2){
int bufferIndex = (int)[EZAudio MAP:i
leftMin:0
leftMax:graphSize
rightMin:0
rightMax:bufferSize];
float x = [EZAudio MAP:bufferIndex
leftMin:0
leftMax:bufferSize
rightMin:-1.0
rightMax:1.0];
graph[i].x = x;
graph[i].y = 0.0f;
}
for(int i = 0; i < graphSize; i+=2){
int bufferIndex = (int)[EZAudio
MAP:i
leftMin:0
leftMax:graphSize
rightMin:0
rightMax:bufferSize];
float x = [EZAudio MAP:bufferIndex
leftMin:0
leftMax:bufferSize
rightMin:-1.0
rightMax:1.0];
graph[i+1].x = x;
graph[i+1].y = gain*buffer[bufferIndex];
}
}
}
+(UInt32)graphSizeForDrawingType:(EZAudioPlotGLDrawType)drawingType
withBufferSize:(UInt32)bufferSize {
UInt32 graphSize = bufferSize;
switch(drawingType) {
case EZAudioPlotGLDrawTypeLineStrip:
graphSize = bufferSize;
break;
case EZAudioPlotGLDrawTypeTriangleStrip:
graphSize = 2*bufferSize;
break;
default:
break;
}
return graphSize;
}
@end
+127
View File
@@ -0,0 +1,127 @@
//
// EZAudioPlotGLKViewController.h
// EZAudio
//
// Created by Syed Haris Ali on 11/22/13.
// Copyright (c) 2013 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 "TargetConditionals.h"
#if TARGET_OS_IPHONE
#import "EZAudioPlotGL.h"
@class EZAudio;
/**
EZAudioPlotGLKViewController is a subclass of the GLKViewController and handles the OpenGL drawing routine for iOS OpenGL ES views. This class has not been used outside the scope of the EZAudioPlotGL, but should be safe to use by itself if the intended use case is to have a view controller take up the whole screen.
*/
@interface EZAudioPlotGLKViewController : GLKViewController
#pragma mark - Properties
///-----------------------------------------------------------
/// @name Customizing The Plot's Appearance
///-----------------------------------------------------------
/**
The default background color of the plot. For iOS the color is specified as a UIColor while for OSX the color is an NSColor. The default value on both platforms is black.
*/
@property (nonatomic,strong) UIColor *backgroundColor;
/**
The default shader to use to fill the graph.
*/
@property (nonatomic,strong) GLKBaseEffect *baseEffect;
/**
The default color of the plot's data (i.e. waveform, y-axis values). For iOS the color is specified as a UIColor while for OSX the color is an NSColor. The default value on both platforms is red.
*/
@property (nonatomic,strong) UIColor *color;
/**
The OpenGL ES context (EAGLContext) in which to perform the drawing
*/
@property (nonatomic,strong) EAGLContext *context;
/**
The EZAudioPlotGLDrawType specifying which OpenGL primitive to use for drawing (either line strip for stroke and no fill or triangle strip for fill)
*/
@property (nonatomic,assign) EZAudioPlotGLDrawType drawingType;
/**
The plot's gain value, which controls the scale of the y-axis values. The default value of the gain is 1.0f and should always be greater than 0.0f.
*/
@property (nonatomic,assign,setter=setGain:) float gain;
/**
The type of plot as specified by the `EZPlotType` enumeration (i.e. a buffer or rolling plot type).
*/
@property (nonatomic,assign,setter=setPlotType:) EZPlotType plotType;
/**
A boolean indicating whether the graph should be rotated along the x-axis to give a mirrored reflection. This is typical for audio plots to produce the classic waveform look. A value of YES will produce a mirrored reflection of the y-values about the x-axis, while a value of NO will only plot the y-values.
*/
@property (nonatomic,assign,setter=setShouldMirror:) BOOL shouldMirror;
#pragma mark - Adjust Resolution
///-----------------------------------------------------------
/// @name Adjusting The Resolution
///-----------------------------------------------------------
/**
Sets the length of the rolling history display. Can grow or shrink the display up to the maximum size specified by the kEZAudioPlotMaxHistoryBufferLength macro. Will return the actual set value, which will be either the given value if smaller than the kEZAudioPlotMaxHistoryBufferLength or kEZAudioPlotMaxHistoryBufferLength if a larger value is attempted to be set.
@param historyLength The new length of the rolling history buffer.
@return The new value equal to the historyLength or the kEZAudioPlotMaxHistoryBufferLength.
*/
-(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
///-----------------------------------------------------------
/**
Updates the plot with the new buffer data and tells the view to redraw itself. Caller will provide a float array with the values they expect to see on the y-axis. The plot will internally handle mapping the x-axis and y-axis to the current view port, any interpolation for fills effects, and mirroring.
@param buffer A float array of values to map to the y-axis.
@param bufferSize The size of the float array that will be mapped to the y-axis.
@warning The bufferSize is expected to be the same, constant value once initial triggered. For plots using OpenGL a vertex buffer object will be allocated with a maximum buffersize of (2 * the initial given buffer size) to account for any interpolation necessary for filling in the graph. Updates use the glBufferSubData(...) function, which will crash if the buffersize exceeds the initial maximum allocated size.
*/
-(void)updateBuffer:(float *)buffer
withBufferSize:(UInt32)bufferSize;
@end
#elif TARGET_OS_MAC
#endif
+483
View File
@@ -0,0 +1,483 @@
//
// EZAudioPlotGLKViewController.m
// EZAudio
//
// Created by Syed Haris Ali on 11/22/13.
// Copyright (c) 2013 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.
#if TARGET_OS_IPHONE
#import "EZAudioPlotGLKViewController.h"
#import "EZAudio.h"
@interface EZAudioPlotGLKViewController () {
// Flags indicating whether the plots have been instantiated
BOOL _hasBufferPlotData;
BOOL _hasRollingPlotData;
// The buffers
GLuint _bufferPlotVBO;
GLuint _rollingPlotVBO;
// Buffers size
UInt32 _bufferPlotGraphSize;
UInt32 _rollingPlotGraphSize;
// Rolling History
BOOL _setMaxLength;
float *_scrollHistory;
int _scrollHistoryIndex;
UInt32 _scrollHistoryLength;
BOOL _changingHistorySize;
}
@end
@implementation EZAudioPlotGLKViewController
@synthesize baseEffect = _baseEffect;
@synthesize context = _context;
@synthesize drawingType = _drawingType;
@synthesize plotType = _plotType;
@synthesize shouldMirror = _shouldMirror;
#pragma mark - Initialization
-(id)init
{
self = [super init];
if (self) {
[self initializeView];
}
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if(self){
[self initializeView];
}
return self;
}
-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if(self){
[self initializeView];
}
return self;
}
#pragma mark - Initialize Properties Here
-(void)initializeView {
// Setup the base effect
self.baseEffect = [[GLKBaseEffect alloc] init];
self.baseEffect.useConstantColor = GL_TRUE;
self.preferredFramesPerSecond = 60;
_scrollHistory = NULL;
_scrollHistoryLength = kEZAudioPlotDefaultHistoryBufferLength;
}
#pragma mark - View Did Load
-(void)viewDidLoad {
[super viewDidLoad];
// Setup the context
if( ![EAGLContext currentContext] )
{
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
}
else
{
self.context = [EAGLContext currentContext];
}
if (!self.context) {
NSLog(@"Failed to create ES context");
}
else {
EAGLContext.currentContext = self.context;
}
// Set the view's context
GLKView *view = (GLKView *)self.view;
view.context = self.context;
view.drawableMultisample = GLKViewDrawableMultisample4X;
// Generate both the buffer id references
glGenBuffers(1, &_bufferPlotVBO);
glGenBuffers(1, &_rollingPlotVBO);
// Refresh color values
[self _refreshWithBackgroundColor: self.backgroundColor];
[self _refreshWithColor: self.color];
// Set the line width for the context
glLineWidth(2.0);
}
#pragma mark - Adjust Resolution
-(int)setRollingHistoryLength:(int)historyLength {
_changingHistorySize = YES;
historyLength = MIN(historyLength,kEZAudioPlotMaxHistoryBufferLength);
size_t floatByteSize = sizeof(float);
if( _scrollHistoryLength != historyLength ){
_scrollHistoryLength = historyLength;
}
_scrollHistory = realloc(_scrollHistory,_scrollHistoryLength*floatByteSize);
if( _scrollHistoryIndex < _scrollHistoryLength ){
memset(&_scrollHistory[_scrollHistoryIndex],
0,
(_scrollHistoryLength-_scrollHistoryIndex)*floatByteSize);
}
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 {
// 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:
[self _updateBufferPlotBufferWithAudioReceived:buffer
withBufferSize:bufferSize];
break;
case EZPlotTypeRolling:
[self _updateRollingPlotBufferWithAudioReceived:buffer
withBufferSize:bufferSize];
break;
default:
break;
}
}
#pragma mark - Buffer Updating By Type
-(void)_updateBufferPlotBufferWithAudioReceived:(float*)buffer
withBufferSize:(UInt32)bufferSize {
glBindBuffer(GL_ARRAY_BUFFER, _bufferPlotVBO);
// 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( !_hasBufferPlotData && _drawingType == EZAudioPlotGLDrawTypeLineStrip ){
EZAudioPlotGLPoint maxGraph[2*bufferSize];
glBufferData(GL_ARRAY_BUFFER, sizeof(maxGraph), maxGraph, GL_STREAM_DRAW );
_hasBufferPlotData = YES;
}
// Setup the buffer plot's graph size
_bufferPlotGraphSize = [EZAudioPlotGL graphSizeForDrawingType:_drawingType
withBufferSize:bufferSize];
// Setup the graph
EZAudioPlotGLPoint graph[_bufferPlotGraphSize];
// Fill in graph data
[EZAudioPlotGL fillGraph:graph
withGraphSize:_bufferPlotGraphSize
forDrawingType:_drawingType
withBuffer:buffer
withBufferSize:bufferSize
withGain:self.gain];
if( !_hasBufferPlotData ){
glBufferData( GL_ARRAY_BUFFER, sizeof(graph), graph, GL_STREAM_DRAW );
_hasBufferPlotData = YES;
}
else {
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(graph), graph);
}
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
-(void)_updateRollingPlotBufferWithAudioReceived:(float*)buffer
withBufferSize:(UInt32)bufferSize {
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 ){
EZAudioPlotGLPoint maxGraph[2*kEZAudioPlotMaxHistoryBufferLength];
glBufferData( GL_ARRAY_BUFFER, sizeof(maxGraph), maxGraph, GL_STREAM_DRAW );
_hasRollingPlotData = YES;
}
// Setup the plot
_rollingPlotGraphSize = [EZAudioPlotGL graphSizeForDrawingType:_drawingType
withBufferSize:_scrollHistoryLength];
// Fill the graph with data
EZAudioPlotGLPoint graph[_rollingPlotGraphSize];
// Update the scroll history datasource
[EZAudio updateScrollHistory:&_scrollHistory
withLength:_scrollHistoryLength
atIndex:&_scrollHistoryIndex
withBuffer:buffer
withBufferSize:bufferSize
isResolutionChanging:&_changingHistorySize];
// Fill in graph data
[EZAudioPlotGL fillGraph:graph
withGraphSize:_rollingPlotGraphSize
forDrawingType:_drawingType
withBuffer:_scrollHistory
withBufferSize:_scrollHistoryLength
withGain:self.gain];
// Update the drawing
if( !_hasRollingPlotData ){
glBufferData( GL_ARRAY_BUFFER, sizeof(graph) , graph, GL_STREAM_DRAW );
_hasRollingPlotData = YES;
}
else {
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);
if( _hasBufferPlotData || _hasRollingPlotData ){
// Prepare the effect for drawing
[self.baseEffect prepareToDraw];
// Plot either a buffer plot or a rolling plot
switch(_plotType) {
case EZPlotTypeBuffer:
[self _drawBufferPlotWithView:view
drawInRect:rect];
break;
case EZPlotTypeRolling:
[self _drawRollingPlotWithView:view
drawInRect:rect];
break;
default:
break;
}
}
}
#pragma mark - Private Drawing
-(void)_drawBufferPlotWithView:(GLKView*)view drawInRect:(CGRect)rect {
if( _hasBufferPlotData ){
glBindBuffer(GL_ARRAY_BUFFER, _bufferPlotVBO);
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, sizeof(EZAudioPlotGLPoint), NULL);
// Normal plot
self.baseEffect.transform.modelviewMatrix = GLKMatrix4MakeXRotation(0);
glDrawArrays(_drawingType, 0, _bufferPlotGraphSize);
if( self.shouldMirror ){
// Mirrored plot
[self.baseEffect prepareToDraw];
self.baseEffect.transform.modelviewMatrix = GLKMatrix4MakeXRotation(M_PI);
glDrawArrays(_drawingType, 0, _bufferPlotGraphSize);
}
glBindBuffer(GL_ARRAY_BUFFER,0);
}
}
-(void)_drawRollingPlotWithView:(GLKView*)view drawInRect:(CGRect)rect {
if( _hasRollingPlotData ){
// Normal plot
glBindBuffer(GL_ARRAY_BUFFER, _rollingPlotVBO);
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, sizeof(EZAudioPlotGLPoint), NULL);
// Normal plot
self.baseEffect.transform.modelviewMatrix = GLKMatrix4MakeXRotation(0);
glDrawArrays(_drawingType, 0, _rollingPlotGraphSize);
if( self.shouldMirror ){
// Mirrored plot
[self.baseEffect prepareToDraw];
self.baseEffect.transform.modelviewMatrix = GLKMatrix4MakeXRotation(3.14159265359);
glDrawArrays(_drawingType, 0, _rollingPlotGraphSize);
}
glBindBuffer(GL_ARRAY_BUFFER,0);
}
}
#pragma mark - Setters
-(void)setBackgroundColor:(UIColor *)backgroundColor {
// Set the background color
_backgroundColor = backgroundColor;
// Refresh background color (map to GL vector)
[self _refreshWithBackgroundColor:backgroundColor];
}
-(void)setColor:(UIColor *)color {
// Set the color
_color = color;
// Refresh the color (map to GL vector)
[self _refreshWithColor:color];
}
#pragma mark - Private Setters
-(void)_refreshWithBackgroundColor:(UIColor*)backgroundColor {
// Extract colors
CGFloat red; CGFloat green; CGFloat blue; CGFloat alpha;
[backgroundColor getRed:&red
green:&green
blue:&blue
alpha:&alpha];
// Set them on the context
glClearColor((GLclampf)red,(GLclampf)green,(GLclampf)blue,(GLclampf)alpha);
}
-(void)_refreshWithColor:(UIColor*)color {
// Extract colors
CGFloat red; CGFloat green; CGFloat blue; CGFloat alpha;
[color getRed:&red
green:&green
blue:&blue
alpha:&alpha];
// Set them on the base shader
self.baseEffect.constantColor = GLKVector4Make((GLclampf)red,(GLclampf)green,(GLclampf)blue,(GLclampf)alpha);
}
@end
#elif TARGET_OS_MAC
#endif
+35
View File
@@ -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
+74
View File
@@ -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
@@ -3,7 +3,7 @@
// EZAudio
//
// Created by Syed Haris Ali on 9/2/13.
// Copyright (c) 2015 Syed Haris Ali. All rights reserved.
// Copyright (c) 2013 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
@@ -23,20 +23,16 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
#import "TargetConditionals.h"
#import "EZAudioDevice.h"
#import "EZOutput.h"
#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
#import "TargetConditionals.h"
@class EZAudio;
@class EZMicrophone;
//------------------------------------------------------------------------------
#pragma mark - EZMicrophoneDelegate
//------------------------------------------------------------------------------
/**
The EZMicrophoneDelegate for the EZMicrophone provides a receiver for the incoming audio data events. When the microphone has been successfully internally configured it will try to send its delegate an AudioStreamBasicDescription describing the format of the incoming audio data.
The delegate for the EZMicrophone provides a receiver for the incoming audio data events. When the microphone has been successfully internally configured it will try to send its delegate an AudioStreamBasicDescription describing the format of the incoming audio data.
The audio data itself is sent back to the delegate in various forms:
@@ -54,41 +50,30 @@
/// @name Audio Data Description
///-----------------------------------------------------------
/**
Called anytime the input device changes on an `EZMicrophone` instance.
@param microphone The instance of the EZMicrophone that triggered the event.
@param device The instance of the new EZAudioDevice the microphone is using to pull input.
*/
- (void)microphone:(EZMicrophone *)microphone changedDevice:(EZAudioDevice *)device;
//------------------------------------------------------------------------------
/**
Returns back the audio stream basic description as soon as it has been initialized. This is guaranteed to occur before the stream callbacks, `microphone:hasBufferList:withBufferSize:withNumberOfChannels:` or `microphone:hasAudioReceived:withBufferSize:withNumberOfChannels:`
@param microphone The instance of the EZMicrophone that triggered the event.
@param audioStreamBasicDescription The AudioStreamBasicDescription that was created for the microphone instance.
*/
- (void) microphone:(EZMicrophone *)microphone
hasAudioStreamBasicDescription:(AudioStreamBasicDescription)audioStreamBasicDescription;
-(void) microphone:(EZMicrophone *)microphone
hasAudioStreamBasicDescription:(AudioStreamBasicDescription)audioStreamBasicDescription;
///-----------------------------------------------------------
/// @name Audio Data Callbacks
///-----------------------------------------------------------
/**
This method provides an array of float arrays of the audio received, each float array representing a channel of audio data This occurs on the background thread so any drawing code must explicity perform its functions on the main thread.
Returns back a float array of the audio received. This occurs on the background thread so any drawing code must explicity perform its functions on the main thread.
@param microphone The instance of the EZMicrophone that triggered the event.
@param buffer The audio data as an array of float arrays. In a stereo signal buffer[0] represents the left channel while buffer[1] would represent the right channel.
@param bufferSize The size of each of the buffers (the length of each float array).
@param numberOfChannels The number of channels for the incoming audio.
@warning This function executes on a background thread to avoid blocking any audio operations. If operations should be performed on any other thread (like the main thread) it should be performed within a dispatch block like so: dispatch_async(dispatch_get_main_queue(), ^{ ...Your Code... })
*/
- (void) microphone:(EZMicrophone *)microphone
hasAudioReceived:(float **)buffer
withBufferSize:(UInt32)bufferSize
withNumberOfChannels:(UInt32)numberOfChannels;
//------------------------------------------------------------------------------
-(void) microphone:(EZMicrophone*)microphone
hasAudioReceived:(float**)buffer
withBufferSize:(UInt32)bufferSize
withNumberOfChannels:(UInt32)numberOfChannels;
/**
Returns back the buffer list containing the audio received. This occurs on the background thread so any drawing code must explicity perform its functions on the main thread.
@@ -98,69 +83,40 @@
@param numberOfChannels The number of channels for the incoming audio.
@warning This function executes on a background thread to avoid blocking any audio operations. If operations should be performed on any other thread (like the main thread) it should be performed within a dispatch block like so: dispatch_async(dispatch_get_main_queue(), ^{ ...Your Code... })
*/
- (void) microphone:(EZMicrophone *)microphone
hasBufferList:(AudioBufferList *)bufferList
withBufferSize:(UInt32)bufferSize
withNumberOfChannels:(UInt32)numberOfChannels;
-(void) microphone:(EZMicrophone*)microphone
hasBufferList:(AudioBufferList*)bufferList
withBufferSize:(UInt32)bufferSize
withNumberOfChannels:(UInt32)numberOfChannels;
@end
//------------------------------------------------------------------------------
#pragma mark - EZMicrophone
//------------------------------------------------------------------------------
/**
The EZMicrophone provides a component to get audio data from the default device microphone. On OSX this is the default selected input device in the system preferences while on iOS this defaults to use the default RemoteIO audio unit. The microphone data is converted to a float buffer array and returned back to the caller via the EZMicrophoneDelegate protocol.
*/
@interface EZMicrophone : NSObject <EZOutputDataSource>
//------------------------------------------------------------------------------
@interface EZMicrophone : NSObject
/**
The EZMicrophoneDelegate for which to handle the microphone callbacks
*/
@property (nonatomic, weak) id<EZMicrophoneDelegate> delegate;
//------------------------------------------------------------------------------
@property (nonatomic,assign) id<EZMicrophoneDelegate> microphoneDelegate;
/**
The EZAudioDevice being used to pull the microphone data.
- On iOS this can be any of the available microphones on the iPhone/iPad devices (usually there are 3). Defaults to the first microphone found (bottom mic)
- On OSX this can be any of the plugged in devices that Core Audio can detect (see kAudioUnitSubType_HALOutput for more information)
System Preferences -> Sound for the available inputs)
A bool describing whether the microphone is on and passing back audio data to its delegate.
*/
@property (nonatomic, strong) EZAudioDevice *device;
@property (nonatomic,assign) BOOL microphoneOn;
//------------------------------------------------------------------------------
/**
A BOOL describing whether the microphone is on and passing back audio data to its delegate.
*/
@property (nonatomic, assign) BOOL microphoneOn;
//------------------------------------------------------------------------------
/**
An EZOutput to use for porting the microphone input out (passthrough).
*/
@property (nonatomic, strong) EZOutput *output;
//------------------------------------------------------------------------------
#pragma mark - Initializers
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Initializers
///-----------------------------------------------------------
/**
Creates an instance of the EZMicrophone with a delegate to respond to the audioReceived callback. This will not start fetching the audio until startFetchingAudio has been called. Use initWithMicrophoneDelegate:startsImmediately: to instantiate this class and immediately start fetching audio data.
@param delegate A EZMicrophoneDelegate delegate that will receive the audioReceived callback.
@param microphoneDelegate A EZMicrophoneDelegate delegate that will receive the audioReceived callback.
@return An instance of the EZMicrophone class. This should be strongly retained.
*/
- (EZMicrophone *)initWithMicrophoneDelegate:(id<EZMicrophoneDelegate>)delegate;
//------------------------------------------------------------------------------
-(EZMicrophone*)initWithMicrophoneDelegate:(id<EZMicrophoneDelegate>)microphoneDelegate;
/**
Creates an instance of the EZMicrophone with a custom AudioStreamBasicDescription and provides the caller to specify a delegate to respond to the audioReceived callback. This will not start fetching the audio until startFetchingAudio has been called. Use initWithMicrophoneDelegate:startsImmediately: to instantiate this class and immediately start fetching audio data.
@@ -168,60 +124,49 @@
@param audioStreamBasicDescription A custom AudioStreamBasicFormat for the microphone input.
@return An instance of the EZMicrophone class. This should be strongly retained.
*/
-(EZMicrophone *)initWithMicrophoneDelegate:(id<EZMicrophoneDelegate>)delegate
withAudioStreamBasicDescription:(AudioStreamBasicDescription)audioStreamBasicDescription;
//------------------------------------------------------------------------------
-(EZMicrophone*)initWithMicrophoneDelegate:(id<EZMicrophoneDelegate>)microphoneDelegate
withAudioStreamBasicDescription:(AudioStreamBasicDescription)audioStreamBasicDescription;
/**
Creates an instance of the EZMicrophone with a delegate to respond to the audioReceived callback and allows the caller to specify whether they'd immediately like to start fetching the audio data.
@param delegate A EZMicrophoneDelegate delegate that will receive the audioReceived callback.
@param microphoneDelegate A EZMicrophoneDelegate delegate that will receive the audioReceived callback.
@param startsImmediately A boolean indicating whether to start fetching the data immediately. IF YES, the delegate's audioReceived callback will immediately start getting called.
@return An instance of the EZMicrophone class. This should be strongly retained.
*/
- (EZMicrophone *)initWithMicrophoneDelegate:(id<EZMicrophoneDelegate>)delegate
startsImmediately:(BOOL)startsImmediately;
//------------------------------------------------------------------------------
-(EZMicrophone*)initWithMicrophoneDelegate:(id<EZMicrophoneDelegate>)microphoneDelegate
startsImmediately:(BOOL)startsImmediately;
/**
Creates an instance of the EZMicrophone with a custom AudioStreamBasicDescription and provides the caller with a delegate to respond to the audioReceived callback and allows the caller to specify whether they'd immediately like to start fetching the audio data.
@param delegate A EZMicrophoneDelegate delegate that will receive the audioReceived callback.
@param microphoneDelegate A EZMicrophoneDelegate delegate that will receive the audioReceived callback.
@param audioStreamBasicDescription A custom AudioStreamBasicFormat for the microphone input.
@param startsImmediately A boolean indicating whether to start fetching the data immediately. IF YES, the delegate's audioReceived callback will immediately start getting called.
@return An instance of the EZMicrophone class. This should be strongly retained.
*/
- (EZMicrophone *)initWithMicrophoneDelegate:(id<EZMicrophoneDelegate>)delegate
withAudioStreamBasicDescription:(AudioStreamBasicDescription)audioStreamBasicDescription
startsImmediately:(BOOL)startsImmediately;
-(EZMicrophone*)initWithMicrophoneDelegate:(id<EZMicrophoneDelegate>)microphoneDelegate
withAudioStreamBasicDescription:(AudioStreamBasicDescription)audioStreamBasicDescription
startsImmediately:(BOOL)startsImmediately;
//------------------------------------------------------------------------------
#pragma mark - Class Initializers
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Class Initializers
///-----------------------------------------------------------
/**
Creates an instance of the EZMicrophone with a delegate to respond to the audioReceived callback. This will not start fetching the audio until startFetchingAudio has been called. Use microphoneWithDelegate:startsImmediately: to instantiate this class and immediately start fetching audio data.
@param delegate A EZMicrophoneDelegate delegate that will receive the audioReceived callback.
@param microphoneDelegate A EZMicrophoneDelegate delegate that will receive the audioReceived callback.
@return An instance of the EZMicrophone class. This should be declared as a strong property!
*/
+ (EZMicrophone *)microphoneWithDelegate:(id<EZMicrophoneDelegate>)delegate;
//------------------------------------------------------------------------------
+(EZMicrophone*)microphoneWithDelegate:(id<EZMicrophoneDelegate>)microphoneDelegate;
/**
Creates an instance of the EZMicrophone with a delegate to respond to the audioReceived callback. This will not start fetching the audio until startFetchingAudio has been called. Use microphoneWithDelegate:startsImmediately: to instantiate this class and immediately start fetching audio data.
@param delegate A EZMicrophoneDelegate delegate that will receive the audioReceived callback.
@param microphoneDelegate A EZMicrophoneDelegate delegate that will receive the audioReceived callback.
@param audioStreamBasicDescription A custom AudioStreamBasicFormat for the microphone input.
@return An instance of the EZMicrophone class. This should be declared as a strong property!
*/
+ (EZMicrophone *)microphoneWithDelegate:(id<EZMicrophoneDelegate>)delegate
withAudioStreamBasicDescription:(AudioStreamBasicDescription)audioStreamBasicDescription;
//------------------------------------------------------------------------------
+(EZMicrophone*)microphoneWithDelegate:(id<EZMicrophoneDelegate>)microphoneDelegate
withAudioStreamBasicDescription:(AudioStreamBasicDescription)audioStreamBasicDescription;
/**
Creates an instance of the EZMicrophone with a delegate to respond to the audioReceived callback and allows the caller to specify whether they'd immediately like to start fetching the audio data.
@@ -230,10 +175,8 @@
@param startsImmediately A boolean indicating whether to start fetching the data immediately. IF YES, the delegate's audioReceived callback will immediately start getting called.
@return An instance of the EZMicrophone class. This should be strongly retained.
*/
+ (EZMicrophone *)microphoneWithDelegate:(id<EZMicrophoneDelegate>)delegate
startsImmediately:(BOOL)startsImmediately;
//------------------------------------------------------------------------------
+(EZMicrophone*)microphoneWithDelegate:(id<EZMicrophoneDelegate>)microphoneDelegate
startsImmediately:(BOOL)startsImmediately;
/**
Creates an instance of the EZMicrophone with a delegate to respond to the audioReceived callback and allows the caller to specify whether they'd immediately like to start fetching the audio data.
@@ -243,14 +186,11 @@
@param startsImmediately A boolean indicating whether to start fetching the data immediately. IF YES, the delegate's audioReceived callback will immediately start getting called.
@return An instance of the EZMicrophone class. This should be strongly retained.
*/
+ (EZMicrophone *)microphoneWithDelegate:(id<EZMicrophoneDelegate>)delegate
withAudioStreamBasicDescription:(AudioStreamBasicDescription)audioStreamBasicDescription
startsImmediately:(BOOL)startsImmediately;
//------------------------------------------------------------------------------
#pragma mark - Shared Instance
//------------------------------------------------------------------------------
+(EZMicrophone*)microphoneWithDelegate:(id<EZMicrophoneDelegate>)microphoneDelegate
withAudioStreamBasicDescription:(AudioStreamBasicDescription)audioStreamBasicDescription
startsImmediately:(BOOL)startsImmediately;
#pragma mark - Singleton
///-----------------------------------------------------------
/// @name Shared Instance
///-----------------------------------------------------------
@@ -259,12 +199,9 @@
A shared instance of the microphone component. Most applications will only need to use one instance of the microphone component across multiple views. Make sure to call the `startFetchingAudio` method to receive the audio data in the microphone delegate.
@return A shared instance of the `EZAudioMicrophone` component.
*/
+ (EZMicrophone *)sharedMicrophone;
+(EZMicrophone*)sharedMicrophone;
//------------------------------------------------------------------------------
#pragma mark - Events
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Starting/Stopping The Microphone
///-----------------------------------------------------------
@@ -272,19 +209,14 @@
/**
Starts fetching audio from the default microphone. Will notify delegate with audioReceived callback.
*/
- (void)startFetchingAudio;
//------------------------------------------------------------------------------
-(void)startFetchingAudio;
/**
Stops fetching audio. Will stop notifying the delegate's audioReceived callback.
*/
- (void)stopFetchingAudio;
-(void)stopFetchingAudio;
//------------------------------------------------------------------------------
#pragma mark - Getters
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Getting The Microphone's Audio Format
///-----------------------------------------------------------
@@ -293,80 +225,18 @@
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;
-(AudioStreamBasicDescription)audioStreamBasicDescription;
//------------------------------------------------------------------------------
/**
Provides the underlying Audio Unit that is being used to fetch the audio.
@return The AudioUnit used for the microphone
*/
- (AudioUnit *)audioUnit;
//------------------------------------------------------------------------------
#pragma mark - Setters
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Customizing The Microphone Stream Format
/// @name Customizing The Microphone Input Format
///-----------------------------------------------------------
/**
Sets the AudioStreamBasicDescription on the microphone input. Must be linear PCM and must be the same sample rate as the stream format coming in (check the current `audioStreamBasicDescription` before setting).
Sets the AudioStreamBasicDescription on the microphone input.
@warning Do not set this while fetching audio (startFetchingAudio)
@param asbd The new AudioStreamBasicDescription to use in place of the current audio format description.
*/
- (void)setAudioStreamBasicDescription:(AudioStreamBasicDescription)asbd;
///-----------------------------------------------------------
/// @name Setting The Microphone's Hardware Device
///-----------------------------------------------------------
/**
Sets the EZAudioDevice being used to pull the microphone data.
- On iOS this can be any of the available microphones on the iPhone/iPad devices (usually there are 3). Defaults to the first microphone found (bottom mic)
- On OSX this can be any of the plugged in devices that Core Audio can detect (see kAudioUnitSubType_HALOutput for more information)
System Preferences -> Sound for the available inputs)
@param device An EZAudioDevice instance that should be used to fetch the microphone data.
*/
- (void)setDevice:(EZAudioDevice *)device;
//------------------------------------------------------------------------------
#pragma mark - Direct Output
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Setting The Microphone's Output (Direct Out)
///-----------------------------------------------------------
/**
When set this will pipe out the contents of the microphone into an EZOutput. This is known as a passthrough or direct out that will simply pipe the microphone input to an output.
@param output An EZOutput instance that the microphone will use to output its audio data to the speaker.
*/
- (void)setOutput:(EZOutput *)output;
//------------------------------------------------------------------------------
#pragma mark - Subclass Methods
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Subclass
///-----------------------------------------------------------
/**
The default AudioStreamBasicDescription set as the stream format of the microphone if no custom description is set. Defaults to a non-interleaved float format with the number of channels specified by the `numberOfChannels` method.
@return An AudioStreamBasicDescription that will be used as the default stream format.
*/
- (AudioStreamBasicDescription)defaultStreamFormat;
//------------------------------------------------------------------------------
/**
The number of channels the input microphone is expected to have. Defaults to 1 (assumes microphone is mono).
@return A UInt32 representing the number of channels expected for the microphone.
*/
- (UInt32)numberOfChannels;
//------------------------------------------------------------------------------
-(void)setAudioStreamBasicDescription:(AudioStreamBasicDescription)asbd;
@end
+598
View File
@@ -0,0 +1,598 @@
//
// EZMicrophone.m
// EZAudio
//
// Created by Syed Haris Ali on 9/2/13.
// Copyright (c) 2013 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 "EZMicrophone.h"
#ifndef MAC_OS_X_VERSION_10_7
// CoreServices defines eofErr, replaced in 10.7 by kAudioFileEndOfFileError
#include <CoreServices/CoreServices.h>
#endif
#import "EZAudio.h"
/// Buses
static const AudioUnitScope kEZAudioMicrophoneInputBus = 1;
static const AudioUnitScope kEZAudioMicrophoneOutputBus = 0;
/// Flags
#if TARGET_OS_IPHONE
static const UInt32 kEZAudioMicrophoneDisableFlag = 1;
#elif TARGET_OS_MAC
static const UInt32 kEZAudioMicrophoneDisableFlag = 0;
#endif
static const UInt32 kEZAudioMicrophoneEnableFlag = 1;
@interface EZMicrophone (){
/// Internal
BOOL _customASBD;
BOOL _isConfigured;
BOOL _isFetching;
/// Stream Description
AudioStreamBasicDescription streamFormat;
/// Audio Graph and Input/Output Units
AudioUnit microphoneInput;
/// Audio Buffers
float **floatBuffers;
AudioBufferList *microphoneInputBuffer;
/// Device Parameters
Float64 _deviceSampleRate;
Float32 _deviceBufferDuration;
UInt32 _deviceBufferFrameSize;
#if TARGET_OS_IPHONE
#elif TARGET_OS_MAC
Float64 inputScopeSampleRate;
#endif
}
@end
@implementation EZMicrophone
@synthesize microphoneDelegate = _microphoneDelegate;
@synthesize microphoneOn = _microphoneOn;
#pragma mark - Callbacks
static OSStatus inputCallback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData ) {
EZMicrophone *microphone = (__bridge EZMicrophone*)inRefCon;
OSStatus result = noErr;
// Render audio into buffer
result = AudioUnitRender(microphone->microphoneInput,
ioActionFlags,
inTimeStamp,
inBusNumber,
inNumberFrames,
microphone->microphoneInputBuffer);
if( !result ){
// ----- 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];
}
}
}
return result;
}
#pragma mark - Initialization
-(id)init {
self = [super init];
if(self){
// 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;
}
-(EZMicrophone *)initWithMicrophoneDelegate:(id<EZMicrophoneDelegate>)microphoneDelegate {
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;
}
-(EZMicrophone *)initWithMicrophoneDelegate:(id<EZMicrophoneDelegate>)microphoneDelegate
withAudioStreamBasicDescription:(AudioStreamBasicDescription)audioStreamBasicDescription {
self = [self initWithMicrophoneDelegate:microphoneDelegate];
if(self){
_customASBD = YES;
streamFormat = audioStreamBasicDescription;
}
return self;
}
-(EZMicrophone *)initWithMicrophoneDelegate:(id<EZMicrophoneDelegate>)microphoneDelegate
startsImmediately:(BOOL)startsImmediately {
self = [self initWithMicrophoneDelegate:microphoneDelegate];
if(self){
startsImmediately ? [self startFetchingAudio] : -1;
}
return self;
}
-(EZMicrophone *)initWithMicrophoneDelegate:(id<EZMicrophoneDelegate>)microphoneDelegate
withAudioStreamBasicDescription:(AudioStreamBasicDescription)audioStreamBasicDescription
startsImmediately:(BOOL)startsImmediately {
self = [self initWithMicrophoneDelegate:microphoneDelegate withAudioStreamBasicDescription:audioStreamBasicDescription];
if(self){
startsImmediately ? [self startFetchingAudio] : -1;
}
return self;
}
#pragma mark - Class Initializers
+(EZMicrophone *)microphoneWithDelegate:(id<EZMicrophoneDelegate>)microphoneDelegate {
return [[EZMicrophone alloc] initWithMicrophoneDelegate:microphoneDelegate];
}
+(EZMicrophone *)microphoneWithDelegate:(id<EZMicrophoneDelegate>)microphoneDelegate
withAudioStreamBasicDescription:(AudioStreamBasicDescription)audioStreamBasicDescription {
return [[EZMicrophone alloc] initWithMicrophoneDelegate:microphoneDelegate
withAudioStreamBasicDescription:audioStreamBasicDescription];
}
+(EZMicrophone *)microphoneWithDelegate:(id<EZMicrophoneDelegate>)microphoneDelegate
startsImmediately:(BOOL)startsImmediately {
return [[EZMicrophone alloc] initWithMicrophoneDelegate:microphoneDelegate
startsImmediately:startsImmediately];
}
+(EZMicrophone *)microphoneWithDelegate:(id<EZMicrophoneDelegate>)microphoneDelegate
withAudioStreamBasicDescription:(AudioStreamBasicDescription)audioStreamBasicDescription
startsImmediately:(BOOL)startsImmediately {
return [[EZMicrophone alloc] initWithMicrophoneDelegate:microphoneDelegate
withAudioStreamBasicDescription:audioStreamBasicDescription
startsImmediately:startsImmediately];
}
#pragma mark - Singleton
+(EZMicrophone*)sharedMicrophone {
static EZMicrophone *_sharedMicrophone = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedMicrophone = [[EZMicrophone alloc] init];
});
return _sharedMicrophone;
}
#pragma mark - Events
-(void)startFetchingAudio {
if( !_isFetching ){
// Start fetching input
[EZAudio checkResult:AudioOutputUnitStart(self->microphoneInput)
operation:"Microphone failed to start fetching audio"];
_isFetching = YES;
self.microphoneOn = YES;
}
}
-(void)stopFetchingAudio {
// Stop fetching input data
if( _isConfigured ){
if( _isFetching ){
[EZAudio checkResult:AudioOutputUnitStop(self->microphoneInput)
operation:"Microphone failed to stop fetching audio"];
_isFetching = NO;
self.microphoneOn = NO;
}
}
}
#pragma mark - Getters
-(AudioStreamBasicDescription)audioStreamBasicDescription {
return streamFormat;
}
#pragma mark - Setter
-(void)setMicrophoneOn:(BOOL)microphoneOn {
_microphoneOn = microphoneOn;
if( microphoneOn ){
[self startFetchingAudio];
}
else {
[self stopFetchingAudio];
}
}
-(void)setAudioStreamBasicDescription:(AudioStreamBasicDescription)asbd {
if( self.microphoneOn ){
NSAssert(self.microphoneOn,@"Cannot set the AudioStreamBasicDescription while microphone is fetching audio");
}
else {
_customASBD = YES;
streamFormat = asbd;
[self _configureStreamFormatWithSampleRate:_deviceSampleRate];
}
}
#pragma mark - Configure The Input Unit
-(void)_createInputUnit {
// Get component description for input
AudioComponentDescription inputComponentDescription = [self _getInputAudioComponentDescription];
// Get the input component
AudioComponent inputComponent = [self _getInputComponentWithAudioComponentDescription:inputComponentDescription];
// Create a new instance of the component and store it for internal use
[self _createNewInstanceForInputComponent:inputComponent];
// Enable Input Scope
[self _enableInputScope];
// Disable Output Scope
[self _disableOutputScope];
// Get the default device if we need to (OSX only, iOS uses RemoteIO)
#if TARGET_OS_IPHONE
// Do nothing (using RemoteIO)
#elif TARGET_OS_MAC
[self _configureDefaultDevice];
#endif
// Configure device and pull hardware specific sampling rate (default = 44.1 kHz)
_deviceSampleRate = [self _configureDeviceSampleRateWithDefault:44100.0];
// Configure device and pull hardware specific buffer duration (default = 0.0232)
_deviceBufferDuration = [self _configureDeviceBufferDurationWithDefault:0.0232];
// Configure the stream format with the hardware sample rate
[self _configureStreamFormatWithSampleRate:_deviceSampleRate];
// Notify delegate the audio stream basic description was successfully created
[self _notifyDelegateOfStreamFormat];
// Get buffer frame size
_deviceBufferFrameSize = [self _getBufferFrameSize];
// Create the audio buffer list and pre-malloc the buffers in the list
[self _configureAudioBufferListWithFrameSize:_deviceBufferFrameSize];
// Set the float converter's stream format
[self _configureFloatConverterWithFrameSize:_deviceBufferFrameSize];
// Setup input callback
[self _configureInputCallback];
// Disable buffer allocation (optional - do this if we want to pass in our own)
[self _disableCallbackBufferAllocation];
// Initialize the audio unit
[EZAudio checkResult:AudioUnitInitialize( microphoneInput )
operation:"Couldn't initialize the input unit"];
}
#pragma mark - Audio Component Initialization
-(AudioComponentDescription)_getInputAudioComponentDescription {
// Create an input component description for mic input
AudioComponentDescription inputComponentDescription;
inputComponentDescription.componentType = kAudioUnitType_Output;
inputComponentDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
inputComponentDescription.componentFlags = 0;
inputComponentDescription.componentFlagsMask = 0;
#if TARGET_OS_IPHONE
inputComponentDescription.componentSubType = kAudioUnitSubType_RemoteIO;
#elif TARGET_OS_MAC
inputComponentDescription.componentSubType = kAudioUnitSubType_HALOutput;
#endif
// Return the successfully created input component description
return inputComponentDescription;
}
-(AudioComponent)_getInputComponentWithAudioComponentDescription:(AudioComponentDescription)audioComponentDescription {
// Try and find the component
AudioComponent inputComponent = AudioComponentFindNext( NULL , &audioComponentDescription );
NSAssert(inputComponent,@"Couldn't get input component unit!");
return inputComponent;
}
-(void)_createNewInstanceForInputComponent:(AudioComponent)audioComponent {
[EZAudio checkResult:AudioComponentInstanceNew(audioComponent,
&microphoneInput )
operation:"Couldn't open component for microphone input unit."];
}
#pragma mark - Input/Output Scope Initialization
-(void)_disableOutputScope {
[EZAudio checkResult:AudioUnitSetProperty(microphoneInput,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Output,
kEZAudioMicrophoneOutputBus,
&kEZAudioMicrophoneDisableFlag,
sizeof(kEZAudioMicrophoneDisableFlag))
operation:"Couldn't disable output on I/O unit."];
}
-(void)_enableInputScope {
[EZAudio checkResult:AudioUnitSetProperty(microphoneInput,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Input,
kEZAudioMicrophoneInputBus,
&kEZAudioMicrophoneEnableFlag,
sizeof(kEZAudioMicrophoneEnableFlag))
operation:"Couldn't enable input on I/O unit."];
}
#pragma mark - Pull Default Device (OSX)
#if TARGET_OS_IPHONE
// Not needed, using RemoteIO
#elif TARGET_OS_MAC
-(void)_configureDefaultDevice {
// Get the default audio input device (pulls an abstract type from system preferences)
AudioDeviceID defaultDevice = kAudioObjectUnknown;
UInt32 propSize = sizeof(defaultDevice);
AudioObjectPropertyAddress defaultDeviceProperty;
defaultDeviceProperty.mSelector = kAudioHardwarePropertyDefaultInputDevice;
defaultDeviceProperty.mScope = kAudioObjectPropertyScopeGlobal;
defaultDeviceProperty.mElement = kAudioObjectPropertyElementMaster;
[EZAudio checkResult:AudioObjectGetPropertyData(kAudioObjectSystemObject,
&defaultDeviceProperty,
0,
NULL,
&propSize,
&defaultDevice)
operation:"Couldn't get default input device"];
// Set the default device on the microphone input unit
propSize = sizeof(defaultDevice);
[EZAudio checkResult:AudioUnitSetProperty(microphoneInput,
kAudioOutputUnitProperty_CurrentDevice,
kAudioUnitScope_Global,
kEZAudioMicrophoneOutputBus,
&defaultDevice,
propSize)
operation:"Couldn't set default device on I/O unit"];
// Get the stream format description from the newly created input unit and assign it to the output of the input unit
AudioStreamBasicDescription inputScopeFormat;
propSize = sizeof(AudioStreamBasicDescription);
[EZAudio checkResult:AudioUnitGetProperty(microphoneInput,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
kEZAudioMicrophoneInputBus,
&inputScopeFormat,
&propSize)
operation:"Couldn't get ASBD from input unit (1)"];
// Assign the same stream format description from the output of the input unit and pull the sample rate
AudioStreamBasicDescription outputScopeFormat;
propSize = sizeof(AudioStreamBasicDescription);
[EZAudio checkResult:AudioUnitGetProperty(microphoneInput,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
kEZAudioMicrophoneInputBus,
&outputScopeFormat,
&propSize)
operation:"Couldn't get ASBD from input unit (2)"];
// Store the input scope's sample rate
inputScopeSampleRate = inputScopeFormat.mSampleRate;
}
#endif
#pragma mark - Pull Sample Rate
-(Float64)_configureDeviceSampleRateWithDefault:(float)defaultSampleRate {
Float64 hardwareSampleRate = defaultSampleRate;
#if TARGET_OS_IPHONE
// Use approximations for simulator and pull from real device if connected
#if !(TARGET_IPHONE_SIMULATOR)
// Sample Rate
hardwareSampleRate = [[AVAudioSession sharedInstance] sampleRate];
#endif
#elif TARGET_OS_MAC
hardwareSampleRate = inputScopeSampleRate;
#endif
return hardwareSampleRate;
}
#pragma mark - Pull Buffer Duration
-(Float32)_configureDeviceBufferDurationWithDefault:(float)defaultBufferDuration {
Float32 bufferDuration = defaultBufferDuration; // Type 1/43 by default
#if TARGET_OS_IPHONE
// Use approximations for simulator and pull from real device if connected
#if !(TARGET_IPHONE_SIMULATOR)
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
#endif
return bufferDuration;
}
#pragma mark - Pull Buffer Frame Size
-(UInt32)_getBufferFrameSize {
UInt32 bufferFrameSize;
UInt32 propSize = sizeof(bufferFrameSize);
[EZAudio checkResult:AudioUnitGetProperty(microphoneInput,
#if TARGET_OS_IPHONE
kAudioUnitProperty_MaximumFramesPerSlice,
#elif TARGET_OS_MAC
kAudioDevicePropertyBufferFrameSize,
#endif
kAudioUnitScope_Global,
kEZAudioMicrophoneOutputBus,
&bufferFrameSize,
&propSize)
operation:"Failed to get buffer frame size"];
return bufferFrameSize;
}
#pragma mark - Stream Format Initialization
-(void)_configureStreamFormatWithSampleRate:(Float64)sampleRate {
// Set the stream format
if( !_customASBD ){
streamFormat = [EZAudio stereoCanonicalNonInterleavedFormatWithSampleRate:sampleRate];
}
else {
streamFormat.mSampleRate = sampleRate;
}
UInt32 propSize = sizeof(streamFormat);
// Set the stream format for output on the microphone's input scope
[EZAudio checkResult:AudioUnitSetProperty(microphoneInput,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
kEZAudioMicrophoneOutputBus,
&streamFormat,
propSize)
operation:"Could not set microphone's stream format bus 0"];
// Set the stream format for the input on the microphone's output scope
[EZAudio checkResult:AudioUnitSetProperty(microphoneInput,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
kEZAudioMicrophoneInputBus,
&streamFormat,
propSize)
operation:"Could not set microphone's stream format bus 1"];
}
-(void)_notifyDelegateOfStreamFormat {
if( _microphoneDelegate ){
if( [_microphoneDelegate respondsToSelector:@selector(microphone:hasAudioStreamBasicDescription:) ] ){
[_microphoneDelegate microphone:self
hasAudioStreamBasicDescription:streamFormat];
}
}
}
#pragma mark - AudioBufferList Initialization
-(void)_configureAudioBufferListWithFrameSize:(UInt32)bufferFrameSize {
UInt32 bufferSizeBytes = bufferFrameSize * streamFormat.mBytesPerFrame;
UInt32 propSize = offsetof( AudioBufferList, mBuffers[0] ) + ( sizeof( AudioBuffer ) *streamFormat.mChannelsPerFrame );
microphoneInputBuffer = (AudioBufferList*)malloc(propSize);
microphoneInputBuffer->mNumberBuffers = streamFormat.mChannelsPerFrame;
for( UInt32 i = 0; i < microphoneInputBuffer->mNumberBuffers; i++ ){
microphoneInputBuffer->mBuffers[i].mNumberChannels = streamFormat.mChannelsPerFrame;
microphoneInputBuffer->mBuffers[i].mDataByteSize = bufferSizeBytes;
microphoneInputBuffer->mBuffers[i].mData = malloc(bufferSizeBytes);
}
}
#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]);
// }
}
#pragma mark - Input Callback Initialization
-(void)_configureInputCallback {
AURenderCallbackStruct microphoneCallbackStruct;
microphoneCallbackStruct.inputProc = inputCallback;
microphoneCallbackStruct.inputProcRefCon = (__bridge void *)self;
[EZAudio checkResult:AudioUnitSetProperty(microphoneInput,
kAudioOutputUnitProperty_SetInputCallback,
kAudioUnitScope_Global,
// output bus for mac
#if TARGET_OS_IPHONE
kEZAudioMicrophoneInputBus,
#elif TARGET_OS_MAC
kEZAudioMicrophoneOutputBus,
#endif
&microphoneCallbackStruct,
sizeof(microphoneCallbackStruct))
operation:"Couldn't set input callback"];
}
-(void)_disableCallbackBufferAllocation {
[EZAudio checkResult:AudioUnitSetProperty(microphoneInput,
kAudioUnitProperty_ShouldAllocateBuffer,
kAudioUnitScope_Output,
kEZAudioMicrophoneInputBus,
&kEZAudioMicrophoneDisableFlag,
sizeof(kEZAudioMicrophoneDisableFlag))
operation:"Could not disable audio unit allocating its own buffers"];
}
@end
+201
View File
@@ -0,0 +1,201 @@
//
// EZOutput.h
// EZAudio
//
// Created by Syed Haris Ali on 12/2/13.
// Copyright (c) 2013 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 <AudioToolbox/AudioToolbox.h>
#if TARGET_OS_IPHONE
#elif TARGET_OS_MAC
#import <AudioUnit/AudioUnit.h>
#endif
#import "TPCircularBuffer.h"
@class EZOutput;
/**
The EZOutputDataSource (required for the EZOutput) specifies a receiver to provide audio data when the EZOutput is started. Only ONE datasource method is expected to be implemented and priority is given as such:
1.) `output:callbackWithActionFlags:inTimeStamp:inBusNumber:inNumberFrames:ioData:`
2.) `outputShouldUseCircularBuffer:`
3.) `output:needsBufferListWithFrames:withBufferSize:`
*/
@protocol EZOutputDataSource <NSObject>
@optional
///-----------------------------------------------------------
/// @name Pulling The Audio Data
///-----------------------------------------------------------
/**
Provides complete override of the output callback function. The delegate is expected to
@param output The instance of the EZOutput that asked for the data
@param ioActionFlags AudioUnitRenderActionFlags provided by the output callback
@param inTimeStamp AudioTimeStamp reference provided by the output callback
@param inBusNumber UInt32 representing the bus number provided by the output callback
@param inNumberFrames UInt32 representing the number of frames provided by the output callback
@param ioData AudioBufferList pointer representing the audio data that will be used for output provided by the output callback (fill this!)
*/
-(void)output:(EZOutput*)output
callbackWithActionFlags:(AudioUnitRenderActionFlags*)ioActionFlags
inTimeStamp:(const AudioTimeStamp*)inTimeStamp
inBusNumber:(UInt32)inBusNumber
inNumberFrames:(UInt32)inNumberFrames
ioData:(AudioBufferList*)ioData;
/**
Provides output using a circular
@param output The instance of the EZOutput that asked for the data
@return The EZOutputDataSource's TPCircularBuffer structure holding the audio data in a circular buffer
*/
-(TPCircularBuffer*)outputShouldUseCircularBuffer:(EZOutput *)output;
/**
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.
@return A pointer to the AudioBufferList structure holding the audio data. If nil or NULL, will output silence.
*/
-(void) output:(EZOutput *)output
shouldFillAudioBufferList:(AudioBufferList*)audioBufferList
withNumberOfFrames:(UInt32)frames;
@end
/**
The EZOutput component provides a generic output to glue all the other EZAudio components together and push whatever sound you've created to the default output device (think opposite of the microphone). The EZOutputDataSource provides the required AudioBufferList needed to populate the output buffer.
*/
@interface EZOutput : NSObject
#pragma mark - Properties
/**
The EZOutputDataSource that provides the required AudioBufferList to the output callback function
*/
@property (nonatomic,assign) id<EZOutputDataSource>outputDataSource;
#pragma mark - Initializers
///-----------------------------------------------------------
/// @name Initializers
///-----------------------------------------------------------
/**
Creates a new instance of the EZOutput and allows the caller to specify an EZOutputDataSource.
@param dataSource The EZOutputDataSource that will be used to pull the audio data for the output callback.
@return A newly created instance of the EZOutput class.
*/
-(id)initWithDataSource:(id<EZOutputDataSource>)dataSource;
/**
Creates a new instance of the EZOutput and allows the caller to specify an EZOutputDataSource.
@param dataSource The EZOutputDataSource that will be used to pull the audio data for the output callback.
@param audioStreamBasicDescription The AudioStreamBasicDescription of the EZOutput.
@warning AudioStreamBasicDescriptions that are invalid will cause the EZOutput to fail to initialize
@return A newly created instance of the EZOutput class.
*/
-(id) initWithDataSource:(id<EZOutputDataSource>)dataSource
withAudioStreamBasicDescription:(AudioStreamBasicDescription)audioStreamBasicDescription;
#pragma mark - Class Initializers
///-----------------------------------------------------------
/// @name Class Initializers
///-----------------------------------------------------------
/**
Class method to create a new instance of the EZOutput and allows the caller to specify an EZOutputDataSource.
@param dataSource The EZOutputDataSource that will be used to pull the audio data for the output callback.
@return A newly created instance of the EZOutput class.
*/
+(EZOutput*)outputWithDataSource:(id<EZOutputDataSource>)dataSource;
/**
Class method to create a new instance of the EZOutput and allows the caller to specify an EZOutputDataSource.
@param dataSource The EZOutputDataSource that will be used to pull the audio data for the output callback.
@param audioStreamBasicDescription The AudioStreamBasicDescription of the EZOutput.
@warning AudioStreamBasicDescriptions that are invalid will cause the EZOutput to fail to initialize
@return A newly created instance of the EZOutput class.
*/
+(EZOutput*)outputWithDataSource:(id<EZOutputDataSource>)dataSource
withAudioStreamBasicDescription:(AudioStreamBasicDescription)audioStreamBasicDescription;
#pragma mark - Singleton
///-----------------------------------------------------------
/// @name Shared Instance
///-----------------------------------------------------------
/**
Creates a shared instance of the EZOutput (one app will usually only need one output and share the role of the EZOutputDataSource).
@return The shared instance of the EZOutput class.
*/
+(EZOutput*)sharedOutput;
#pragma mark - Events
///-----------------------------------------------------------
/// @name Starting/Stopping The Output
///-----------------------------------------------------------
/**
Starts pulling audio data from the EZOutputDataSource to the default device output.
*/
-(void)startPlayback;
/**
Stops pulling audio data from the EZOutputDataSource to the default device output.
*/
-(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
///-----------------------------------------------------------
/**
Provides a flag indicating whether the EZOutput is pulling audio data from the EZOutputDataSource for playback.
@return YES if the EZOutput is pulling audio data to the output device, NO if it is stopped
*/
-(BOOL)isPlaying;
#pragma mark - Setters
///-----------------------------------------------------------
/// @name Customizing The Output Format
///-----------------------------------------------------------
/**
Sets the AudioStreamBasicDescription on the output.
@warning Do not set this during playback.
@param asbd The new AudioStreamBasicDescription to use in place of the current audio format description.
*/
-(void)setAudioStreamBasicDescription:(AudioStreamBasicDescription)asbd;
@end
+374
View File
@@ -0,0 +1,374 @@
//
// EZOutput.m
// EZAudio
//
// Created by Syed Haris Ali on 12/2/13.
// Copyright (c) 2013 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 "EZOutput.h"
#import "EZAudio.h"
@interface EZOutput (){
BOOL _customASBD;
BOOL _isPlaying;
AudioStreamBasicDescription _outputASBD;
AudioUnit _outputUnit;
}
@end
@implementation EZOutput
@synthesize outputDataSource = _outputDataSource;
static OSStatus OutputRenderCallback(void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
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:)] ){
[output.outputDataSource output:output
callbackWithActionFlags:ioActionFlags
inTimeStamp:inTimeStamp
inBusNumber:inBusNumber
inNumberFrames:inNumberFrames
ioData:ioData];
}
else if( [output.outputDataSource respondsToSelector:@selector(outputShouldUseCircularBuffer:)] ){
TPCircularBuffer *circularBuffer = [output.outputDataSource outputShouldUseCircularBuffer:output];
if( !circularBuffer ){
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;
}
return noErr;
};
/**
Thank you Michael Tyson (A Tasty Pixel) for writing the TPCircularBuffer, you are amazing!
*/
// Get the desired amount of bytes to copy
int32_t bytesToCopy = ioData->mBuffers[0].mDataByteSize;
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;
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 );
// Consume those bytes ( this will internally push the head of the circular buffer )
TPCircularBufferConsume(circularBuffer,amount);
}
// Provided an AudioBufferList (defaults to silence)
else if( [output.outputDataSource respondsToSelector:@selector(output:shouldFillAudioBufferList:withNumberOfFrames:)] ) {
[output.outputDataSource output:output
shouldFillAudioBufferList:ioData
withNumberOfFrames:inNumberFrames];
}
return noErr;
}
#pragma mark - Initialization
-(id)init {
self = [super init];
if(self){
[self _configureOutput];
}
return self;
}
-(id)initWithDataSource:(id<EZOutputDataSource>)dataSource {
self = [super init];
if(self){
self.outputDataSource = dataSource;
[self _configureOutput];
}
return self;
}
-(id) initWithDataSource:(id<EZOutputDataSource>)dataSource
withAudioStreamBasicDescription:(AudioStreamBasicDescription)audioStreamBasicDescription {
self = [super init];
if(self){
_customASBD = YES;
_outputASBD = audioStreamBasicDescription;
self.outputDataSource = dataSource;
[self _configureOutput];
}
return self;
}
#pragma mark - Class Initializers
+(EZOutput*)outputWithDataSource:(id<EZOutputDataSource>)dataSource {
return [[EZOutput alloc] initWithDataSource:dataSource];
}
+(EZOutput *)outputWithDataSource:(id<EZOutputDataSource>)dataSource
withAudioStreamBasicDescription:(AudioStreamBasicDescription)audioStreamBasicDescription {
return [[EZOutput alloc] initWithDataSource:dataSource withAudioStreamBasicDescription:audioStreamBasicDescription];
}
#pragma mark - Singleton
+(EZOutput*)sharedOutput {
static EZOutput *_sharedOutput = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedOutput = [[EZOutput alloc] init];
});
return _sharedOutput;
}
#pragma mark - Audio Component Initialization
-(AudioComponentDescription)_getOutputAudioComponentDescription {
// Create an output component description for default output device
AudioComponentDescription outputComponentDescription;
outputComponentDescription.componentFlags = 0;
outputComponentDescription.componentFlagsMask = 0;
outputComponentDescription.componentManufacturer = kAudioUnitManufacturer_Apple;
#if TARGET_OS_IPHONE
outputComponentDescription.componentSubType = kAudioUnitSubType_RemoteIO;
#elif TARGET_OS_MAC
outputComponentDescription.componentSubType = kAudioUnitSubType_DefaultOutput;
#endif
outputComponentDescription.componentType = kAudioUnitType_Output;
return outputComponentDescription;
}
-(AudioComponent)_getOutputComponentWithAudioComponentDescription:(AudioComponentDescription)outputComponentDescription {
// Try and find the component
AudioComponent outputComponent = AudioComponentFindNext( NULL , &outputComponentDescription );
NSAssert(outputComponent,@"Couldn't get input component unit!");
return outputComponent;
}
-(void)_createNewInstanceForOutputComponent:(AudioComponent)outputComponent {
//
[EZAudio checkResult:AudioComponentInstanceNew( outputComponent, &_outputUnit )
operation:"Failed to open component for output unit"];
}
#pragma mark - Configure The Output Unit
//-(void)_configureOutput {
//
// // Get component description for output
// AudioComponentDescription outputComponentDescription = [self _getOutputAudioComponentDescription];
//
// // Get the output component
// AudioComponent outputComponent = [self _getOutputComponentWithAudioComponentDescription:outputComponentDescription];
//
// // Create a new instance of the component and store it for internal use
// [self _createNewInstanceForOutputComponent:outputComponent];
//
//}
#if TARGET_OS_IPHONE
-(void)_configureOutput {
//
AudioComponentDescription outputcd;
outputcd.componentFlags = 0;
outputcd.componentFlagsMask = 0;
outputcd.componentManufacturer = kAudioUnitManufacturer_Apple;
outputcd.componentSubType = kAudioUnitSubType_RemoteIO;
outputcd.componentType = kAudioUnitType_Output;
//
AudioComponent comp = AudioComponentFindNext(NULL,&outputcd);
[EZAudio checkResult:AudioComponentInstanceNew(comp,&_outputUnit)
operation:"Failed to get output unit"];
// Setup the output unit for playback
UInt32 oneFlag = 1;
AudioUnitElement bus0 = 0;
[EZAudio checkResult:AudioUnitSetProperty(_outputUnit,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Output,
bus0,
&oneFlag,
sizeof(oneFlag))
operation:"Failed to enable output unit"];
// Get the hardware sample rate
Float64 hardwareSampleRate = 44100;
#if !(TARGET_IPHONE_SIMULATOR)
hardwareSampleRate = [[AVAudioSession sharedInstance] sampleRate];
#endif
// Setup an ASBD in canonical format by default
if( !_customASBD ){
_outputASBD = [EZAudio stereoCanonicalNonInterleavedFormatWithSampleRate:hardwareSampleRate];
}
// Set the format for output
[EZAudio checkResult:AudioUnitSetProperty(_outputUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
bus0,
&_outputASBD,
sizeof(_outputASBD))
operation:"Couldn't set the ASBD for input scope/bos 0"];
//
AURenderCallbackStruct input;
input.inputProc = OutputRenderCallback;
input.inputProcRefCon = (__bridge void *)self;
[EZAudio checkResult:AudioUnitSetProperty(_outputUnit,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input,
bus0,
&input,
sizeof(input))
operation:"Failed to set the render callback on the output unit"];
//
[EZAudio checkResult:AudioUnitInitialize(_outputUnit)
operation:"Couldn't initialize output unit"];
}
#elif TARGET_OS_MAC
-(void)_configureOutput {
//
AudioComponentDescription outputcd;
outputcd.componentType = kAudioUnitType_Output;
outputcd.componentSubType = kAudioUnitSubType_DefaultOutput;
outputcd.componentManufacturer = kAudioUnitManufacturer_Apple;
//
AudioComponent comp = AudioComponentFindNext(NULL,&outputcd);
if( comp == NULL ){
NSLog(@"Failed to get output unit");
exit(-1);
}
[EZAudio checkResult:AudioComponentInstanceNew(comp,&_outputUnit)
operation:"Failed to open component for output unit"];
// Setup an ASBD in canonical format by default
if( !_customASBD ){
_outputASBD = [EZAudio stereoFloatNonInterleavedFormatWithSampleRate:44100];
}
// 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"];
//
AURenderCallbackStruct input;
input.inputProc = OutputRenderCallback;
input.inputProcRefCon = (__bridge void *)(self);
[EZAudio checkResult:AudioUnitSetProperty(_outputUnit,
kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input,
0,
&input,
sizeof(input))
operation:"Failed to set the render callback on the output unit"];
//
[EZAudio checkResult:AudioUnitInitialize(_outputUnit)
operation:"Couldn't initialize output unit"];
}
#endif
#pragma mark - Events
-(void)startPlayback {
if( !_isPlaying ){
[EZAudio checkResult:AudioOutputUnitStart(_outputUnit)
operation:"Failed to start output unit"];
_isPlaying = YES;
}
}
-(void)stopPlayback {
if( _isPlaying ){
[EZAudio checkResult:AudioOutputUnitStop(_outputUnit)
operation:"Failed to stop output unit"];
_isPlaying = NO;
}
}
#pragma mark - Getters
-(AudioStreamBasicDescription)audioStreamBasicDescription {
return _outputASBD;
}
-(BOOL)isPlaying {
return _isPlaying;
}
#pragma mark - Setters
-(void)setAudioStreamBasicDescription:(AudioStreamBasicDescription)asbd {
BOOL wasPlaying = NO;
if( self.isPlaying ){
[self stopPlayback];
wasPlaying = YES;
}
_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];
}
}
-(void)dealloc {
[EZAudio checkResult:AudioOutputUnitStop(_outputUnit)
operation:"Failed to uninitialize output unit"];
[EZAudio checkResult:AudioUnitUninitialize(_outputUnit)
operation:"Failed to uninitialize output unit"];
[EZAudio checkResult:AudioComponentInstanceDispose(_outputUnit)
operation:"Failed to uninitialize output unit"];
}
@end
@@ -3,7 +3,7 @@
// EZAudio
//
// Created by Syed Haris Ali on 11/24/13.
// Copyright (c) 2015 Syed Haris Ali. All rights reserved.
// Copyright (c) 2013 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
@@ -23,13 +23,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import <Foundation/Foundation.h>
#import "EZAudioUtilities.h"
//------------------------------------------------------------------------------
#pragma mark - Enumerations
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Plot Types
///-----------------------------------------------------------
@@ -37,17 +31,15 @@
/**
The types of plots that can be displayed in the view using the data.
*/
typedef NS_ENUM(NSInteger, EZPlotType)
{
/**
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
*/
EZPlotTypeRolling
typedef NS_ENUM(NSInteger,EZPlotType){
/**
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
*/
EZPlotTypeRolling
};
/**
@@ -65,10 +57,7 @@ typedef NS_ENUM(NSInteger, EZPlotType)
@interface EZPlot : NSView
#endif
//------------------------------------------------------------------------------
#pragma mark - Properties
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Customizing The Plot's Appearance
///-----------------------------------------------------------
@@ -102,10 +91,7 @@ typedef NS_ENUM(NSInteger, EZPlotType)
*/
@property (nonatomic,assign,setter=setShouldMirror:) BOOL shouldMirror;
//------------------------------------------------------------------------------
#pragma mark - Clearing
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Clearing The Plot
///-----------------------------------------------------------
@@ -115,10 +101,7 @@ typedef NS_ENUM(NSInteger, EZPlotType)
*/
-(void)clear;
//------------------------------------------------------------------------------
#pragma mark - Get Samples
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Updating The Plot
///-----------------------------------------------------------
@@ -3,7 +3,7 @@
// EZAudio
//
// Created by Syed Haris Ali on 11/24/13.
// Copyright (c) 2015 Syed Haris Ali. All rights reserved.
// Copyright (c) 2013 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
@@ -25,6 +25,10 @@
#import "EZPlot.h"
@interface EZPlot ()
@end
@implementation EZPlot
#pragma mark - Clearing
@@ -3,7 +3,7 @@
// EZAudio
//
// Created by Syed Haris Ali on 12/1/13.
// Copyright (c) 2015 Syed Haris Ali. All rights reserved.
// Copyright (c) 2013 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
@@ -3,7 +3,7 @@
// EZAudio
//
// Created by Syed Haris Ali on 12/1/13.
// Copyright (c) 2015 Syed Haris Ali. All rights reserved.
// Copyright (c) 2013 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
@@ -24,7 +24,8 @@
// THE SOFTWARE.
#import "EZRecorder.h"
#import "EZAudioUtilities.h"
#import "EZAudio.h"
@interface EZRecorder (){
ExtAudioFileRef _destinationFile;
@@ -44,7 +45,7 @@
destinationFileType:(EZRecorderFileType)destinationFileType
{
self = [super init];
if (self)
if( self )
{
// Set defaults
_destinationFile = NULL;
@@ -76,23 +77,23 @@
withSourceFormat:(AudioStreamBasicDescription)sourceFormat
{
AudioStreamBasicDescription asbd;
switch ( fileType)
switch ( fileType )
{
case EZRecorderFileTypeAIFF:
asbd = [EZAudioUtilities AIFFFormatWithNumberOfChannels:sourceFormat.mChannelsPerFrame
sampleRate:sourceFormat.mSampleRate];
asbd = [EZAudio AIFFFormatWithNumberOfChannels:sourceFormat.mChannelsPerFrame
sampleRate:sourceFormat.mSampleRate];
break;
case EZRecorderFileTypeM4A:
asbd = [EZAudioUtilities M4AFormatWithNumberOfChannels:sourceFormat.mChannelsPerFrame
sampleRate:sourceFormat.mSampleRate];
asbd = [EZAudio M4AFormatWithNumberOfChannels:sourceFormat.mChannelsPerFrame
sampleRate:sourceFormat.mSampleRate];
break;
case EZRecorderFileTypeWAV:
asbd = [EZAudioUtilities stereoFloatInterleavedFormatWithSampleRate:sourceFormat.mSampleRate];
asbd = [EZAudio stereoFloatInterleavedFormatWithSampleRate:sourceFormat.mSampleRate];
break;
default:
asbd = [EZAudioUtilities stereoCanonicalNonInterleavedFormatWithSampleRate:sourceFormat.mSampleRate];
asbd = [EZAudio stereoCanonicalNonInterleavedFormatWithSampleRate:sourceFormat.mSampleRate];
break;
}
return asbd;
@@ -102,7 +103,7 @@
withSourceFormat:(AudioStreamBasicDescription)sourceFormat
{
AudioFileTypeID audioFileTypeID;
switch ( fileType)
switch ( fileType )
{
case EZRecorderFileTypeAIFF:
audioFileTypeID = kAudioFileAIFFType;
@@ -127,28 +128,28 @@
{
// Finish filling out the destination format description
UInt32 propSize = sizeof(_destinationFormat);
[EZAudioUtilities checkResult:AudioFormatGetProperty(kAudioFormatProperty_FormatInfo,
0,
NULL,
&propSize,
&_destinationFormat)
operation:"Failed to fill out rest of destination format"];
[EZAudio checkResult:AudioFormatGetProperty(kAudioFormatProperty_FormatInfo,
0,
NULL,
&propSize,
&_destinationFormat)
operation:"Failed to fill out rest of destination format"];
// Create the audio file
[EZAudioUtilities checkResult:ExtAudioFileCreateWithURL(_destinationFileURL,
_destinationFileTypeID,
&_destinationFormat,
NULL,
kAudioFileFlags_EraseFile,
&_destinationFile)
operation:"Failed to create 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)
[EZAudioUtilities checkResult:ExtAudioFileSetProperty(_destinationFile,
kExtAudioFileProperty_ClientDataFormat,
sizeof(_sourceFormat),
&_sourceFormat)
operation:"Failed to set client format on recorded audio file"];
[EZAudio checkResult:ExtAudioFileSetProperty(_destinationFile,
kExtAudioFileProperty_ClientDataFormat,
sizeof(_sourceFormat),
&_sourceFormat)
operation:"Failed to set client format on recorded audio file"];
}
@@ -156,22 +157,22 @@
-(void)appendDataFromBufferList:(AudioBufferList *)bufferList
withBufferSize:(UInt32)bufferSize
{
if (_destinationFile)
if( _destinationFile )
{
[EZAudioUtilities checkResult:ExtAudioFileWriteAsync(_destinationFile,
bufferSize,
bufferList)
[EZAudio checkResult:ExtAudioFileWriteAsync(_destinationFile,
bufferSize,
bufferList)
operation:"Failed to write audio data to recorded audio file"];
}
}
-(void)closeAudioFile
{
if (_destinationFile)
if( _destinationFile )
{
// Dispose of the audio file reference
[EZAudioUtilities checkResult:ExtAudioFileDispose(_destinationFile)
operation:"Failed to close audio file"];
[EZAudio checkResult:ExtAudioFileDispose(_destinationFile)
operation:"Failed to close audio file"];
// Null out the file reference
_destinationFile = NULL;
@@ -1,444 +0,0 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
6611CE781B45EB3200AE0EE8 /* EZAudioDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = 6611CE571B45EB3200AE0EE8 /* EZAudioDevice.h */; settings = {ATTRIBUTES = (Public, ); }; };
6611CE791B45EB3200AE0EE8 /* EZAudioDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = 6611CE581B45EB3200AE0EE8 /* EZAudioDevice.m */; };
6611CE7A1B45EB3200AE0EE8 /* EZAudioFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 6611CE591B45EB3200AE0EE8 /* EZAudioFile.h */; settings = {ATTRIBUTES = (Public, ); }; };
6611CE7B1B45EB3200AE0EE8 /* EZAudioFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 6611CE5A1B45EB3200AE0EE8 /* EZAudioFile.m */; };
6611CE7C1B45EB3200AE0EE8 /* EZAudioPlayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 6611CE5B1B45EB3200AE0EE8 /* EZAudioPlayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
6611CE7D1B45EB3200AE0EE8 /* EZAudioPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 6611CE5C1B45EB3200AE0EE8 /* EZAudioPlayer.m */; };
6611CE7E1B45EB3200AE0EE8 /* EZMicrophone.h in Headers */ = {isa = PBXBuildFile; fileRef = 6611CE5D1B45EB3200AE0EE8 /* EZMicrophone.h */; settings = {ATTRIBUTES = (Public, ); }; };
6611CE7F1B45EB3200AE0EE8 /* EZMicrophone.m in Sources */ = {isa = PBXBuildFile; fileRef = 6611CE5E1B45EB3200AE0EE8 /* EZMicrophone.m */; };
6611CE801B45EB3200AE0EE8 /* EZOutput.h in Headers */ = {isa = PBXBuildFile; fileRef = 6611CE5F1B45EB3200AE0EE8 /* EZOutput.h */; settings = {ATTRIBUTES = (Public, ); }; };
6611CE811B45EB3200AE0EE8 /* EZOutput.m in Sources */ = {isa = PBXBuildFile; fileRef = 6611CE601B45EB3200AE0EE8 /* EZOutput.m */; };
6611CE821B45EB3200AE0EE8 /* EZRecorder.h in Headers */ = {isa = PBXBuildFile; fileRef = 6611CE611B45EB3200AE0EE8 /* EZRecorder.h */; settings = {ATTRIBUTES = (Public, ); }; };
6611CE831B45EB3200AE0EE8 /* EZRecorder.m in Sources */ = {isa = PBXBuildFile; fileRef = 6611CE621B45EB3200AE0EE8 /* EZRecorder.m */; };
6611CE841B45EB3200AE0EE8 /* TPCircularBuffer.c in Sources */ = {isa = PBXBuildFile; fileRef = 6611CE641B45EB3200AE0EE8 /* TPCircularBuffer.c */; };
6611CE851B45EB3200AE0EE8 /* TPCircularBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 6611CE651B45EB3200AE0EE8 /* TPCircularBuffer.h */; settings = {ATTRIBUTES = (Public, ); }; };
6611CE861B45EB3200AE0EE8 /* EZAudio.h in Headers */ = {isa = PBXBuildFile; fileRef = 6611CE661B45EB3200AE0EE8 /* EZAudio.h */; settings = {ATTRIBUTES = (Public, ); }; };
6611CE871B45EB3200AE0EE8 /* EZAudio.m in Sources */ = {isa = PBXBuildFile; fileRef = 6611CE671B45EB3200AE0EE8 /* EZAudio.m */; };
6611CE881B45EB3200AE0EE8 /* EZAudioDisplayLink.h in Headers */ = {isa = PBXBuildFile; fileRef = 6611CE691B45EB3200AE0EE8 /* EZAudioDisplayLink.h */; settings = {ATTRIBUTES = (Public, ); }; };
6611CE891B45EB3200AE0EE8 /* EZAudioDisplayLink.m in Sources */ = {isa = PBXBuildFile; fileRef = 6611CE6A1B45EB3200AE0EE8 /* EZAudioDisplayLink.m */; };
6611CE8A1B45EB3200AE0EE8 /* EZAudioPlot.h in Headers */ = {isa = PBXBuildFile; fileRef = 6611CE6B1B45EB3200AE0EE8 /* EZAudioPlot.h */; settings = {ATTRIBUTES = (Public, ); }; };
6611CE8B1B45EB3200AE0EE8 /* EZAudioPlot.m in Sources */ = {isa = PBXBuildFile; fileRef = 6611CE6C1B45EB3200AE0EE8 /* EZAudioPlot.m */; };
6611CE8C1B45EB3200AE0EE8 /* EZAudioPlotGL.h in Headers */ = {isa = PBXBuildFile; fileRef = 6611CE6D1B45EB3200AE0EE8 /* EZAudioPlotGL.h */; settings = {ATTRIBUTES = (Public, ); }; };
6611CE8D1B45EB3200AE0EE8 /* EZAudioPlotGL.m in Sources */ = {isa = PBXBuildFile; fileRef = 6611CE6E1B45EB3200AE0EE8 /* EZAudioPlotGL.m */; };
6611CE8E1B45EB3200AE0EE8 /* EZPlot.h in Headers */ = {isa = PBXBuildFile; fileRef = 6611CE6F1B45EB3200AE0EE8 /* EZPlot.h */; settings = {ATTRIBUTES = (Public, ); }; };
6611CE8F1B45EB3200AE0EE8 /* EZPlot.m in Sources */ = {isa = PBXBuildFile; fileRef = 6611CE701B45EB3200AE0EE8 /* EZPlot.m */; };
6611CE901B45EB3200AE0EE8 /* EZAudioFloatConverter.h in Headers */ = {isa = PBXBuildFile; fileRef = 6611CE721B45EB3200AE0EE8 /* EZAudioFloatConverter.h */; settings = {ATTRIBUTES = (Public, ); }; };
6611CE911B45EB3200AE0EE8 /* EZAudioFloatConverter.m in Sources */ = {isa = PBXBuildFile; fileRef = 6611CE731B45EB3200AE0EE8 /* EZAudioFloatConverter.m */; };
6611CE921B45EB3200AE0EE8 /* EZAudioFloatData.h in Headers */ = {isa = PBXBuildFile; fileRef = 6611CE741B45EB3200AE0EE8 /* EZAudioFloatData.h */; settings = {ATTRIBUTES = (Public, ); }; };
6611CE931B45EB3200AE0EE8 /* EZAudioFloatData.m in Sources */ = {isa = PBXBuildFile; fileRef = 6611CE751B45EB3200AE0EE8 /* EZAudioFloatData.m */; };
6611CE941B45EB3200AE0EE8 /* EZAudioUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 6611CE761B45EB3200AE0EE8 /* EZAudioUtilities.h */; settings = {ATTRIBUTES = (Public, ); }; };
6611CE951B45EB3200AE0EE8 /* EZAudioUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 6611CE771B45EB3200AE0EE8 /* EZAudioUtilities.m */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
6611CE571B45EB3200AE0EE8 /* EZAudioDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioDevice.h; sourceTree = "<group>"; };
6611CE581B45EB3200AE0EE8 /* EZAudioDevice.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioDevice.m; sourceTree = "<group>"; };
6611CE591B45EB3200AE0EE8 /* EZAudioFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioFile.h; sourceTree = "<group>"; };
6611CE5A1B45EB3200AE0EE8 /* EZAudioFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioFile.m; sourceTree = "<group>"; };
6611CE5B1B45EB3200AE0EE8 /* EZAudioPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioPlayer.h; sourceTree = "<group>"; };
6611CE5C1B45EB3200AE0EE8 /* EZAudioPlayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioPlayer.m; sourceTree = "<group>"; };
6611CE5D1B45EB3200AE0EE8 /* EZMicrophone.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZMicrophone.h; sourceTree = "<group>"; };
6611CE5E1B45EB3200AE0EE8 /* EZMicrophone.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZMicrophone.m; sourceTree = "<group>"; };
6611CE5F1B45EB3200AE0EE8 /* EZOutput.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZOutput.h; sourceTree = "<group>"; };
6611CE601B45EB3200AE0EE8 /* EZOutput.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZOutput.m; sourceTree = "<group>"; };
6611CE611B45EB3200AE0EE8 /* EZRecorder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZRecorder.h; sourceTree = "<group>"; };
6611CE621B45EB3200AE0EE8 /* EZRecorder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZRecorder.m; sourceTree = "<group>"; };
6611CE641B45EB3200AE0EE8 /* TPCircularBuffer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = TPCircularBuffer.c; sourceTree = "<group>"; };
6611CE651B45EB3200AE0EE8 /* TPCircularBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPCircularBuffer.h; sourceTree = "<group>"; };
6611CE661B45EB3200AE0EE8 /* EZAudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EZAudio.h; path = ../../../Classes/EZAudio.h; sourceTree = "<group>"; };
6611CE671B45EB3200AE0EE8 /* EZAudio.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = EZAudio.m; path = ../../../Classes/EZAudio.m; sourceTree = "<group>"; };
6611CE691B45EB3200AE0EE8 /* EZAudioDisplayLink.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioDisplayLink.h; sourceTree = "<group>"; };
6611CE6A1B45EB3200AE0EE8 /* EZAudioDisplayLink.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioDisplayLink.m; sourceTree = "<group>"; };
6611CE6B1B45EB3200AE0EE8 /* EZAudioPlot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioPlot.h; sourceTree = "<group>"; };
6611CE6C1B45EB3200AE0EE8 /* EZAudioPlot.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioPlot.m; sourceTree = "<group>"; };
6611CE6D1B45EB3200AE0EE8 /* EZAudioPlotGL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioPlotGL.h; sourceTree = "<group>"; };
6611CE6E1B45EB3200AE0EE8 /* EZAudioPlotGL.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioPlotGL.m; sourceTree = "<group>"; };
6611CE6F1B45EB3200AE0EE8 /* EZPlot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZPlot.h; sourceTree = "<group>"; };
6611CE701B45EB3200AE0EE8 /* EZPlot.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZPlot.m; sourceTree = "<group>"; };
6611CE721B45EB3200AE0EE8 /* EZAudioFloatConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioFloatConverter.h; sourceTree = "<group>"; };
6611CE731B45EB3200AE0EE8 /* EZAudioFloatConverter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioFloatConverter.m; sourceTree = "<group>"; };
6611CE741B45EB3200AE0EE8 /* EZAudioFloatData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioFloatData.h; sourceTree = "<group>"; };
6611CE751B45EB3200AE0EE8 /* EZAudioFloatData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioFloatData.m; sourceTree = "<group>"; };
6611CE761B45EB3200AE0EE8 /* EZAudioUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioUtilities.h; sourceTree = "<group>"; };
6611CE771B45EB3200AE0EE8 /* EZAudioUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioUtilities.m; sourceTree = "<group>"; };
662427121B4510F30069FFD7 /* EZAudio.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = EZAudio.framework; sourceTree = BUILT_PRODUCTS_DIR; };
662427161B4510F30069FFD7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
6624270E1B4510F30069FFD7 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
6611CE561B45EB3200AE0EE8 /* Core Components */ = {
isa = PBXGroup;
children = (
6611CE571B45EB3200AE0EE8 /* EZAudioDevice.h */,
6611CE581B45EB3200AE0EE8 /* EZAudioDevice.m */,
6611CE591B45EB3200AE0EE8 /* EZAudioFile.h */,
6611CE5A1B45EB3200AE0EE8 /* EZAudioFile.m */,
6611CE5B1B45EB3200AE0EE8 /* EZAudioPlayer.h */,
6611CE5C1B45EB3200AE0EE8 /* EZAudioPlayer.m */,
6611CE5D1B45EB3200AE0EE8 /* EZMicrophone.h */,
6611CE5E1B45EB3200AE0EE8 /* EZMicrophone.m */,
6611CE5F1B45EB3200AE0EE8 /* EZOutput.h */,
6611CE601B45EB3200AE0EE8 /* EZOutput.m */,
6611CE611B45EB3200AE0EE8 /* EZRecorder.h */,
6611CE621B45EB3200AE0EE8 /* EZRecorder.m */,
);
name = "Core Components";
path = "../../../Classes/Core Components";
sourceTree = "<group>";
};
6611CE631B45EB3200AE0EE8 /* External */ = {
isa = PBXGroup;
children = (
6611CE641B45EB3200AE0EE8 /* TPCircularBuffer.c */,
6611CE651B45EB3200AE0EE8 /* TPCircularBuffer.h */,
);
name = External;
path = ../../../Classes/External;
sourceTree = "<group>";
};
6611CE681B45EB3200AE0EE8 /* Interface Components */ = {
isa = PBXGroup;
children = (
6611CE691B45EB3200AE0EE8 /* EZAudioDisplayLink.h */,
6611CE6A1B45EB3200AE0EE8 /* EZAudioDisplayLink.m */,
6611CE6B1B45EB3200AE0EE8 /* EZAudioPlot.h */,
6611CE6C1B45EB3200AE0EE8 /* EZAudioPlot.m */,
6611CE6D1B45EB3200AE0EE8 /* EZAudioPlotGL.h */,
6611CE6E1B45EB3200AE0EE8 /* EZAudioPlotGL.m */,
6611CE6F1B45EB3200AE0EE8 /* EZPlot.h */,
6611CE701B45EB3200AE0EE8 /* EZPlot.m */,
);
name = "Interface Components";
path = "../../../Classes/Interface Components";
sourceTree = "<group>";
};
6611CE711B45EB3200AE0EE8 /* Utility Components */ = {
isa = PBXGroup;
children = (
6611CE721B45EB3200AE0EE8 /* EZAudioFloatConverter.h */,
6611CE731B45EB3200AE0EE8 /* EZAudioFloatConverter.m */,
6611CE741B45EB3200AE0EE8 /* EZAudioFloatData.h */,
6611CE751B45EB3200AE0EE8 /* EZAudioFloatData.m */,
6611CE761B45EB3200AE0EE8 /* EZAudioUtilities.h */,
6611CE771B45EB3200AE0EE8 /* EZAudioUtilities.m */,
);
name = "Utility Components";
path = "../../../Classes/Utility Components";
sourceTree = "<group>";
};
662427081B4510F30069FFD7 = {
isa = PBXGroup;
children = (
662427141B4510F30069FFD7 /* EZAudio */,
662427131B4510F30069FFD7 /* Products */,
);
sourceTree = "<group>";
};
662427131B4510F30069FFD7 /* Products */ = {
isa = PBXGroup;
children = (
662427121B4510F30069FFD7 /* EZAudio.framework */,
);
name = Products;
sourceTree = "<group>";
};
662427141B4510F30069FFD7 /* EZAudio */ = {
isa = PBXGroup;
children = (
6611CE661B45EB3200AE0EE8 /* EZAudio.h */,
6611CE671B45EB3200AE0EE8 /* EZAudio.m */,
6611CE561B45EB3200AE0EE8 /* Core Components */,
6611CE631B45EB3200AE0EE8 /* External */,
6611CE681B45EB3200AE0EE8 /* Interface Components */,
6611CE711B45EB3200AE0EE8 /* Utility Components */,
662427151B4510F30069FFD7 /* Supporting Files */,
);
path = EZAudio;
sourceTree = "<group>";
};
662427151B4510F30069FFD7 /* Supporting Files */ = {
isa = PBXGroup;
children = (
662427161B4510F30069FFD7 /* Info.plist */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
6624270F1B4510F30069FFD7 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
6611CE8C1B45EB3200AE0EE8 /* EZAudioPlotGL.h in Headers */,
6611CE861B45EB3200AE0EE8 /* EZAudio.h in Headers */,
6611CE7A1B45EB3200AE0EE8 /* EZAudioFile.h in Headers */,
6611CE781B45EB3200AE0EE8 /* EZAudioDevice.h in Headers */,
6611CE8A1B45EB3200AE0EE8 /* EZAudioPlot.h in Headers */,
6611CE881B45EB3200AE0EE8 /* EZAudioDisplayLink.h in Headers */,
6611CE7C1B45EB3200AE0EE8 /* EZAudioPlayer.h in Headers */,
6611CE921B45EB3200AE0EE8 /* EZAudioFloatData.h in Headers */,
6611CE851B45EB3200AE0EE8 /* TPCircularBuffer.h in Headers */,
6611CE8E1B45EB3200AE0EE8 /* EZPlot.h in Headers */,
6611CE801B45EB3200AE0EE8 /* EZOutput.h in Headers */,
6611CE901B45EB3200AE0EE8 /* EZAudioFloatConverter.h in Headers */,
6611CE941B45EB3200AE0EE8 /* EZAudioUtilities.h in Headers */,
6611CE7E1B45EB3200AE0EE8 /* EZMicrophone.h in Headers */,
6611CE821B45EB3200AE0EE8 /* EZRecorder.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
662427111B4510F30069FFD7 /* EZAudio */ = {
isa = PBXNativeTarget;
buildConfigurationList = 662427281B4510F30069FFD7 /* Build configuration list for PBXNativeTarget "EZAudio" */;
buildPhases = (
6624270D1B4510F30069FFD7 /* Sources */,
6624270E1B4510F30069FFD7 /* Frameworks */,
6624270F1B4510F30069FFD7 /* Headers */,
662427101B4510F30069FFD7 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = EZAudio;
productName = EZAudio;
productReference = 662427121B4510F30069FFD7 /* EZAudio.framework */;
productType = "com.apple.product-type.framework";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
662427091B4510F30069FFD7 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0640;
ORGANIZATIONNAME = "Syed Haris Ali";
TargetAttributes = {
662427111B4510F30069FFD7 = {
CreatedOnToolsVersion = 6.4;
};
};
};
buildConfigurationList = 6624270C1B4510F30069FFD7 /* Build configuration list for PBXProject "EZAudio" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = 662427081B4510F30069FFD7;
productRefGroup = 662427131B4510F30069FFD7 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
662427111B4510F30069FFD7 /* EZAudio */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
662427101B4510F30069FFD7 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
6624270D1B4510F30069FFD7 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
6611CE8D1B45EB3200AE0EE8 /* EZAudioPlotGL.m in Sources */,
6611CE841B45EB3200AE0EE8 /* TPCircularBuffer.c in Sources */,
6611CE891B45EB3200AE0EE8 /* EZAudioDisplayLink.m in Sources */,
6611CE831B45EB3200AE0EE8 /* EZRecorder.m in Sources */,
6611CE7F1B45EB3200AE0EE8 /* EZMicrophone.m in Sources */,
6611CE791B45EB3200AE0EE8 /* EZAudioDevice.m in Sources */,
6611CE7B1B45EB3200AE0EE8 /* EZAudioFile.m in Sources */,
6611CE951B45EB3200AE0EE8 /* EZAudioUtilities.m in Sources */,
6611CE871B45EB3200AE0EE8 /* EZAudio.m in Sources */,
6611CE811B45EB3200AE0EE8 /* EZOutput.m in Sources */,
6611CE8F1B45EB3200AE0EE8 /* EZPlot.m in Sources */,
6611CE931B45EB3200AE0EE8 /* EZAudioFloatData.m in Sources */,
6611CE8B1B45EB3200AE0EE8 /* EZAudioPlot.m in Sources */,
6611CE911B45EB3200AE0EE8 /* EZAudioFloatConverter.m in Sources */,
6611CE7D1B45EB3200AE0EE8 /* EZAudioPlayer.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
662427261B4510F30069FFD7 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.8;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
SYMROOT = build;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Debug;
};
662427271B4510F30069FFD7 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.8;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = macosx;
SYMROOT = build;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Release;
};
662427291B4510F30069FFD7 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
COMBINE_HIDPI_IMAGES = YES;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_VERSION = A;
INFOPLIST_FILE = EZAudio/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SYMROOT = build;
};
name = Debug;
};
6624272A1B4510F30069FFD7 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
COMBINE_HIDPI_IMAGES = YES;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
FRAMEWORK_VERSION = A;
INFOPLIST_FILE = EZAudio/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SYMROOT = build;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
6624270C1B4510F30069FFD7 /* Build configuration list for PBXProject "EZAudio" */ = {
isa = XCConfigurationList;
buildConfigurations = (
662427261B4510F30069FFD7 /* Debug */,
662427271B4510F30069FFD7 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
662427281B4510F30069FFD7 /* Build configuration list for PBXNativeTarget "EZAudio" */ = {
isa = XCConfigurationList;
buildConfigurations = (
662427291B4510F30069FFD7 /* Debug */,
6624272A1B4510F30069FFD7 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 662427091B4510F30069FFD7 /* Project object */;
}
@@ -33,7 +33,7 @@
#define reportResult(result,operation) (_reportResult((result),(operation),strrchr(__FILE__, '/')+1,__LINE__))
static inline bool _reportResult(kern_return_t result, const char *operation, const char* file, int line) {
if (result != ERR_SUCCESS) {
if ( result != ERR_SUCCESS ) {
printf("%s:%d: %s: %s\n", file, line, operation, mach_error_string(result));
return false;
}
@@ -44,7 +44,7 @@ bool TPCircularBufferInit(TPCircularBuffer *buffer, int length) {
// Keep trying until we get our buffer, needed to handle race conditions
int retries = 3;
while ( true) {
while ( true ) {
buffer->length = (int32_t)round_page(length); // We need whole page sizes
@@ -55,8 +55,8 @@ bool TPCircularBufferInit(TPCircularBuffer *buffer, int length) {
&bufferAddress,
buffer->length * 2,
VM_FLAGS_ANYWHERE); // allocate anywhere it'll fit
if (result != ERR_SUCCESS) {
if (retries-- == 0) {
if ( result != ERR_SUCCESS ) {
if ( retries-- == 0 ) {
reportResult(result, "Buffer allocation");
return false;
}
@@ -68,8 +68,8 @@ bool TPCircularBufferInit(TPCircularBuffer *buffer, int length) {
result = vm_deallocate(mach_task_self(),
bufferAddress + buffer->length,
buffer->length);
if (result != ERR_SUCCESS) {
if (retries-- == 0) {
if ( result != ERR_SUCCESS ) {
if ( retries-- == 0 ) {
reportResult(result, "Buffer deallocation");
return false;
}
@@ -92,8 +92,8 @@ bool TPCircularBufferInit(TPCircularBuffer *buffer, int length) {
&cur_prot, // unused protection struct
&max_prot, // unused protection struct
VM_INHERIT_DEFAULT);
if (result != ERR_SUCCESS) {
if (retries-- == 0) {
if ( result != ERR_SUCCESS ) {
if ( retries-- == 0 ) {
reportResult(result, "Remap buffer memory");
return false;
}
@@ -102,9 +102,9 @@ bool TPCircularBufferInit(TPCircularBuffer *buffer, int length) {
continue;
}
if (virtualAddress != bufferAddress+buffer->length) {
if ( virtualAddress != bufferAddress+buffer->length ) {
// If the memory is not contiguous, clean up both allocated buffers and try again
if (retries-- == 0) {
if ( retries-- == 0 ) {
printf("Couldn't map buffer memory to end of buffer\n");
return false;
}
@@ -130,7 +130,7 @@ void TPCircularBufferCleanup(TPCircularBuffer *buffer) {
void TPCircularBufferClear(TPCircularBuffer *buffer) {
int32_t fillCount;
if (TPCircularBufferTail(buffer, &fillCount)) {
if ( TPCircularBufferTail(buffer, &fillCount) ) {
TPCircularBufferConsume(buffer, fillCount);
}
}
@@ -101,7 +101,7 @@ void TPCircularBufferClear(TPCircularBuffer *buffer);
*/
static __inline__ __attribute__((always_inline)) void* TPCircularBufferTail(TPCircularBuffer *buffer, int32_t* availableBytes) {
*availableBytes = buffer->fillCount;
if (*availableBytes == 0) return NULL;
if ( *availableBytes == 0 ) return NULL;
return (void*)((char*)buffer->buffer + buffer->tail);
}
@@ -140,7 +140,7 @@ static __inline__ __attribute__((always_inline)) void TPCircularBufferConsumeNoB
*/
static __inline__ __attribute__((always_inline)) void* TPCircularBufferHead(TPCircularBuffer *buffer, int32_t* availableBytes) {
*availableBytes = (buffer->length - buffer->fillCount);
if (*availableBytes == 0) return NULL;
if ( *availableBytes == 0 ) return NULL;
return (void*)((char*)buffer->buffer + buffer->head);
}
@@ -182,7 +182,7 @@ static __inline__ __attribute__((always_inline)) void TPCircularBufferProduceNoB
static __inline__ __attribute__((always_inline)) bool TPCircularBufferProduceBytes(TPCircularBuffer *buffer, const void* src, int32_t len) {
int32_t space;
void *ptr = TPCircularBufferHead(buffer, &space);
if (space < len) return false;
if ( space < len ) return false;
memcpy(ptr, src, len);
TPCircularBufferProduce(buffer, len);
return true;
+51
View File
@@ -0,0 +1,51 @@
EZAudio
Created by Syed Haris Ali
Copyright (c) 2013 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.
==========================================================================================
0.0.1
Initial release.
Components include: EZAudioFile, EZAudioPlot, EZAudioPlotGL, EZMicrophone, EZOutput, and EZRecorder.
Provided 6 example projects: CoreGraphicsWaveform, OpenGLWaveform, WaveformFromFile, PassThrough, Record, and PlayFile.
0.0.2
Fix for Cocoapod spec. Forgot to include files with .c extension.
0.0.3
Changing EZAudioPlot and EZAudioPlotGL to scroll for the EZPlotTypeRolling instead of wiping the screen clean when hitting the end
Allowed EZMicrophone EZOutput to have custom AudioStreamBasicDescription setters
Fixed bug in EZAudioFile's getWaveformData function where it was not exiting after sending back cached waveform data (rereading from the audio file)
Added stereo support for EZMicrophone, EZAudioFile, EZRecorder, and EZOutput
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)
+26
View File
@@ -0,0 +1,26 @@
EZAudio
Created by Syed Haris Ali
Copyright (c) 2013 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.
==========================================================================================
0.0.5
@@ -1,443 +0,0 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
6611CE181B45CE2400AE0EE8 /* EZAudio.h in Headers */ = {isa = PBXBuildFile; fileRef = 6611CDF71B45CE2400AE0EE8 /* EZAudio.h */; settings = {ATTRIBUTES = (Public, ); }; };
6611CE191B45CE2400AE0EE8 /* EZAudio.m in Sources */ = {isa = PBXBuildFile; fileRef = 6611CDF81B45CE2400AE0EE8 /* EZAudio.m */; };
6611CE1A1B45CE2400AE0EE8 /* EZAudioDevice.h in Headers */ = {isa = PBXBuildFile; fileRef = 6611CDF91B45CE2400AE0EE8 /* EZAudioDevice.h */; settings = {ATTRIBUTES = (Public, ); }; };
6611CE1B1B45CE2400AE0EE8 /* EZAudioDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = 6611CDFA1B45CE2400AE0EE8 /* EZAudioDevice.m */; };
6611CE1C1B45CE2400AE0EE8 /* EZAudioDisplayLink.h in Headers */ = {isa = PBXBuildFile; fileRef = 6611CDFB1B45CE2400AE0EE8 /* EZAudioDisplayLink.h */; settings = {ATTRIBUTES = (Public, ); }; };
6611CE1D1B45CE2400AE0EE8 /* EZAudioDisplayLink.m in Sources */ = {isa = PBXBuildFile; fileRef = 6611CDFC1B45CE2400AE0EE8 /* EZAudioDisplayLink.m */; };
6611CE1E1B45CE2400AE0EE8 /* EZAudioFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 6611CDFD1B45CE2400AE0EE8 /* EZAudioFile.h */; settings = {ATTRIBUTES = (Public, ); }; };
6611CE1F1B45CE2400AE0EE8 /* EZAudioFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 6611CDFE1B45CE2400AE0EE8 /* EZAudioFile.m */; };
6611CE201B45CE2400AE0EE8 /* EZAudioFloatConverter.h in Headers */ = {isa = PBXBuildFile; fileRef = 6611CDFF1B45CE2400AE0EE8 /* EZAudioFloatConverter.h */; settings = {ATTRIBUTES = (Public, ); }; };
6611CE211B45CE2400AE0EE8 /* EZAudioFloatConverter.m in Sources */ = {isa = PBXBuildFile; fileRef = 6611CE001B45CE2400AE0EE8 /* EZAudioFloatConverter.m */; };
6611CE221B45CE2400AE0EE8 /* EZAudioFloatData.h in Headers */ = {isa = PBXBuildFile; fileRef = 6611CE011B45CE2400AE0EE8 /* EZAudioFloatData.h */; settings = {ATTRIBUTES = (Public, ); }; };
6611CE231B45CE2400AE0EE8 /* EZAudioFloatData.m in Sources */ = {isa = PBXBuildFile; fileRef = 6611CE021B45CE2400AE0EE8 /* EZAudioFloatData.m */; };
6611CE241B45CE2400AE0EE8 /* EZAudioPlayer.h in Headers */ = {isa = PBXBuildFile; fileRef = 6611CE031B45CE2400AE0EE8 /* EZAudioPlayer.h */; settings = {ATTRIBUTES = (Public, ); }; };
6611CE251B45CE2400AE0EE8 /* EZAudioPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 6611CE041B45CE2400AE0EE8 /* EZAudioPlayer.m */; };
6611CE261B45CE2400AE0EE8 /* EZAudioPlot.h in Headers */ = {isa = PBXBuildFile; fileRef = 6611CE051B45CE2400AE0EE8 /* EZAudioPlot.h */; settings = {ATTRIBUTES = (Public, ); }; };
6611CE271B45CE2400AE0EE8 /* EZAudioPlot.m in Sources */ = {isa = PBXBuildFile; fileRef = 6611CE061B45CE2400AE0EE8 /* EZAudioPlot.m */; };
6611CE281B45CE2400AE0EE8 /* EZAudioPlotGL.h in Headers */ = {isa = PBXBuildFile; fileRef = 6611CE071B45CE2400AE0EE8 /* EZAudioPlotGL.h */; settings = {ATTRIBUTES = (Public, ); }; };
6611CE291B45CE2400AE0EE8 /* EZAudioPlotGL.m in Sources */ = {isa = PBXBuildFile; fileRef = 6611CE081B45CE2400AE0EE8 /* EZAudioPlotGL.m */; };
6611CE2A1B45CE2400AE0EE8 /* EZAudioUtilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 6611CE091B45CE2400AE0EE8 /* EZAudioUtilities.h */; settings = {ATTRIBUTES = (Public, ); }; };
6611CE2B1B45CE2400AE0EE8 /* EZAudioUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 6611CE0A1B45CE2400AE0EE8 /* EZAudioUtilities.m */; };
6611CE2C1B45CE2400AE0EE8 /* EZMicrophone.h in Headers */ = {isa = PBXBuildFile; fileRef = 6611CE0B1B45CE2400AE0EE8 /* EZMicrophone.h */; settings = {ATTRIBUTES = (Public, ); }; };
6611CE2D1B45CE2400AE0EE8 /* EZMicrophone.m in Sources */ = {isa = PBXBuildFile; fileRef = 6611CE0C1B45CE2400AE0EE8 /* EZMicrophone.m */; };
6611CE2E1B45CE2400AE0EE8 /* EZOutput.h in Headers */ = {isa = PBXBuildFile; fileRef = 6611CE0D1B45CE2400AE0EE8 /* EZOutput.h */; settings = {ATTRIBUTES = (Public, ); }; };
6611CE2F1B45CE2400AE0EE8 /* EZOutput.m in Sources */ = {isa = PBXBuildFile; fileRef = 6611CE0E1B45CE2400AE0EE8 /* EZOutput.m */; };
6611CE301B45CE2400AE0EE8 /* EZPlot.h in Headers */ = {isa = PBXBuildFile; fileRef = 6611CE0F1B45CE2400AE0EE8 /* EZPlot.h */; settings = {ATTRIBUTES = (Public, ); }; };
6611CE311B45CE2400AE0EE8 /* EZPlot.m in Sources */ = {isa = PBXBuildFile; fileRef = 6611CE101B45CE2400AE0EE8 /* EZPlot.m */; };
6611CE321B45CE2400AE0EE8 /* EZRecorder.h in Headers */ = {isa = PBXBuildFile; fileRef = 6611CE111B45CE2400AE0EE8 /* EZRecorder.h */; settings = {ATTRIBUTES = (Public, ); }; };
6611CE331B45CE2400AE0EE8 /* EZRecorder.m in Sources */ = {isa = PBXBuildFile; fileRef = 6611CE121B45CE2400AE0EE8 /* EZRecorder.m */; };
6611CE341B45CE2400AE0EE8 /* TPCircularBuffer.c in Sources */ = {isa = PBXBuildFile; fileRef = 6611CE131B45CE2400AE0EE8 /* TPCircularBuffer.c */; };
6611CE351B45CE2400AE0EE8 /* TPCircularBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 6611CE141B45CE2400AE0EE8 /* TPCircularBuffer.h */; settings = {ATTRIBUTES = (Public, ); }; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
6611CDDA1B45CDD800AE0EE8 /* EZAudio.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = EZAudio.framework; sourceTree = BUILT_PRODUCTS_DIR; };
6611CDDE1B45CDD800AE0EE8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
6611CDF71B45CE2400AE0EE8 /* EZAudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EZAudio.h; path = ../../../Classes/EZAudio.h; sourceTree = "<group>"; };
6611CDF81B45CE2400AE0EE8 /* EZAudio.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = EZAudio.m; path = ../../../Classes/EZAudio.m; sourceTree = "<group>"; };
6611CDF91B45CE2400AE0EE8 /* EZAudioDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioDevice.h; sourceTree = "<group>"; };
6611CDFA1B45CE2400AE0EE8 /* EZAudioDevice.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioDevice.m; sourceTree = "<group>"; };
6611CDFB1B45CE2400AE0EE8 /* EZAudioDisplayLink.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioDisplayLink.h; sourceTree = "<group>"; };
6611CDFC1B45CE2400AE0EE8 /* EZAudioDisplayLink.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioDisplayLink.m; sourceTree = "<group>"; };
6611CDFD1B45CE2400AE0EE8 /* EZAudioFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioFile.h; sourceTree = "<group>"; };
6611CDFE1B45CE2400AE0EE8 /* EZAudioFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioFile.m; sourceTree = "<group>"; };
6611CDFF1B45CE2400AE0EE8 /* EZAudioFloatConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioFloatConverter.h; sourceTree = "<group>"; };
6611CE001B45CE2400AE0EE8 /* EZAudioFloatConverter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioFloatConverter.m; sourceTree = "<group>"; };
6611CE011B45CE2400AE0EE8 /* EZAudioFloatData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioFloatData.h; sourceTree = "<group>"; };
6611CE021B45CE2400AE0EE8 /* EZAudioFloatData.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioFloatData.m; sourceTree = "<group>"; };
6611CE031B45CE2400AE0EE8 /* EZAudioPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioPlayer.h; sourceTree = "<group>"; };
6611CE041B45CE2400AE0EE8 /* EZAudioPlayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioPlayer.m; sourceTree = "<group>"; };
6611CE051B45CE2400AE0EE8 /* EZAudioPlot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioPlot.h; sourceTree = "<group>"; };
6611CE061B45CE2400AE0EE8 /* EZAudioPlot.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioPlot.m; sourceTree = "<group>"; };
6611CE071B45CE2400AE0EE8 /* EZAudioPlotGL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioPlotGL.h; sourceTree = "<group>"; };
6611CE081B45CE2400AE0EE8 /* EZAudioPlotGL.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioPlotGL.m; sourceTree = "<group>"; };
6611CE091B45CE2400AE0EE8 /* EZAudioUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioUtilities.h; sourceTree = "<group>"; };
6611CE0A1B45CE2400AE0EE8 /* EZAudioUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioUtilities.m; sourceTree = "<group>"; };
6611CE0B1B45CE2400AE0EE8 /* EZMicrophone.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZMicrophone.h; sourceTree = "<group>"; };
6611CE0C1B45CE2400AE0EE8 /* EZMicrophone.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZMicrophone.m; sourceTree = "<group>"; };
6611CE0D1B45CE2400AE0EE8 /* EZOutput.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZOutput.h; sourceTree = "<group>"; };
6611CE0E1B45CE2400AE0EE8 /* EZOutput.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZOutput.m; sourceTree = "<group>"; };
6611CE0F1B45CE2400AE0EE8 /* EZPlot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZPlot.h; sourceTree = "<group>"; };
6611CE101B45CE2400AE0EE8 /* EZPlot.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZPlot.m; sourceTree = "<group>"; };
6611CE111B45CE2400AE0EE8 /* EZRecorder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZRecorder.h; sourceTree = "<group>"; };
6611CE121B45CE2400AE0EE8 /* EZRecorder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZRecorder.m; sourceTree = "<group>"; };
6611CE131B45CE2400AE0EE8 /* TPCircularBuffer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = TPCircularBuffer.c; sourceTree = "<group>"; };
6611CE141B45CE2400AE0EE8 /* TPCircularBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPCircularBuffer.h; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
6611CDD61B45CDD800AE0EE8 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
6611CDD01B45CDD800AE0EE8 = {
isa = PBXGroup;
children = (
6611CDDC1B45CDD800AE0EE8 /* EZAudio */,
6611CDDB1B45CDD800AE0EE8 /* Products */,
);
sourceTree = "<group>";
};
6611CDDB1B45CDD800AE0EE8 /* Products */ = {
isa = PBXGroup;
children = (
6611CDDA1B45CDD800AE0EE8 /* EZAudio.framework */,
);
name = Products;
sourceTree = "<group>";
};
6611CDDC1B45CDD800AE0EE8 /* EZAudio */ = {
isa = PBXGroup;
children = (
6611CDF71B45CE2400AE0EE8 /* EZAudio.h */,
6611CDF81B45CE2400AE0EE8 /* EZAudio.m */,
6611CE4E1B45D87A00AE0EE8 /* Core Components */,
6611CE511B45D91500AE0EE8 /* External */,
6611CE4F1B45D8DE00AE0EE8 /* Interface Components */,
6611CE501B45D90A00AE0EE8 /* Utility Components */,
6611CDDD1B45CDD800AE0EE8 /* Supporting Files */,
);
path = EZAudio;
sourceTree = "<group>";
};
6611CDDD1B45CDD800AE0EE8 /* Supporting Files */ = {
isa = PBXGroup;
children = (
6611CDDE1B45CDD800AE0EE8 /* Info.plist */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
6611CE4E1B45D87A00AE0EE8 /* Core Components */ = {
isa = PBXGroup;
children = (
6611CDF91B45CE2400AE0EE8 /* EZAudioDevice.h */,
6611CDFA1B45CE2400AE0EE8 /* EZAudioDevice.m */,
6611CDFD1B45CE2400AE0EE8 /* EZAudioFile.h */,
6611CDFE1B45CE2400AE0EE8 /* EZAudioFile.m */,
6611CE031B45CE2400AE0EE8 /* EZAudioPlayer.h */,
6611CE041B45CE2400AE0EE8 /* EZAudioPlayer.m */,
6611CE0B1B45CE2400AE0EE8 /* EZMicrophone.h */,
6611CE0C1B45CE2400AE0EE8 /* EZMicrophone.m */,
6611CE0D1B45CE2400AE0EE8 /* EZOutput.h */,
6611CE0E1B45CE2400AE0EE8 /* EZOutput.m */,
6611CE111B45CE2400AE0EE8 /* EZRecorder.h */,
6611CE121B45CE2400AE0EE8 /* EZRecorder.m */,
);
name = "Core Components";
path = "../../../Classes/Core Components";
sourceTree = "<group>";
};
6611CE4F1B45D8DE00AE0EE8 /* Interface Components */ = {
isa = PBXGroup;
children = (
6611CDFB1B45CE2400AE0EE8 /* EZAudioDisplayLink.h */,
6611CDFC1B45CE2400AE0EE8 /* EZAudioDisplayLink.m */,
6611CE051B45CE2400AE0EE8 /* EZAudioPlot.h */,
6611CE061B45CE2400AE0EE8 /* EZAudioPlot.m */,
6611CE071B45CE2400AE0EE8 /* EZAudioPlotGL.h */,
6611CE081B45CE2400AE0EE8 /* EZAudioPlotGL.m */,
6611CE0F1B45CE2400AE0EE8 /* EZPlot.h */,
6611CE101B45CE2400AE0EE8 /* EZPlot.m */,
);
name = "Interface Components";
path = "../../../Classes/Interface Components";
sourceTree = "<group>";
};
6611CE501B45D90A00AE0EE8 /* Utility Components */ = {
isa = PBXGroup;
children = (
6611CDFF1B45CE2400AE0EE8 /* EZAudioFloatConverter.h */,
6611CE001B45CE2400AE0EE8 /* EZAudioFloatConverter.m */,
6611CE011B45CE2400AE0EE8 /* EZAudioFloatData.h */,
6611CE021B45CE2400AE0EE8 /* EZAudioFloatData.m */,
6611CE091B45CE2400AE0EE8 /* EZAudioUtilities.h */,
6611CE0A1B45CE2400AE0EE8 /* EZAudioUtilities.m */,
);
name = "Utility Components";
path = "../../../Classes/Utility Components";
sourceTree = "<group>";
};
6611CE511B45D91500AE0EE8 /* External */ = {
isa = PBXGroup;
children = (
6611CE131B45CE2400AE0EE8 /* TPCircularBuffer.c */,
6611CE141B45CE2400AE0EE8 /* TPCircularBuffer.h */,
);
name = External;
path = ../../../Classes/External;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
6611CDD71B45CDD800AE0EE8 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
6611CE2E1B45CE2400AE0EE8 /* EZOutput.h in Headers */,
6611CE301B45CE2400AE0EE8 /* EZPlot.h in Headers */,
6611CE1E1B45CE2400AE0EE8 /* EZAudioFile.h in Headers */,
6611CE221B45CE2400AE0EE8 /* EZAudioFloatData.h in Headers */,
6611CE2C1B45CE2400AE0EE8 /* EZMicrophone.h in Headers */,
6611CE1C1B45CE2400AE0EE8 /* EZAudioDisplayLink.h in Headers */,
6611CE261B45CE2400AE0EE8 /* EZAudioPlot.h in Headers */,
6611CE321B45CE2400AE0EE8 /* EZRecorder.h in Headers */,
6611CE1A1B45CE2400AE0EE8 /* EZAudioDevice.h in Headers */,
6611CE281B45CE2400AE0EE8 /* EZAudioPlotGL.h in Headers */,
6611CE181B45CE2400AE0EE8 /* EZAudio.h in Headers */,
6611CE2A1B45CE2400AE0EE8 /* EZAudioUtilities.h in Headers */,
6611CE241B45CE2400AE0EE8 /* EZAudioPlayer.h in Headers */,
6611CE351B45CE2400AE0EE8 /* TPCircularBuffer.h in Headers */,
6611CE201B45CE2400AE0EE8 /* EZAudioFloatConverter.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
6611CDD91B45CDD800AE0EE8 /* EZAudio */ = {
isa = PBXNativeTarget;
buildConfigurationList = 6611CDF01B45CDD800AE0EE8 /* Build configuration list for PBXNativeTarget "EZAudio" */;
buildPhases = (
6611CDD51B45CDD800AE0EE8 /* Sources */,
6611CDD61B45CDD800AE0EE8 /* Frameworks */,
6611CDD71B45CDD800AE0EE8 /* Headers */,
6611CDD81B45CDD800AE0EE8 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = EZAudio;
productName = EZAudio;
productReference = 6611CDDA1B45CDD800AE0EE8 /* EZAudio.framework */;
productType = "com.apple.product-type.framework";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
6611CDD11B45CDD800AE0EE8 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0640;
ORGANIZATIONNAME = "Syed Haris Ali";
TargetAttributes = {
6611CDD91B45CDD800AE0EE8 = {
CreatedOnToolsVersion = 6.4;
};
};
};
buildConfigurationList = 6611CDD41B45CDD800AE0EE8 /* Build configuration list for PBXProject "EZAudio" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = 6611CDD01B45CDD800AE0EE8;
productRefGroup = 6611CDDB1B45CDD800AE0EE8 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
6611CDD91B45CDD800AE0EE8 /* EZAudio */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
6611CDD81B45CDD800AE0EE8 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
6611CDD51B45CDD800AE0EE8 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
6611CE271B45CE2400AE0EE8 /* EZAudioPlot.m in Sources */,
6611CE2F1B45CE2400AE0EE8 /* EZOutput.m in Sources */,
6611CE191B45CE2400AE0EE8 /* EZAudio.m in Sources */,
6611CE341B45CE2400AE0EE8 /* TPCircularBuffer.c in Sources */,
6611CE2B1B45CE2400AE0EE8 /* EZAudioUtilities.m in Sources */,
6611CE251B45CE2400AE0EE8 /* EZAudioPlayer.m in Sources */,
6611CE1F1B45CE2400AE0EE8 /* EZAudioFile.m in Sources */,
6611CE231B45CE2400AE0EE8 /* EZAudioFloatData.m in Sources */,
6611CE291B45CE2400AE0EE8 /* EZAudioPlotGL.m in Sources */,
6611CE1D1B45CE2400AE0EE8 /* EZAudioDisplayLink.m in Sources */,
6611CE1B1B45CE2400AE0EE8 /* EZAudioDevice.m in Sources */,
6611CE331B45CE2400AE0EE8 /* EZRecorder.m in Sources */,
6611CE211B45CE2400AE0EE8 /* EZAudioFloatConverter.m in Sources */,
6611CE2D1B45CE2400AE0EE8 /* EZMicrophone.m in Sources */,
6611CE311B45CE2400AE0EE8 /* EZPlot.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
6611CDEE1B45CDD800AE0EE8 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Debug;
};
6611CDEF1B45CDD800AE0EE8 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Release;
};
6611CDF11B45CDD800AE0EE8 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = EZAudio/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
};
name = Debug;
};
6611CDF21B45CDD800AE0EE8 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = EZAudio/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
6611CDD41B45CDD800AE0EE8 /* Build configuration list for PBXProject "EZAudio" */ = {
isa = XCConfigurationList;
buildConfigurations = (
6611CDEE1B45CDD800AE0EE8 /* Debug */,
6611CDEF1B45CDD800AE0EE8 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
6611CDF01B45CDD800AE0EE8 /* Build configuration list for PBXNativeTarget "EZAudio" */ = {
isa = XCConfigurationList;
buildConfigurations = (
6611CDF11B45CDD800AE0EE8 /* Debug */,
6611CDF21B45CDD800AE0EE8 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 6611CDD11B45CDD800AE0EE8 /* Project object */;
}
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
@@ -21,10 +21,21 @@
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
6611CEC21B45F81500AE0EE8 /* EZAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6611CEC11B45F81500AE0EE8 /* EZAudio.framework */; };
6611CEC31B45F81500AE0EE8 /* EZAudio.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 6611CEC11B45F81500AE0EE8 /* EZAudio.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
94056D88185B97E300EB94BA /* CoreGraphicsWaveformViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 94056D86185B97E300EB94BA /* CoreGraphicsWaveformViewController.m */; };
94056D89185B97E300EB94BA /* CoreGraphicsWaveformViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 94056D87185B97E300EB94BA /* CoreGraphicsWaveformViewController.xib */; };
9417A6F01867DC8300D9D37B /* AEFloatConverter.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A6D81867DC8300D9D37B /* AEFloatConverter.m */; };
9417A6F11867DC8300D9D37B /* EZAudio.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A6DA1867DC8300D9D37B /* EZAudio.m */; };
9417A6F21867DC8300D9D37B /* EZAudioFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A6DC1867DC8300D9D37B /* EZAudioFile.m */; };
9417A6F31867DC8300D9D37B /* EZAudioPlot.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A6DE1867DC8300D9D37B /* EZAudioPlot.m */; };
9417A6F41867DC8300D9D37B /* EZAudioPlotGL.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A6E01867DC8300D9D37B /* EZAudioPlotGL.m */; };
9417A6F51867DC8300D9D37B /* EZAudioPlotGLKViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A6E21867DC8300D9D37B /* EZAudioPlotGLKViewController.m */; };
9417A6F61867DC8300D9D37B /* EZMicrophone.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A6E41867DC8300D9D37B /* EZMicrophone.m */; };
9417A6F71867DC8300D9D37B /* EZOutput.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A6E61867DC8300D9D37B /* EZOutput.m */; };
9417A6F81867DC8300D9D37B /* EZPlot.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A6E81867DC8300D9D37B /* EZPlot.m */; };
9417A6F91867DC8300D9D37B /* EZRecorder.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A6EA1867DC8300D9D37B /* EZRecorder.m */; };
9417A6FA1867DC8300D9D37B /* TPCircularBuffer.c in Sources */ = {isa = PBXBuildFile; fileRef = 9417A6EB1867DC8300D9D37B /* TPCircularBuffer.c */; };
9417A6FB1867DC8300D9D37B /* CHANGELOG in Resources */ = {isa = PBXBuildFile; fileRef = 9417A6EE1867DC8300D9D37B /* CHANGELOG */; };
9417A6FC1867DC8300D9D37B /* VERSION in Resources */ = {isa = PBXBuildFile; fileRef = 9417A6EF1867DC8300D9D37B /* VERSION */; };
94373025185B931C00F315F0 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 94373024185B931C00F315F0 /* Cocoa.framework */; };
9437302F185B931C00F315F0 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 9437302D185B931C00F315F0 /* InfoPlist.strings */; };
94373031185B931C00F315F0 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 94373030185B931C00F315F0 /* main.m */; };
@@ -32,6 +43,10 @@
94373038185B931C00F315F0 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 94373037185B931C00F315F0 /* AppDelegate.m */; };
9437303B185B931C00F315F0 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 94373039185B931C00F315F0 /* MainMenu.xib */; };
9437303D185B931C00F315F0 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9437303C185B931C00F315F0 /* Images.xcassets */; };
94373044185B931C00F315F0 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 94373043185B931C00F315F0 /* XCTest.framework */; };
94373045185B931C00F315F0 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 94373024185B931C00F315F0 /* Cocoa.framework */; };
9437304D185B931C00F315F0 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 9437304B185B931C00F315F0 /* InfoPlist.strings */; };
9437304F185B931C00F315F0 /* EZAudioCoreGraphicsWaveformExampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9437304E185B931C00F315F0 /* EZAudioCoreGraphicsWaveformExampleTests.m */; };
94373080185B934900F315F0 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9437307D185B934900F315F0 /* AudioToolbox.framework */; };
94373081185B934900F315F0 /* AudioUnit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9437307E185B934900F315F0 /* AudioUnit.framework */; };
94373082185B934900F315F0 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9437307F185B934900F315F0 /* CoreAudio.framework */; };
@@ -40,27 +55,44 @@
94373088185B937E00F315F0 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 94373087185B937E00F315F0 /* QuartzCore.framework */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
6611CEC41B45F81500AE0EE8 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
6611CEC31B45F81500AE0EE8 /* EZAudio.framework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
/* Begin PBXContainerItemProxy section */
94373046185B931C00F315F0 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 94373019185B931C00F315F0 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 94373020185B931C00F315F0;
remoteInfo = EZAudioCoreGraphicsWaveformExample;
};
/* End PBXCopyFilesBuildPhase section */
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
6611CEA01B45F0C700AE0EE8 /* EZAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = EZAudio.framework; path = ../../../EZAudio/OSX/build/Release/EZAudio.framework; sourceTree = "<group>"; };
6611CEA61B45F42300AE0EE8 /* EZAudio.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; name = EZAudio.framework; path = /Users/haris/Documents/code/openSource/EZAudio/EZAudio/OSX/build/Debug/EZAudio.framework; sourceTree = "<absolute>"; };
6611CEC11B45F81500AE0EE8 /* EZAudio.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; name = EZAudio.framework; path = /Users/haris/Documents/code/openSource/EZAudio/EZAudioExamples/OSX/Build/Products/Debug/EZAudio.framework; sourceTree = "<absolute>"; };
94056D85185B97E300EB94BA /* CoreGraphicsWaveformViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = CoreGraphicsWaveformViewController.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
94056D86185B97E300EB94BA /* CoreGraphicsWaveformViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = CoreGraphicsWaveformViewController.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
94056D87185B97E300EB94BA /* CoreGraphicsWaveformViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CoreGraphicsWaveformViewController.xib; sourceTree = "<group>"; };
9417A6D71867DC8300D9D37B /* AEFloatConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEFloatConverter.h; sourceTree = "<group>"; };
9417A6D81867DC8300D9D37B /* AEFloatConverter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AEFloatConverter.m; sourceTree = "<group>"; };
9417A6D91867DC8300D9D37B /* EZAudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudio.h; sourceTree = "<group>"; };
9417A6DA1867DC8300D9D37B /* EZAudio.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudio.m; sourceTree = "<group>"; };
9417A6DB1867DC8300D9D37B /* EZAudioFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioFile.h; sourceTree = "<group>"; };
9417A6DC1867DC8300D9D37B /* EZAudioFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioFile.m; sourceTree = "<group>"; };
9417A6DD1867DC8300D9D37B /* EZAudioPlot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioPlot.h; sourceTree = "<group>"; };
9417A6DE1867DC8300D9D37B /* EZAudioPlot.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioPlot.m; sourceTree = "<group>"; };
9417A6DF1867DC8300D9D37B /* EZAudioPlotGL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioPlotGL.h; sourceTree = "<group>"; };
9417A6E01867DC8300D9D37B /* EZAudioPlotGL.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioPlotGL.m; sourceTree = "<group>"; };
9417A6E11867DC8300D9D37B /* EZAudioPlotGLKViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioPlotGLKViewController.h; sourceTree = "<group>"; };
9417A6E21867DC8300D9D37B /* EZAudioPlotGLKViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioPlotGLKViewController.m; sourceTree = "<group>"; };
9417A6E31867DC8300D9D37B /* EZMicrophone.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZMicrophone.h; sourceTree = "<group>"; };
9417A6E41867DC8300D9D37B /* EZMicrophone.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZMicrophone.m; sourceTree = "<group>"; };
9417A6E51867DC8300D9D37B /* EZOutput.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZOutput.h; sourceTree = "<group>"; };
9417A6E61867DC8300D9D37B /* EZOutput.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZOutput.m; sourceTree = "<group>"; };
9417A6E71867DC8300D9D37B /* EZPlot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZPlot.h; sourceTree = "<group>"; };
9417A6E81867DC8300D9D37B /* EZPlot.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZPlot.m; sourceTree = "<group>"; };
9417A6E91867DC8300D9D37B /* EZRecorder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZRecorder.h; sourceTree = "<group>"; };
9417A6EA1867DC8300D9D37B /* EZRecorder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZRecorder.m; sourceTree = "<group>"; };
9417A6EB1867DC8300D9D37B /* TPCircularBuffer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = TPCircularBuffer.c; sourceTree = "<group>"; };
9417A6EC1867DC8300D9D37B /* TPCircularBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPCircularBuffer.h; sourceTree = "<group>"; };
9417A6EE1867DC8300D9D37B /* CHANGELOG */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CHANGELOG; sourceTree = "<group>"; };
9417A6EF1867DC8300D9D37B /* VERSION */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = VERSION; sourceTree = "<group>"; };
94373021185B931C00F315F0 /* EZAudioCoreGraphicsWaveformExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = EZAudioCoreGraphicsWaveformExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
94373024185B931C00F315F0 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
94373027185B931C00F315F0 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
@@ -75,7 +107,11 @@
94373037185B931C00F315F0 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = AppDelegate.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
9437303A185B931C00F315F0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
9437303C185B931C00F315F0 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
94373042185B931C00F315F0 /* EZAudioCoreGraphicsWaveformExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = EZAudioCoreGraphicsWaveformExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
94373043185B931C00F315F0 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
9437304A185B931C00F315F0 /* EZAudioCoreGraphicsWaveformExampleTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "EZAudioCoreGraphicsWaveformExampleTests-Info.plist"; sourceTree = "<group>"; };
9437304C185B931C00F315F0 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
9437304E185B931C00F315F0 /* EZAudioCoreGraphicsWaveformExampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = EZAudioCoreGraphicsWaveformExampleTests.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
9437307D185B934900F315F0 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
9437307E185B934900F315F0 /* AudioUnit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioUnit.framework; path = System/Library/Frameworks/AudioUnit.framework; sourceTree = SDKROOT; };
9437307F185B934900F315F0 /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; };
@@ -95,20 +131,67 @@
94373080185B934900F315F0 /* AudioToolbox.framework in Frameworks */,
94373081185B934900F315F0 /* AudioUnit.framework in Frameworks */,
94373082185B934900F315F0 /* CoreAudio.framework in Frameworks */,
6611CEC21B45F81500AE0EE8 /* EZAudio.framework in Frameworks */,
94373025185B931C00F315F0 /* Cocoa.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
9437303F185B931C00F315F0 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
94373045185B931C00F315F0 /* Cocoa.framework in Frameworks */,
94373044185B931C00F315F0 /* XCTest.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
9417A6D61867DC8300D9D37B /* EZAudio */ = {
isa = PBXGroup;
children = (
9417A6D71867DC8300D9D37B /* AEFloatConverter.h */,
9417A6D81867DC8300D9D37B /* AEFloatConverter.m */,
9417A6D91867DC8300D9D37B /* EZAudio.h */,
9417A6DA1867DC8300D9D37B /* EZAudio.m */,
9417A6DB1867DC8300D9D37B /* EZAudioFile.h */,
9417A6DC1867DC8300D9D37B /* EZAudioFile.m */,
9417A6DD1867DC8300D9D37B /* EZAudioPlot.h */,
9417A6DE1867DC8300D9D37B /* EZAudioPlot.m */,
9417A6DF1867DC8300D9D37B /* EZAudioPlotGL.h */,
9417A6E01867DC8300D9D37B /* EZAudioPlotGL.m */,
9417A6E11867DC8300D9D37B /* EZAudioPlotGLKViewController.h */,
9417A6E21867DC8300D9D37B /* EZAudioPlotGLKViewController.m */,
9417A6E31867DC8300D9D37B /* EZMicrophone.h */,
9417A6E41867DC8300D9D37B /* EZMicrophone.m */,
9417A6E51867DC8300D9D37B /* EZOutput.h */,
9417A6E61867DC8300D9D37B /* EZOutput.m */,
9417A6E71867DC8300D9D37B /* EZPlot.h */,
9417A6E81867DC8300D9D37B /* EZPlot.m */,
9417A6E91867DC8300D9D37B /* EZRecorder.h */,
9417A6EA1867DC8300D9D37B /* EZRecorder.m */,
9417A6EB1867DC8300D9D37B /* TPCircularBuffer.c */,
9417A6EC1867DC8300D9D37B /* TPCircularBuffer.h */,
9417A6ED1867DC8300D9D37B /* VERSION */,
);
name = EZAudio;
path = ../../../../EZAudio;
sourceTree = "<group>";
};
9417A6ED1867DC8300D9D37B /* VERSION */ = {
isa = PBXGroup;
children = (
9417A6EE1867DC8300D9D37B /* CHANGELOG */,
9417A6EF1867DC8300D9D37B /* VERSION */,
);
path = VERSION;
sourceTree = "<group>";
};
94373018185B931C00F315F0 = {
isa = PBXGroup;
children = (
6611CEC11B45F81500AE0EE8 /* EZAudio.framework */,
6611CEA61B45F42300AE0EE8 /* EZAudio.framework */,
9437302A185B931C00F315F0 /* EZAudioCoreGraphicsWaveformExample */,
94373048185B931C00F315F0 /* EZAudioCoreGraphicsWaveformExampleTests */,
94373023185B931C00F315F0 /* Frameworks */,
94373022185B931C00F315F0 /* Products */,
);
@@ -118,6 +201,7 @@
isa = PBXGroup;
children = (
94373021185B931C00F315F0 /* EZAudioCoreGraphicsWaveformExample.app */,
94373042185B931C00F315F0 /* EZAudioCoreGraphicsWaveformExampleTests.xctest */,
);
name = Products;
sourceTree = "<group>";
@@ -125,7 +209,6 @@
94373023185B931C00F315F0 /* Frameworks */ = {
isa = PBXGroup;
children = (
6611CEA01B45F0C700AE0EE8 /* EZAudio.framework */,
94373087185B937E00F315F0 /* QuartzCore.framework */,
94373085185B937100F315F0 /* OpenGL.framework */,
94373083185B936B00F315F0 /* GLKit.framework */,
@@ -152,6 +235,7 @@
9437302A185B931C00F315F0 /* EZAudioCoreGraphicsWaveformExample */ = {
isa = PBXGroup;
children = (
9417A6D61867DC8300D9D37B /* EZAudio */,
94373036185B931C00F315F0 /* AppDelegate.h */,
94373037185B931C00F315F0 /* AppDelegate.m */,
94056D85185B97E300EB94BA /* CoreGraphicsWaveformViewController.h */,
@@ -176,6 +260,24 @@
name = "Supporting Files";
sourceTree = "<group>";
};
94373048185B931C00F315F0 /* EZAudioCoreGraphicsWaveformExampleTests */ = {
isa = PBXGroup;
children = (
9437304E185B931C00F315F0 /* EZAudioCoreGraphicsWaveformExampleTests.m */,
94373049185B931C00F315F0 /* Supporting Files */,
);
path = EZAudioCoreGraphicsWaveformExampleTests;
sourceTree = "<group>";
};
94373049185B931C00F315F0 /* Supporting Files */ = {
isa = PBXGroup;
children = (
9437304A185B931C00F315F0 /* EZAudioCoreGraphicsWaveformExampleTests-Info.plist */,
9437304B185B931C00F315F0 /* InfoPlist.strings */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -186,7 +288,6 @@
9437301D185B931C00F315F0 /* Sources */,
9437301E185B931C00F315F0 /* Frameworks */,
9437301F185B931C00F315F0 /* Resources */,
6611CEC41B45F81500AE0EE8 /* Embed Frameworks */,
);
buildRules = (
);
@@ -197,6 +298,24 @@
productReference = 94373021185B931C00F315F0 /* EZAudioCoreGraphicsWaveformExample.app */;
productType = "com.apple.product-type.application";
};
94373041185B931C00F315F0 /* EZAudioCoreGraphicsWaveformExampleTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 94373055185B931D00F315F0 /* Build configuration list for PBXNativeTarget "EZAudioCoreGraphicsWaveformExampleTests" */;
buildPhases = (
9437303E185B931C00F315F0 /* Sources */,
9437303F185B931C00F315F0 /* Frameworks */,
94373040185B931C00F315F0 /* Resources */,
);
buildRules = (
);
dependencies = (
94373047185B931C00F315F0 /* PBXTargetDependency */,
);
name = EZAudioCoreGraphicsWaveformExampleTests;
productName = EZAudioCoreGraphicsWaveformExampleTests;
productReference = 94373042185B931C00F315F0 /* EZAudioCoreGraphicsWaveformExampleTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
@@ -205,6 +324,11 @@
attributes = {
LastUpgradeCheck = 0500;
ORGANIZATIONNAME = "Syed Haris Ali";
TargetAttributes = {
94373041185B931C00F315F0 = {
TestTargetID = 94373020185B931C00F315F0;
};
};
};
buildConfigurationList = 9437301C185B931C00F315F0 /* Build configuration list for PBXProject "EZAudioCoreGraphicsWaveformExample" */;
compatibilityVersion = "Xcode 3.2";
@@ -220,6 +344,7 @@
projectRoot = "";
targets = (
94373020185B931C00F315F0 /* EZAudioCoreGraphicsWaveformExample */,
94373041185B931C00F315F0 /* EZAudioCoreGraphicsWaveformExampleTests */,
94F8DF4A18C84203005C4CBD /* Generate Documentation */,
);
};
@@ -231,10 +356,20 @@
buildActionMask = 2147483647;
files = (
9437302F185B931C00F315F0 /* InfoPlist.strings in Resources */,
9417A6FC1867DC8300D9D37B /* VERSION in Resources */,
9437303D185B931C00F315F0 /* Images.xcassets in Resources */,
94373035185B931C00F315F0 /* Credits.rtf in Resources */,
94056D89185B97E300EB94BA /* CoreGraphicsWaveformViewController.xib in Resources */,
9437303B185B931C00F315F0 /* MainMenu.xib in Resources */,
9417A6FB1867DC8300D9D37B /* CHANGELOG in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
94373040185B931C00F315F0 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9437304D185B931C00F315F0 /* InfoPlist.strings in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -261,14 +396,41 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9417A6FA1867DC8300D9D37B /* TPCircularBuffer.c in Sources */,
9417A6F71867DC8300D9D37B /* EZOutput.m in Sources */,
9417A6F51867DC8300D9D37B /* EZAudioPlotGLKViewController.m in Sources */,
94056D88185B97E300EB94BA /* CoreGraphicsWaveformViewController.m in Sources */,
9417A6F01867DC8300D9D37B /* AEFloatConverter.m in Sources */,
94373038185B931C00F315F0 /* AppDelegate.m in Sources */,
9417A6F31867DC8300D9D37B /* EZAudioPlot.m in Sources */,
9417A6F21867DC8300D9D37B /* EZAudioFile.m in Sources */,
9417A6F41867DC8300D9D37B /* EZAudioPlotGL.m in Sources */,
94373031185B931C00F315F0 /* main.m in Sources */,
9417A6F81867DC8300D9D37B /* EZPlot.m in Sources */,
9417A6F61867DC8300D9D37B /* EZMicrophone.m in Sources */,
9417A6F91867DC8300D9D37B /* EZRecorder.m in Sources */,
9417A6F11867DC8300D9D37B /* EZAudio.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
9437303E185B931C00F315F0 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9437304F185B931C00F315F0 /* EZAudioCoreGraphicsWaveformExampleTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
94373047185B931C00F315F0 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 94373020185B931C00F315F0 /* EZAudioCoreGraphicsWaveformExample */;
targetProxy = 94373046185B931C00F315F0 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
9437302D185B931C00F315F0 /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
@@ -294,6 +456,14 @@
name = MainMenu.xib;
sourceTree = "<group>";
};
9437304B185B931C00F315F0 /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
9437304C185B931C00F315F0 /* en */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
@@ -313,7 +483,6 @@
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
FRAMEWORK_SEARCH_PATHS = "";
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
@@ -353,7 +522,6 @@
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
FRAMEWORK_SEARCH_PATHS = "";
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@@ -372,14 +540,9 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
COMBINE_HIDPI_IMAGES = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
/Users/haris/Documents/code/openSource/EZAudio/EZAudioExamples/OSX/Build/Products/Debug,
);
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "EZAudioCoreGraphicsWaveformExample/EZAudioCoreGraphicsWaveformExample-Prefix.pch";
INFOPLIST_FILE = "EZAudioCoreGraphicsWaveformExample/EZAudioCoreGraphicsWaveformExample-Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
@@ -390,19 +553,54 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
COMBINE_HIDPI_IMAGES = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
/Users/haris/Documents/code/openSource/EZAudio/EZAudioExamples/OSX/Build/Products/Debug,
);
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "EZAudioCoreGraphicsWaveformExample/EZAudioCoreGraphicsWaveformExample-Prefix.pch";
INFOPLIST_FILE = "EZAudioCoreGraphicsWaveformExample/EZAudioCoreGraphicsWaveformExample-Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
name = Release;
};
94373056185B931D00F315F0 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/EZAudioCoreGraphicsWaveformExample.app/Contents/MacOS/EZAudioCoreGraphicsWaveformExample";
COMBINE_HIDPI_IMAGES = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(DEVELOPER_FRAMEWORKS_DIR)",
"$(inherited)",
);
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "EZAudioCoreGraphicsWaveformExample/EZAudioCoreGraphicsWaveformExample-Prefix.pch";
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
INFOPLIST_FILE = "EZAudioCoreGraphicsWaveformExampleTests/EZAudioCoreGraphicsWaveformExampleTests-Info.plist";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUNDLE_LOADER)";
WRAPPER_EXTENSION = xctest;
};
name = Debug;
};
94373057185B931D00F315F0 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/EZAudioCoreGraphicsWaveformExample.app/Contents/MacOS/EZAudioCoreGraphicsWaveformExample";
COMBINE_HIDPI_IMAGES = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(DEVELOPER_FRAMEWORKS_DIR)",
"$(inherited)",
);
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "EZAudioCoreGraphicsWaveformExample/EZAudioCoreGraphicsWaveformExample-Prefix.pch";
INFOPLIST_FILE = "EZAudioCoreGraphicsWaveformExampleTests/EZAudioCoreGraphicsWaveformExampleTests-Info.plist";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUNDLE_LOADER)";
WRAPPER_EXTENSION = xctest;
};
name = Release;
};
94F8DF4B18C84204005C4CBD /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -438,6 +636,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
94373055185B931D00F315F0 /* Build configuration list for PBXNativeTarget "EZAudioCoreGraphicsWaveformExampleTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
94373056185B931D00F315F0 /* Debug */,
94373057185B931D00F315F0 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
94F8DF4D18C84204005C4CBD /* Build configuration list for PBXAggregateTarget "Generate Documentation" */ = {
isa = XCConfigurationList;
buildConfigurations = (
@@ -445,7 +652,6 @@
94F8DF4C18C84204005C4CBD /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
@@ -31,7 +31,7 @@
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Swap in our view controller in the window's content view
self.coreGraphicsWaveformViewController = [[CoreGraphicsWaveformViewController alloc] initWithNibName:NSStringFromClass(CoreGraphicsWaveformViewController.class) bundle:nil];
self.coreGraphicsWaveformViewController = [[CoreGraphicsWaveformViewController alloc] init];
// Resize view controller to content view's current size
self.coreGraphicsWaveformViewController.view.frame = [self.window.contentView frame];
// Add resizing flags to make the view controller resize with the window
@@ -1,17 +1,100 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="7702" systemVersion="14C109" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="4439" systemVersion="13A451" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="7702"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="4439"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
<connections>
<action selector="orderFrontStandardAboutPanel:" destination="58" id="142"/>
<outlet property="delegate" destination="494" id="495"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder">
<connections>
<action selector="alignCenter:" destination="499" id="518"/>
<action selector="alignJustified:" destination="500" id="523"/>
<action selector="alignLeft:" destination="498" id="524"/>
<action selector="alignRight:" destination="501" id="521"/>
<action selector="arrangeInFront:" destination="5" id="39"/>
<action selector="capitalizeWord:" destination="466" id="467"/>
<action selector="centerSelectionInVisibleArea:" destination="210" id="245"/>
<action selector="checkSpelling:" destination="201" id="225"/>
<action selector="clearRecentDocuments:" destination="126" id="127"/>
<action selector="copy:" destination="197" id="224"/>
<action selector="copyFont:" destination="403" id="428"/>
<action selector="copyRuler:" destination="506" id="522"/>
<action selector="cut:" destination="199" id="228"/>
<action selector="delete:" destination="202" id="235"/>
<action selector="hide:" destination="134" id="367"/>
<action selector="hideOtherApplications:" destination="145" id="368"/>
<action selector="loosenKerning:" destination="419" id="435"/>
<action selector="lowerBaseline:" destination="410" id="427"/>
<action selector="lowercaseWord:" destination="465" id="468"/>
<action selector="makeBaseWritingDirectionLeftToRight:" destination="511" id="526"/>
<action selector="makeBaseWritingDirectionNatural:" destination="510" id="525"/>
<action selector="makeBaseWritingDirectionRightToLeft:" destination="512" id="527"/>
<action selector="makeTextWritingDirectionLeftToRight:" destination="516" id="529"/>
<action selector="makeTextWritingDirectionNatural:" destination="515" id="528"/>
<action selector="makeTextWritingDirectionRightToLeft:" destination="517" id="530"/>
<action selector="newDocument:" destination="82" id="373"/>
<action selector="openDocument:" destination="72" id="374"/>
<action selector="orderFrontColorPanel:" destination="401" id="433"/>
<action selector="orderFrontSubstitutionsPanel:" destination="457" id="458"/>
<action selector="paste:" destination="203" id="226"/>
<action selector="pasteAsPlainText:" destination="485" id="486"/>
<action selector="pasteFont:" destination="404" id="436"/>
<action selector="pasteRuler:" destination="507" id="519"/>
<action selector="performClose:" destination="73" id="193"/>
<action selector="performFindPanelAction:" destination="209" id="241"/>
<action selector="performFindPanelAction:" destination="208" id="487"/>
<action selector="performFindPanelAction:" destination="213" id="488"/>
<action selector="performFindPanelAction:" destination="221" id="489"/>
<action selector="performFindPanelAction:" destination="534" id="535"/>
<action selector="performMiniaturize:" destination="23" id="37"/>
<action selector="performZoom:" destination="239" id="240"/>
<action selector="print:" destination="78" id="86"/>
<action selector="raiseBaseline:" destination="409" id="426"/>
<action selector="redo:" destination="215" id="231"/>
<action selector="revertDocumentToSaved:" destination="112" id="364"/>
<action selector="runPageLayout:" destination="77" id="87"/>
<action selector="runToolbarCustomizationPalette:" destination="298" id="365"/>
<action selector="saveDocument:" destination="75" id="362"/>
<action selector="selectAll:" destination="198" id="232"/>
<action selector="showGuessPanel:" destination="204" id="230"/>
<action selector="showHelp:" destination="492" id="493"/>
<action selector="startSpeaking:" destination="196" id="233"/>
<action selector="stopSpeaking:" destination="195" id="227"/>
<action selector="subscript:" destination="408" id="429"/>
<action selector="superscript:" destination="407" id="430"/>
<action selector="tightenKerning:" destination="418" id="431"/>
<action selector="toggleAutomaticDashSubstitution:" destination="460" id="461"/>
<action selector="toggleAutomaticLinkDetection:" destination="354" id="357"/>
<action selector="toggleAutomaticQuoteSubstitution:" destination="351" id="356"/>
<action selector="toggleAutomaticSpellingCorrection:" destination="454" id="456"/>
<action selector="toggleAutomaticTextReplacement:" destination="462" id="463"/>
<action selector="toggleContinuousSpellChecking:" destination="219" id="222"/>
<action selector="toggleGrammarChecking:" destination="346" id="347"/>
<action selector="toggleRuler:" destination="505" id="520"/>
<action selector="toggleSmartInsertDelete:" destination="350" id="355"/>
<action selector="toggleToolbarShown:" destination="297" id="366"/>
<action selector="turnOffKerning:" destination="417" id="441"/>
<action selector="turnOffLigatures:" destination="413" id="440"/>
<action selector="underline:" destination="392" id="432"/>
<action selector="undo:" destination="207" id="223"/>
<action selector="unhideAllApplications:" destination="150" id="370"/>
<action selector="unscript:" destination="406" id="437"/>
<action selector="uppercaseWord:" destination="452" id="464"/>
<action selector="useAllLigatures:" destination="414" id="434"/>
<action selector="useStandardKerning:" destination="416" id="438"/>
<action selector="useStandardLigatures:" destination="412" id="439"/>
</connections>
</customObject>
<customObject id="-3" userLabel="Application">
<connections>
<action selector="terminate:" destination="136" id="449"/>
</connections>
</customObject>
<menu title="AMainMenu" systemMenu="main" id="29">
<items>
<menuItem title="EZAudioCoreGraphicsWaveformExample" id="56">
@@ -19,9 +102,6 @@
<items>
<menuItem title="About EZAudioCoreGraphicsWaveformExample" id="58">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontStandardAboutPanel:" target="-2" id="142"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="236">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
@@ -36,211 +116,95 @@
<menuItem isSeparatorItem="YES" id="144">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Hide EZAudioCoreGraphicsWaveformExample" keyEquivalent="h" id="134">
<connections>
<action selector="hide:" target="-1" id="367"/>
</connections>
</menuItem>
<menuItem title="Hide EZAudioCoreGraphicsWaveformExample" keyEquivalent="h" id="134"/>
<menuItem title="Hide Others" keyEquivalent="h" id="145">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="hideOtherApplications:" target="-1" id="368"/>
</connections>
</menuItem>
<menuItem title="Show All" id="150">
<connections>
<action selector="unhideAllApplications:" target="-1" id="370"/>
</connections>
</menuItem>
<menuItem title="Show All" id="150"/>
<menuItem isSeparatorItem="YES" id="149">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Quit EZAudioCoreGraphicsWaveformExample" keyEquivalent="q" id="136">
<connections>
<action selector="terminate:" target="-3" id="449"/>
</connections>
</menuItem>
<menuItem title="Quit EZAudioCoreGraphicsWaveformExample" keyEquivalent="q" id="136"/>
</items>
</menu>
</menuItem>
<menuItem title="File" id="83">
<menu key="submenu" title="File" id="81">
<items>
<menuItem title="New" keyEquivalent="n" id="82">
<connections>
<action selector="newDocument:" target="-1" id="373"/>
</connections>
</menuItem>
<menuItem title="Open…" keyEquivalent="o" id="72">
<connections>
<action selector="openDocument:" target="-1" id="374"/>
</connections>
</menuItem>
<menuItem title="New" keyEquivalent="n" id="82"/>
<menuItem title="Open…" keyEquivalent="o" id="72"/>
<menuItem title="Open Recent" id="124">
<menu key="submenu" title="Open Recent" systemMenu="recentDocuments" id="125">
<items>
<menuItem title="Clear Menu" id="126">
<connections>
<action selector="clearRecentDocuments:" target="-1" id="127"/>
</connections>
</menuItem>
<menuItem title="Clear Menu" id="126"/>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="79">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Close" keyEquivalent="w" id="73">
<connections>
<action selector="performClose:" target="-1" id="193"/>
</connections>
</menuItem>
<menuItem title="Save…" keyEquivalent="s" id="75">
<connections>
<action selector="saveDocument:" target="-1" id="362"/>
</connections>
</menuItem>
<menuItem title="Close" keyEquivalent="w" id="73"/>
<menuItem title="Save…" keyEquivalent="s" id="75"/>
<menuItem title="Revert to Saved" id="112">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="revertDocumentToSaved:" target="-1" id="364"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="74">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Page Setup..." keyEquivalent="P" id="77">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="runPageLayout:" target="-1" id="87"/>
</connections>
</menuItem>
<menuItem title="Print…" keyEquivalent="p" id="78">
<connections>
<action selector="print:" target="-1" id="86"/>
</connections>
</menuItem>
<menuItem title="Print…" keyEquivalent="p" id="78"/>
</items>
</menu>
</menuItem>
<menuItem title="Edit" id="217">
<menu key="submenu" title="Edit" id="205">
<items>
<menuItem title="Undo" keyEquivalent="z" id="207">
<connections>
<action selector="undo:" target="-1" id="223"/>
</connections>
</menuItem>
<menuItem title="Undo" keyEquivalent="z" id="207"/>
<menuItem title="Redo" keyEquivalent="Z" id="215">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="redo:" target="-1" id="231"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="206">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Cut" keyEquivalent="x" id="199">
<connections>
<action selector="cut:" target="-1" id="228"/>
</connections>
</menuItem>
<menuItem title="Copy" keyEquivalent="c" id="197">
<connections>
<action selector="copy:" target="-1" id="224"/>
</connections>
</menuItem>
<menuItem title="Paste" keyEquivalent="v" id="203">
<connections>
<action selector="paste:" target="-1" id="226"/>
</connections>
</menuItem>
<menuItem title="Cut" keyEquivalent="x" id="199"/>
<menuItem title="Copy" keyEquivalent="c" id="197"/>
<menuItem title="Paste" keyEquivalent="v" id="203"/>
<menuItem title="Paste and Match Style" keyEquivalent="V" id="485">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteAsPlainText:" target="-1" id="486"/>
</connections>
</menuItem>
<menuItem title="Delete" id="202">
<connections>
<action selector="delete:" target="-1" id="235"/>
</connections>
</menuItem>
<menuItem title="Select All" keyEquivalent="a" id="198">
<connections>
<action selector="selectAll:" target="-1" id="232"/>
</connections>
</menuItem>
<menuItem title="Delete" id="202"/>
<menuItem title="Select All" keyEquivalent="a" id="198"/>
<menuItem isSeparatorItem="YES" id="214">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Find" id="218">
<menu key="submenu" title="Find" id="220">
<items>
<menuItem title="Find…" tag="1" keyEquivalent="f" id="209">
<connections>
<action selector="performFindPanelAction:" target="-1" id="241"/>
</connections>
</menuItem>
<menuItem title="Find…" tag="1" keyEquivalent="f" id="209"/>
<menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="534">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="performFindPanelAction:" target="-1" id="535"/>
</connections>
</menuItem>
<menuItem title="Find Next" tag="2" keyEquivalent="g" id="208">
<connections>
<action selector="performFindPanelAction:" target="-1" id="487"/>
</connections>
</menuItem>
<menuItem title="Find Next" tag="2" keyEquivalent="g" id="208"/>
<menuItem title="Find Previous" tag="3" keyEquivalent="G" id="213">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="performFindPanelAction:" target="-1" id="488"/>
</connections>
</menuItem>
<menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="221">
<connections>
<action selector="performFindPanelAction:" target="-1" id="489"/>
</connections>
</menuItem>
<menuItem title="Jump to Selection" keyEquivalent="j" id="210">
<connections>
<action selector="centerSelectionInVisibleArea:" target="-1" id="245"/>
</connections>
</menuItem>
<menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="221"/>
<menuItem title="Jump to Selection" keyEquivalent="j" id="210"/>
</items>
</menu>
</menuItem>
<menuItem title="Spelling and Grammar" id="216">
<menu key="submenu" title="Spelling and Grammar" id="200">
<items>
<menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="204">
<connections>
<action selector="showGuessPanel:" target="-1" id="230"/>
</connections>
</menuItem>
<menuItem title="Check Document Now" keyEquivalent=";" id="201">
<connections>
<action selector="checkSpelling:" target="-1" id="225"/>
</connections>
</menuItem>
<menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="204"/>
<menuItem title="Check Document Now" keyEquivalent=";" id="201"/>
<menuItem isSeparatorItem="YES" id="453"/>
<menuItem title="Check Spelling While Typing" id="219">
<connections>
<action selector="toggleContinuousSpellChecking:" target="-1" id="222"/>
</connections>
</menuItem>
<menuItem title="Check Grammar With Spelling" id="346">
<connections>
<action selector="toggleGrammarChecking:" target="-1" id="347"/>
</connections>
</menuItem>
<menuItem title="Check Spelling While Typing" id="219"/>
<menuItem title="Check Grammar With Spelling" id="346"/>
<menuItem title="Correct Spelling Automatically" id="454">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticSpellingCorrection:" target="-1" id="456"/>
</connections>
</menuItem>
</items>
</menu>
@@ -250,38 +214,18 @@
<items>
<menuItem title="Show Substitutions" id="457">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontSubstitutionsPanel:" target="-1" id="458"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="459"/>
<menuItem title="Smart Copy/Paste" tag="1" keyEquivalent="f" id="350">
<connections>
<action selector="toggleSmartInsertDelete:" target="-1" id="355"/>
</connections>
</menuItem>
<menuItem title="Smart Quotes" tag="2" keyEquivalent="g" id="351">
<connections>
<action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="356"/>
</connections>
</menuItem>
<menuItem title="Smart Copy/Paste" tag="1" keyEquivalent="f" id="350"/>
<menuItem title="Smart Quotes" tag="2" keyEquivalent="g" id="351"/>
<menuItem title="Smart Dashes" id="460">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticDashSubstitution:" target="-1" id="461"/>
</connections>
</menuItem>
<menuItem title="Smart Links" tag="3" keyEquivalent="G" id="354">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="toggleAutomaticLinkDetection:" target="-1" id="357"/>
</connections>
</menuItem>
<menuItem title="Text Replacement" id="462">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleAutomaticTextReplacement:" target="-1" id="463"/>
</connections>
</menuItem>
</items>
</menu>
@@ -292,21 +236,12 @@
<items>
<menuItem title="Make Upper Case" id="452">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="uppercaseWord:" target="-1" id="464"/>
</connections>
</menuItem>
<menuItem title="Make Lower Case" id="465">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowercaseWord:" target="-1" id="468"/>
</connections>
</menuItem>
<menuItem title="Capitalize" id="466">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="capitalizeWord:" target="-1" id="467"/>
</connections>
</menuItem>
</items>
</menu>
@@ -314,16 +249,8 @@
<menuItem title="Speech" id="211">
<menu key="submenu" title="Speech" id="212">
<items>
<menuItem title="Start Speaking" id="196">
<connections>
<action selector="startSpeaking:" target="-1" id="233"/>
</connections>
</menuItem>
<menuItem title="Stop Speaking" id="195">
<connections>
<action selector="stopSpeaking:" target="-1" id="227"/>
</connections>
</menuItem>
<menuItem title="Start Speaking" id="196"/>
<menuItem title="Stop Speaking" id="195"/>
</items>
</menu>
</menuItem>
@@ -338,37 +265,13 @@
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Font" systemMenu="font" id="388">
<items>
<menuItem title="Show Fonts" keyEquivalent="t" id="389">
<connections>
<action selector="orderFrontFontPanel:" target="420" id="424"/>
</connections>
</menuItem>
<menuItem title="Bold" tag="2" keyEquivalent="b" id="390">
<connections>
<action selector="addFontTrait:" target="420" id="421"/>
</connections>
</menuItem>
<menuItem title="Italic" tag="1" keyEquivalent="i" id="391">
<connections>
<action selector="addFontTrait:" target="420" id="422"/>
</connections>
</menuItem>
<menuItem title="Underline" keyEquivalent="u" id="392">
<connections>
<action selector="underline:" target="-1" id="432"/>
</connections>
</menuItem>
<menuItem title="Show Fonts" keyEquivalent="t" id="389"/>
<menuItem title="Bold" tag="2" keyEquivalent="b" id="390"/>
<menuItem title="Italic" tag="1" keyEquivalent="i" id="391"/>
<menuItem title="Underline" keyEquivalent="u" id="392"/>
<menuItem isSeparatorItem="YES" id="393"/>
<menuItem title="Bigger" tag="3" keyEquivalent="+" id="394">
<connections>
<action selector="modifyFont:" target="420" id="425"/>
</connections>
</menuItem>
<menuItem title="Smaller" tag="4" keyEquivalent="-" id="395">
<connections>
<action selector="modifyFont:" target="420" id="423"/>
</connections>
</menuItem>
<menuItem title="Bigger" tag="3" keyEquivalent="+" id="394"/>
<menuItem title="Smaller" tag="4" keyEquivalent="-" id="395"/>
<menuItem isSeparatorItem="YES" id="396"/>
<menuItem title="Kern" id="397">
<modifierMask key="keyEquivalentModifierMask"/>
@@ -376,27 +279,15 @@
<items>
<menuItem title="Use Default" id="416">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useStandardKerning:" target="-1" id="438"/>
</connections>
</menuItem>
<menuItem title="Use None" id="417">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="turnOffKerning:" target="-1" id="441"/>
</connections>
</menuItem>
<menuItem title="Tighten" id="418">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="tightenKerning:" target="-1" id="431"/>
</connections>
</menuItem>
<menuItem title="Loosen" id="419">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="loosenKerning:" target="-1" id="435"/>
</connections>
</menuItem>
</items>
</menu>
@@ -407,21 +298,12 @@
<items>
<menuItem title="Use Default" id="412">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useStandardLigatures:" target="-1" id="439"/>
</connections>
</menuItem>
<menuItem title="Use None" id="413">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="turnOffLigatures:" target="-1" id="440"/>
</connections>
</menuItem>
<menuItem title="Use All" id="414">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useAllLigatures:" target="-1" id="434"/>
</connections>
</menuItem>
</items>
</menu>
@@ -432,55 +314,30 @@
<items>
<menuItem title="Use Default" id="406">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="unscript:" target="-1" id="437"/>
</connections>
</menuItem>
<menuItem title="Superscript" id="407">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="superscript:" target="-1" id="430"/>
</connections>
</menuItem>
<menuItem title="Subscript" id="408">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="subscript:" target="-1" id="429"/>
</connections>
</menuItem>
<menuItem title="Raise" id="409">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="raiseBaseline:" target="-1" id="426"/>
</connections>
</menuItem>
<menuItem title="Lower" id="410">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowerBaseline:" target="-1" id="427"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="400"/>
<menuItem title="Show Colors" keyEquivalent="C" id="401">
<connections>
<action selector="orderFrontColorPanel:" target="-1" id="433"/>
</connections>
</menuItem>
<menuItem title="Show Colors" keyEquivalent="C" id="401"/>
<menuItem isSeparatorItem="YES" id="402"/>
<menuItem title="Copy Style" keyEquivalent="c" id="403">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="copyFont:" target="-1" id="428"/>
</connections>
</menuItem>
<menuItem title="Paste Style" keyEquivalent="v" id="404">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteFont:" target="-1" id="436"/>
</connections>
</menuItem>
</items>
</menu>
@@ -489,27 +346,12 @@
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Text" id="497">
<items>
<menuItem title="Align Left" keyEquivalent="{" id="498">
<connections>
<action selector="alignLeft:" target="-1" id="524"/>
</connections>
</menuItem>
<menuItem title="Center" keyEquivalent="|" id="499">
<connections>
<action selector="alignCenter:" target="-1" id="518"/>
</connections>
</menuItem>
<menuItem title="Align Left" keyEquivalent="{" id="498"/>
<menuItem title="Center" keyEquivalent="|" id="499"/>
<menuItem title="Justify" id="500">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="alignJustified:" target="-1" id="523"/>
</connections>
</menuItem>
<menuItem title="Align Right" keyEquivalent="}" id="501">
<connections>
<action selector="alignRight:" target="-1" id="521"/>
</connections>
</menuItem>
<menuItem title="Align Right" keyEquivalent="}" id="501"/>
<menuItem isSeparatorItem="YES" id="502"/>
<menuItem title="Writing Direction" id="503">
<modifierMask key="keyEquivalentModifierMask"/>
@@ -521,23 +363,14 @@
<menuItem id="510">
<string key="title"> Default</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionNatural:" target="-1" id="525"/>
</connections>
</menuItem>
<menuItem id="511">
<string key="title"> Left to Right</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionLeftToRight:" target="-1" id="526"/>
</connections>
</menuItem>
<menuItem id="512">
<string key="title"> Right to Left</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeBaseWritingDirectionRightToLeft:" target="-1" id="527"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="513"/>
<menuItem title="Selection" enabled="NO" id="514">
@@ -546,23 +379,14 @@
<menuItem id="515">
<string key="title"> Default</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionNatural:" target="-1" id="528"/>
</connections>
</menuItem>
<menuItem id="516">
<string key="title"> Left to Right</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionLeftToRight:" target="-1" id="529"/>
</connections>
</menuItem>
<menuItem id="517">
<string key="title"> Right to Left</string>
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="makeTextWritingDirectionRightToLeft:" target="-1" id="530"/>
</connections>
</menuItem>
</items>
</menu>
@@ -570,21 +394,12 @@
<menuItem isSeparatorItem="YES" id="504"/>
<menuItem title="Show Ruler" id="505">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleRuler:" target="-1" id="520"/>
</connections>
</menuItem>
<menuItem title="Copy Ruler" keyEquivalent="c" id="506">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="copyRuler:" target="-1" id="522"/>
</connections>
</menuItem>
<menuItem title="Paste Ruler" keyEquivalent="v" id="507">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="pasteRuler:" target="-1" id="519"/>
</connections>
</menuItem>
</items>
</menu>
@@ -597,39 +412,20 @@
<items>
<menuItem title="Show Toolbar" keyEquivalent="t" id="297">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="toggleToolbarShown:" target="-1" id="366"/>
</connections>
</menuItem>
<menuItem title="Customize Toolbar…" id="298">
<connections>
<action selector="runToolbarCustomizationPalette:" target="-1" id="365"/>
</connections>
</menuItem>
<menuItem title="Customize Toolbar…" id="298"/>
</items>
</menu>
</menuItem>
<menuItem title="Window" id="19">
<menu key="submenu" title="Window" systemMenu="window" id="24">
<items>
<menuItem title="Minimize" keyEquivalent="m" id="23">
<connections>
<action selector="performMiniaturize:" target="-1" id="37"/>
</connections>
</menuItem>
<menuItem title="Zoom" id="239">
<connections>
<action selector="performZoom:" target="-1" id="240"/>
</connections>
</menuItem>
<menuItem title="Minimize" keyEquivalent="m" id="23"/>
<menuItem title="Zoom" id="239"/>
<menuItem isSeparatorItem="YES" id="92">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Bring All to Front" id="5">
<connections>
<action selector="arrangeInFront:" target="-1" id="39"/>
</connections>
</menuItem>
<menuItem title="Bring All to Front" id="5"/>
</items>
</menu>
</menuItem>
@@ -637,11 +433,7 @@
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Help" systemMenu="help" id="491">
<items>
<menuItem title="EZAudioCoreGraphicsWaveformExample Help" keyEquivalent="?" id="492">
<connections>
<action selector="showHelp:" target="-1" id="493"/>
</connections>
</menuItem>
<menuItem title="EZAudioCoreGraphicsWaveformExample Help" keyEquivalent="?" id="492"/>
</items>
</menu>
</menuItem>
@@ -651,7 +443,7 @@
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="335" y="390" width="480" height="360"/>
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="877"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1418"/>
<view key="contentView" id="372">
<rect key="frame" x="0.0" y="0.0" width="480" height="360"/>
<autoresizingMask key="autoresizingMask"/>
@@ -662,6 +454,14 @@
<outlet property="window" destination="371" id="532"/>
</connections>
</customObject>
<customObject id="420" customClass="NSFontManager"/>
<customObject id="420" customClass="NSFontManager">
<connections>
<action selector="addFontTrait:" destination="390" id="421"/>
<action selector="addFontTrait:" destination="391" id="422"/>
<action selector="modifyFont:" destination="395" id="423"/>
<action selector="modifyFont:" destination="394" id="425"/>
<action selector="orderFrontFontPanel:" destination="389" id="424"/>
</connections>
</customObject>
</objects>
</document>
@@ -26,41 +26,25 @@
#import <Cocoa/Cocoa.h>
// Import EZAudio header
#import <EZAudio/EZAudio.h>
#import "EZAudio.h"
/**
We will allow this view controller to act as an EZMicrophoneDelegate. This is how we listen for the microphone callback.
*/
@interface CoreGraphicsWaveformViewController : NSViewController <EZMicrophoneDelegate>
//------------------------------------------------------------------------------
#pragma mark - Components
//------------------------------------------------------------------------------
/**
The CoreGraphics based audio plot
*/
@property (nonatomic, weak) IBOutlet EZAudioPlot *audioPlot;
@property (nonatomic,weak) IBOutlet EZAudioPlot *audioPlot;
/**
The microphone component
*/
@property (nonatomic, strong) EZMicrophone *microphone;
@property (nonatomic,strong) EZMicrophone *microphone;
/**
The microphone pop up button (contains the menu for choosing a microphone input)
*/
@property (nonatomic, weak) IBOutlet NSPopUpButton *microphoneInputPopUpButton;
/**
The microphone input channel pop up button (contains the menu for choosing a microphone input channel)
*/
@property (nonatomic, weak) IBOutlet NSPopUpButton *microphoneInputChannelPopUpButton;
//------------------------------------------------------------------------------
#pragma mark - Actions
//------------------------------------------------------------------------------
/**
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)
*/
@@ -25,219 +25,140 @@
#import "CoreGraphicsWaveformViewController.h"
@interface CoreGraphicsWaveformViewController ()
@end
@implementation CoreGraphicsWaveformViewController
//------------------------------------------------------------------------------
#pragma mark - Initialization
-(id)init {
self = [super initWithNibName:NSStringFromClass(self.class) bundle:nil];
if(self){
[self initializeViewController];
}
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithNibName:NSStringFromClass(self.class) bundle:nil];
if(self){
[self initializeViewController];
}
return self;
}
-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:NSStringFromClass(self.class) bundle:nil];
if(self){
[self initializeViewController];
}
return self;
}
#pragma mark - Initialize View Controller
-(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];
}
#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.471 blue: 0.525 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;
//
// Create the microphone
//
self.microphone = [EZMicrophone microphoneWithDelegate:self];
//
// Start the microphone
//
[self.microphone startFetchingAudio];
-(void)awakeFromNib {
/*
Customizing the audio plot's look
*/
// Background color
self.audioPlot.backgroundColor = [NSColor colorWithCalibratedRed: 0.984 green: 0.471 blue: 0.525 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;
/*
Start the microphone
*/
[self.microphone startFetchingAudio];
}
//------------------------------------------------------------------------------
#pragma mark - Setup
//------------------------------------------------------------------------------
- (void) reloadMicrophoneInputPopUpButtonMenu
{
NSArray *inputDevices = [EZAudioDevice inputDevices];
NSMenu *menu = [[NSMenu alloc] init];
NSMenuItem *defaultInputMenuItem;
for (EZAudioDevice *device in inputDevices)
{
NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:device.name
action:@selector(changedInput:)
keyEquivalent:@""];
item.representedObject = device;
item.target = self;
[menu addItem:item];
// If this device is the same one the microphone is using then
// we will use this menu item as the currently selected item
// in the microphone input popup button's list of items. For instance,
// if you are connected to an external display by default the external
// display's microphone might be used instead of the mac's built in
// mic.
if ([device isEqual:self.microphone.device])
{
defaultInputMenuItem = item;
}
}
self.microphoneInputPopUpButton.menu = menu;
//
// Set the selected device to the current selection on the
// microphone input popup button
//
[self.microphoneInputPopUpButton selectItem:defaultInputMenuItem];
}
//------------------------------------------------------------------------------
- (void) reloadMicrophoneInputChannelPopUpButtonMenu
{
NSMenu *menu = [[NSMenu alloc] init];
for (int i = 0; i < self.microphone.device.inputChannelCount; i++)
{
NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:@(i).stringValue
action:nil
keyEquivalent:@""];
[menu addItem:item];
}
self.microphoneInputChannelPopUpButton.menu = menu;
[self.microphoneInputChannelPopUpButton selectItemAtIndex:0];
}
//------------------------------------------------------------------------------
#pragma mark - Actions
//------------------------------------------------------------------------------
- (void)changedInput:(id)sender
{
EZAudioDevice *device = [sender representedObject];
[self.microphone setDevice:device];
-(void)changePlotType:(id)sender {
NSInteger selectedSegment = [sender selectedSegment];
switch(selectedSegment){
case 0:
[self drawBufferPlot];
break;
case 1:
[self drawRollingPlot];
break;
default:
break;
}
}
//------------------------------------------------------------------------------
- (void)changePlotType:(id)sender
{
NSInteger selectedSegment = [sender selectedSegment];
switch(selectedSegment)
{
case 0:
[self drawBufferPlot];
break;
case 1:
[self drawRollingPlot];
break;
default:
break;
}
-(void)toggleMicrophone:(id)sender {
switch([sender state]){
case NSOffState:
[self.microphone stopFetchingAudio];
break;
case NSOnState:
[self.microphone startFetchingAudio];
break;
default:
break;
}
}
//------------------------------------------------------------------------------
- (void)toggleMicrophone:(id)sender
{
switch([sender state])
{
case NSOffState:
[self.microphone stopFetchingAudio];
break;
case NSOnState:
[self.microphone startFetchingAudio];
break;
default:
break;
}
}
//------------------------------------------------------------------------------
#pragma mark - Action Extensions
//------------------------------------------------------------------------------
/*
Give the visualization of the current buffer (this is almost exactly the openFrameworks audio input eample)
*/
- (void)drawBufferPlot
{
self.audioPlot.plotType = EZPlotTypeBuffer;
self.audioPlot.shouldMirror = NO;
self.audioPlot.shouldFill = NO;
-(void)drawBufferPlot {
// Change the plot type to the buffer plot
self.audioPlot.plotType = EZPlotTypeBuffer;
// Don't mirror over the x-axis
self.audioPlot.shouldMirror = NO;
// Don't fill
self.audioPlot.shouldFill = NO;
}
//------------------------------------------------------------------------------
/*
Give the classic mirrored, rolling waveform look
*/
- (void)drawRollingPlot
{
self.audioPlot.plotType = EZPlotTypeRolling;
self.audioPlot.shouldFill = YES;
self.audioPlot.shouldMirror = YES;
-(void)drawRollingPlot {
self.audioPlot.plotType = EZPlotTypeRolling;
self.audioPlot.shouldFill = YES;
self.audioPlot.shouldMirror = YES;
}
#pragma mark - EZMicrophoneDelegate
#warning Thread Safety
// Note that any callback that provides streamed audio data (like streaming microphone input) happens on a separate audio thread that should not be blocked. When we feed audio data into any of the UI components we need to explicity create a GCD block on the main thread to properly get the UI to work.
- (void) microphone:(EZMicrophone *)microphone
hasAudioReceived:(float **)buffer
withBufferSize:(UInt32)bufferSize
withNumberOfChannels:(UInt32)numberOfChannels
{
// See the Thread Safety warning above, but in a nutshell these callbacks happen on a separate audio thread. We wrap any UI updating in a GCD block on the main thread to avoid blocking that audio flow.
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(),^{
-(void)microphone:(EZMicrophone *)microphone
hasAudioReceived:(float **)buffer
withBufferSize:(UInt32)bufferSize
withNumberOfChannels:(UInt32)numberOfChannels {
// Getting audio data as an array of float buffer arrays. What does that mean? Because the audio is coming in as a stereo signal the data is split into a left and right channel. So buffer[0] corresponds to the float* data for the left channel while buffer[1] corresponds to the float* data for the right channel.
// See the Thread Safety warning above, but in a nutshell these callbacks happen on a separate audio thread. We wrap any UI updating in a GCD block on the main thread to avoid blocking that audio flow.
dispatch_async(dispatch_get_main_queue(),^{
// All the audio plot needs is the buffer data (float*) and the size. Internally the audio plot will handle all the drawing related code, history management, and freeing its own resources. Hence, one badass line of code gets you a pretty plot :)
NSInteger channel = [weakSelf.microphoneInputChannelPopUpButton indexOfSelectedItem];
[weakSelf.audioPlot updateBuffer:buffer[channel] withBufferSize:bufferSize];
});
[self.audioPlot updateBuffer:buffer[0] withBufferSize:bufferSize];
});
}
//------------------------------------------------------------------------------
- (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
[EZAudioUtilities printASBD:audioStreamBasicDescription];
-(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];
}
//------------------------------------------------------------------------------
- (void)microphone:(EZMicrophone *)microphone
hasBufferList:(AudioBufferList *)bufferList
withBufferSize:(UInt32)bufferSize
withNumberOfChannels:(UInt32)numberOfChannels
{
-(void)microphone:(EZMicrophone *)microphone
hasBufferList:(AudioBufferList *)bufferList
withBufferSize:(UInt32)bufferSize
withNumberOfChannels:(UInt32)numberOfChannels {
// Getting audio data as a buffer list that can be directly fed into the EZRecorder or EZOutput. Say whattt...
}
//------------------------------------------------------------------------------
- (void)microphone:(EZMicrophone *)microphone
changedDevice:(EZAudioDevice *)device
{
__weak typeof (self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
//
// Set up the microphone input popup button's items to select
// between different microphone inputs
//
[weakSelf reloadMicrophoneInputPopUpButtonMenu];
//
// Set up the microphone input popup button's items to select
// between different microphone input channels
//
[weakSelf reloadMicrophoneInputChannelPopUpButtonMenu];
});
}
//------------------------------------------------------------------------------
@end
@@ -1,117 +1,66 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="7702" systemVersion="14C109" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="4514" systemVersion="13A603" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment version="1070" identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="7702"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="4514"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="CoreGraphicsWaveformViewController">
<connections>
<outlet property="audioPlot" destination="wpL-Ou-GSb" id="OME-Hf-I27"/>
<outlet property="microphoneInputChannelPopUpButton" destination="Yi6-fS-Cob" id="pLg-4c-klV"/>
<outlet property="microphoneInputPopUpButton" destination="SjR-qx-mWV" id="NuN-SS-ESg"/>
<outlet property="view" destination="wpL-Ou-GSb" id="oxJ-iT-SKO"/>
<outlet property="audioPlot" destination="1uw-Yi-HM2" id="64z-gn-hPW"/>
<outlet property="view" destination="1" id="2"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customView id="wpL-Ou-GSb" customClass="EZAudioPlot">
<customObject id="-3" userLabel="Application"/>
<customView id="1">
<rect key="frame" x="0.0" y="0.0" width="480" height="272"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<button translatesAutoresizingMaskIntoConstraints="NO" id="kAI-gs-c31">
<rect key="frame" x="339" y="44" width="123" height="18"/>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="1uw-Yi-HM2" customClass="EZAudioPlot">
<rect key="frame" x="0.0" y="0.0" width="480" height="272"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<button translatesAutoresizingMaskIntoConstraints="NO" id="bst-4p-f4H">
<rect key="frame" x="18" y="18" width="119" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" title="Microphone On" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="X83-kX-Oau">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="toggleMicrophone:" target="-2" id="WUF-aw-GV2"/>
</connections>
</button>
<segmentedControl verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="xVu-eW-fRp">
<rect key="frame" x="333" y="15" width="129" height="24"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<segmentedCell key="cell" alignment="left" style="rounded" trackingMode="selectOne" id="mpa-RS-2hy">
<font key="font" metaFont="system"/>
<segments>
<segment label="Buffer" selected="YES"/>
<segment label="Rolling" tag="1"/>
</segments>
</segmentedCell>
<connections>
<action selector="changePlotType:" target="-2" id="IsT-Sq-sKO"/>
</connections>
</segmentedControl>
</subviews>
<constraints>
<constraint firstAttribute="width" constant="119" id="FP4-Wg-HAb"/>
<constraint firstItem="bst-4p-f4H" firstAttribute="leading" secondItem="1uw-Yi-HM2" secondAttribute="leading" constant="20" id="OEK-K5-KgC"/>
<constraint firstAttribute="bottom" secondItem="bst-4p-f4H" secondAttribute="bottom" constant="20" id="Xb8-Oc-8K5"/>
<constraint firstAttribute="bottom" secondItem="xVu-eW-fRp" secondAttribute="bottom" constant="17" id="gPn-xu-hrZ"/>
<constraint firstAttribute="trailing" secondItem="xVu-eW-fRp" secondAttribute="trailing" constant="20" id="tHh-2L-ypE"/>
</constraints>
<buttonCell key="cell" type="check" title="Microphone On" bezelStyle="regularSquare" imagePosition="right" state="on" inset="2" id="Aml-Gg-JmL">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="toggleMicrophone:" target="-2" id="sFe-vk-GLb"/>
</connections>
</button>
<segmentedControl verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="HTa-6n-EPo">
<rect key="frame" x="335" y="15" width="127" height="24"/>
<segmentedCell key="cell" borderStyle="border" alignment="left" style="rounded" trackingMode="selectOne" id="bLT-tl-mJ6">
<font key="font" metaFont="system"/>
<segments>
<segment label="Buffer" selected="YES"/>
<segment label="Rolling" tag="1"/>
</segments>
</segmentedCell>
<connections>
<action selector="changePlotType:" target="-2" id="JTN-f7-xiC"/>
</connections>
</segmentedControl>
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="SjR-qx-mWV" userLabel="microphoneInputPopUpButton">
<rect key="frame" x="18" y="14" width="180" height="26"/>
<constraints>
<constraint firstAttribute="width" constant="175" id="qhz-1c-cOR"/>
</constraints>
<popUpButtonCell key="cell" type="push" title="Item 1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="4Kz-CU-9wA" id="Ifz-u8-4sz">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" id="si8-Yh-kZX">
<items>
<menuItem title="Item 1" state="on" id="4Kz-CU-9wA"/>
<menuItem title="Item 2" id="XbC-Uc-PrQ"/>
<menuItem title="Item 3" id="aao-3c-s5T"/>
</items>
</menu>
</popUpButtonCell>
</popUpButton>
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Yi6-fS-Cob" userLabel="microphoneInputChannelPopUpButton">
<rect key="frame" x="204" y="14" width="79" height="26"/>
<constraints>
<constraint firstAttribute="width" constant="74" id="w4n-SY-B3n"/>
</constraints>
<popUpButtonCell key="cell" type="push" title="1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="K3K-bf-PA5" id="mM3-bX-3dm">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" id="u3a-FE-6Wo">
<items>
<menuItem title="1" state="on" id="K3K-bf-PA5"/>
<menuItem title="Item 2" id="gAP-8p-hIp"/>
<menuItem title="Item 3" id="5ds-DC-X9S"/>
</items>
</menu>
</popUpButtonCell>
</popUpButton>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="fi6-Uh-bvr">
<rect key="frame" x="18" y="43" width="36" height="17"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Input" id="Png-Pk-fMc">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="HVN-Im-3J1">
<rect key="frame" x="204" y="43" width="55" height="17"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Channel" id="lkh-zp-WCY">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</customView>
</subviews>
<constraints>
<constraint firstAttribute="bottom" secondItem="SjR-qx-mWV" secondAttribute="bottom" constant="17" id="1kd-d1-9WR"/>
<constraint firstAttribute="bottom" secondItem="HTa-6n-EPo" secondAttribute="bottom" constant="17" id="47V-Zg-MD0"/>
<constraint firstAttribute="bottom" secondItem="Yi6-fS-Cob" secondAttribute="bottom" constant="17" id="B2R-Tc-cz7"/>
<constraint firstItem="Yi6-fS-Cob" firstAttribute="top" secondItem="HVN-Im-3J1" secondAttribute="bottom" constant="5" id="DoN-81-ICT"/>
<constraint firstItem="HTa-6n-EPo" firstAttribute="top" secondItem="kAI-gs-c31" secondAttribute="bottom" constant="8" id="GLf-f7-seC"/>
<constraint firstItem="Yi6-fS-Cob" firstAttribute="leading" secondItem="SjR-qx-mWV" secondAttribute="trailing" constant="11" id="Pqx-7E-osx"/>
<constraint firstItem="Yi6-fS-Cob" firstAttribute="leading" secondItem="HVN-Im-3J1" secondAttribute="leading" id="Ux9-Kd-07J"/>
<constraint firstItem="SjR-qx-mWV" firstAttribute="leading" secondItem="wpL-Ou-GSb" secondAttribute="leading" constant="20" id="b29-6j-MdI"/>
<constraint firstAttribute="trailing" secondItem="kAI-gs-c31" secondAttribute="trailing" constant="20" id="bR5-ru-Lto"/>
<constraint firstItem="fi6-Uh-bvr" firstAttribute="leading" secondItem="wpL-Ou-GSb" secondAttribute="leading" constant="20" id="gqn-qW-0CY"/>
<constraint firstItem="HTa-6n-EPo" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="Yi6-fS-Cob" secondAttribute="trailing" constant="11" id="qGD-Te-9IQ"/>
<constraint firstItem="SjR-qx-mWV" firstAttribute="top" secondItem="fi6-Uh-bvr" secondAttribute="bottom" constant="5" id="qkS-wp-O0d"/>
<constraint firstAttribute="trailing" secondItem="HTa-6n-EPo" secondAttribute="trailing" constant="20" id="rGZ-5W-ZDN"/>
<constraint firstItem="1uw-Yi-HM2" firstAttribute="leading" secondItem="1" secondAttribute="leading" id="Fcx-zY-IPv"/>
<constraint firstItem="1uw-Yi-HM2" firstAttribute="top" secondItem="1" secondAttribute="top" id="Sfu-lR-KCJ"/>
<constraint firstAttribute="trailing" secondItem="1uw-Yi-HM2" secondAttribute="trailing" id="bjW-C2-2Mw"/>
<constraint firstAttribute="bottom" secondItem="1uw-Yi-HM2" secondAttribute="bottom" id="f1L-xR-BaI"/>
</constraints>
<point key="canvasLocation" x="178" y="314"/>
</customView>
</objects>
</document>
</document>
@@ -5,22 +5,18 @@
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>com.sha.$(PRODUCT_NAME:rfc1034identifier)</string>
<string>com.sha.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key>
<string></string>
<string>1</string>
</dict>
</plist>
@@ -0,0 +1,51 @@
//
// EZAudioCoreGraphicsWaveformExampleTests.m
// EZAudioCoreGraphicsWaveformExampleTests
//
// Created by Syed Haris Ali on 12/1/13.
// Copyright (c) 2013 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 <XCTest/XCTest.h>
@interface EZAudioCoreGraphicsWaveformExampleTests : XCTestCase
@end
@implementation EZAudioCoreGraphicsWaveformExampleTests
- (void)setUp
{
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
}
- (void)tearDown
{
// Put teardown code here. This method is called after the invocation of each test method in the class.
[super tearDown];
}
- (void)testExample
{
XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__);
}
@end
@@ -0,0 +1,2 @@
/* Localized versions of Info.plist keys */
@@ -1,9 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:../../EZAudio/OSX/EZAudio.xcodeproj">
</FileRef>
<FileRef
location = "group:EZAudioCoreGraphicsWaveformExample/EZAudioCoreGraphicsWaveformExample.xcodeproj">
</FileRef>
@@ -0,0 +1,41 @@
<?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>IDESourceControlProjectFavoriteDictionaryKey</key>
<false/>
<key>IDESourceControlProjectIdentifier</key>
<string>C3083F0E-46EF-4F95-B26F-149C34EA858B</string>
<key>IDESourceControlProjectName</key>
<string>EZAudioExamplesOSX</string>
<key>IDESourceControlProjectOriginsDictionary</key>
<dict>
<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>9D6FF97A89F512CD81EAE1A971A1D2EB03E03F7C</key>
<string>../../..</string>
</dict>
<key>IDESourceControlProjectURL</key>
<string>https://github.com/syedhali/EZAudio.git</string>
<key>IDESourceControlProjectVersion</key>
<integer>111</integer>
<key>IDESourceControlProjectWCCIdentifier</key>
<string>9D6FF97A89F512CD81EAE1A971A1D2EB03E03F7C</string>
<key>IDESourceControlProjectWCConfigurations</key>
<array>
<dict>
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
<string>public.vcs.git</string>
<key>IDESourceControlWCCIdentifierKey</key>
<string>9D6FF97A89F512CD81EAE1A971A1D2EB03E03F7C</string>
<key>IDESourceControlWCCName</key>
<string>EZAudio</string>
</dict>
</array>
</dict>
</plist>
@@ -7,8 +7,6 @@
objects = {
/* Begin PBXBuildFile section */
6611CEBE1B45F76200AE0EE8 /* EZAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6611CEBD1B45F76200AE0EE8 /* EZAudio.framework */; };
6611CEBF1B45F76200AE0EE8 /* EZAudio.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 6611CEBD1B45F76200AE0EE8 /* EZAudio.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
9417A8F71871492000D9D37B /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9417A8F61871492000D9D37B /* Cocoa.framework */; };
9417A9011871492000D9D37B /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 9417A8FF1871492000D9D37B /* InfoPlist.strings */; };
9417A9031871492000D9D37B /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A9021871492000D9D37B /* main.m */; };
@@ -16,6 +14,23 @@
9417A90A1871492100D9D37B /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A9091871492100D9D37B /* AppDelegate.m */; };
9417A90D1871492100D9D37B /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9417A90B1871492100D9D37B /* MainMenu.xib */; };
9417A90F1871492100D9D37B /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9417A90E1871492100D9D37B /* Images.xcassets */; };
9417A9161871492100D9D37B /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9417A9151871492100D9D37B /* XCTest.framework */; };
9417A9171871492100D9D37B /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9417A8F61871492000D9D37B /* Cocoa.framework */; };
9417A91F1871492100D9D37B /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 9417A91D1871492100D9D37B /* InfoPlist.strings */; };
9417A9211871492100D9D37B /* EZAudioFFTExampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A9201871492100D9D37B /* EZAudioFFTExampleTests.m */; };
9417A9441871493900D9D37B /* AEFloatConverter.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A92C1871493900D9D37B /* AEFloatConverter.m */; };
9417A9451871493900D9D37B /* EZAudio.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A92E1871493900D9D37B /* EZAudio.m */; };
9417A9461871493900D9D37B /* EZAudioFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A9301871493900D9D37B /* EZAudioFile.m */; };
9417A9471871493900D9D37B /* EZAudioPlot.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A9321871493900D9D37B /* EZAudioPlot.m */; };
9417A9481871493900D9D37B /* EZAudioPlotGL.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A9341871493900D9D37B /* EZAudioPlotGL.m */; };
9417A9491871493900D9D37B /* EZAudioPlotGLKViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A9361871493900D9D37B /* EZAudioPlotGLKViewController.m */; };
9417A94A1871493900D9D37B /* EZMicrophone.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A9381871493900D9D37B /* EZMicrophone.m */; };
9417A94B1871493900D9D37B /* EZOutput.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A93A1871493900D9D37B /* EZOutput.m */; };
9417A94C1871493900D9D37B /* EZPlot.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A93C1871493900D9D37B /* EZPlot.m */; };
9417A94D1871493900D9D37B /* EZRecorder.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A93E1871493900D9D37B /* EZRecorder.m */; };
9417A94E1871493900D9D37B /* TPCircularBuffer.c in Sources */ = {isa = PBXBuildFile; fileRef = 9417A93F1871493900D9D37B /* TPCircularBuffer.c */; };
9417A94F1871493900D9D37B /* CHANGELOG in Resources */ = {isa = PBXBuildFile; fileRef = 9417A9421871493900D9D37B /* CHANGELOG */; };
9417A9501871493900D9D37B /* VERSION in Resources */ = {isa = PBXBuildFile; fileRef = 9417A9431871493900D9D37B /* VERSION */; };
9417A954187149EA00D9D37B /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9417A951187149EA00D9D37B /* AudioToolbox.framework */; };
9417A955187149EA00D9D37B /* AudioUnit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9417A952187149EA00D9D37B /* AudioUnit.framework */; };
9417A956187149EA00D9D37B /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9417A953187149EA00D9D37B /* CoreAudio.framework */; };
@@ -27,22 +42,17 @@
9417A9D71872130200D9D37B /* FFTViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 9417A9D51872130200D9D37B /* FFTViewController.xib */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
6611CEC01B45F76200AE0EE8 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
6611CEBF1B45F76200AE0EE8 /* EZAudio.framework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
/* Begin PBXContainerItemProxy section */
9417A9181871492100D9D37B /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 9417A8EB1871492000D9D37B /* Project object */;
proxyType = 1;
remoteGlobalIDString = 9417A8F21871492000D9D37B;
remoteInfo = EZAudioFFTExample;
};
/* End PBXCopyFilesBuildPhase section */
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
6611CEBD1B45F76200AE0EE8 /* EZAudio.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; name = EZAudio.framework; path = /Users/haris/Documents/code/openSource/EZAudio/EZAudioExamples/OSX/Build/Products/Debug/EZAudio.framework; sourceTree = "<absolute>"; };
9417A8F31871492000D9D37B /* EZAudioFFTExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = EZAudioFFTExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
9417A8F61871492000D9D37B /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
9417A8F91871492000D9D37B /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
@@ -57,7 +67,35 @@
9417A9091871492100D9D37B /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
9417A90C1871492100D9D37B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
9417A90E1871492100D9D37B /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
9417A9141871492100D9D37B /* EZAudioFFTExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = EZAudioFFTExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
9417A9151871492100D9D37B /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
9417A91C1871492100D9D37B /* EZAudioFFTExampleTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "EZAudioFFTExampleTests-Info.plist"; sourceTree = "<group>"; };
9417A91E1871492100D9D37B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
9417A9201871492100D9D37B /* EZAudioFFTExampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EZAudioFFTExampleTests.m; sourceTree = "<group>"; };
9417A92B1871493900D9D37B /* AEFloatConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEFloatConverter.h; sourceTree = "<group>"; };
9417A92C1871493900D9D37B /* AEFloatConverter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AEFloatConverter.m; sourceTree = "<group>"; };
9417A92D1871493900D9D37B /* EZAudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudio.h; sourceTree = "<group>"; };
9417A92E1871493900D9D37B /* EZAudio.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudio.m; sourceTree = "<group>"; };
9417A92F1871493900D9D37B /* EZAudioFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioFile.h; sourceTree = "<group>"; };
9417A9301871493900D9D37B /* EZAudioFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioFile.m; sourceTree = "<group>"; };
9417A9311871493900D9D37B /* EZAudioPlot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioPlot.h; sourceTree = "<group>"; };
9417A9321871493900D9D37B /* EZAudioPlot.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioPlot.m; sourceTree = "<group>"; };
9417A9331871493900D9D37B /* EZAudioPlotGL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioPlotGL.h; sourceTree = "<group>"; };
9417A9341871493900D9D37B /* EZAudioPlotGL.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioPlotGL.m; sourceTree = "<group>"; };
9417A9351871493900D9D37B /* EZAudioPlotGLKViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioPlotGLKViewController.h; sourceTree = "<group>"; };
9417A9361871493900D9D37B /* EZAudioPlotGLKViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioPlotGLKViewController.m; sourceTree = "<group>"; };
9417A9371871493900D9D37B /* EZMicrophone.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZMicrophone.h; sourceTree = "<group>"; };
9417A9381871493900D9D37B /* EZMicrophone.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZMicrophone.m; sourceTree = "<group>"; };
9417A9391871493900D9D37B /* EZOutput.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZOutput.h; sourceTree = "<group>"; };
9417A93A1871493900D9D37B /* EZOutput.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZOutput.m; sourceTree = "<group>"; };
9417A93B1871493900D9D37B /* EZPlot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZPlot.h; sourceTree = "<group>"; };
9417A93C1871493900D9D37B /* EZPlot.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZPlot.m; sourceTree = "<group>"; };
9417A93D1871493900D9D37B /* EZRecorder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZRecorder.h; sourceTree = "<group>"; };
9417A93E1871493900D9D37B /* EZRecorder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZRecorder.m; sourceTree = "<group>"; };
9417A93F1871493900D9D37B /* TPCircularBuffer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = TPCircularBuffer.c; sourceTree = "<group>"; };
9417A9401871493900D9D37B /* TPCircularBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPCircularBuffer.h; sourceTree = "<group>"; };
9417A9421871493900D9D37B /* CHANGELOG */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CHANGELOG; sourceTree = "<group>"; };
9417A9431871493900D9D37B /* VERSION */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = VERSION; sourceTree = "<group>"; };
9417A951187149EA00D9D37B /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
9417A952187149EA00D9D37B /* AudioUnit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioUnit.framework; path = System/Library/Frameworks/AudioUnit.framework; sourceTree = SDKROOT; };
9417A953187149EA00D9D37B /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; };
@@ -75,7 +113,6 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
6611CEBE1B45F76200AE0EE8 /* EZAudio.framework in Frameworks */,
9417A95E18714A2A00D9D37B /* Accelerate.framework in Frameworks */,
9417A95C18714A1000D9D37B /* QuartzCore.framework in Frameworks */,
9417A959187149F000D9D37B /* GLKit.framework in Frameworks */,
@@ -87,14 +124,23 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
9417A9111871492100D9D37B /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
9417A9171871492100D9D37B /* Cocoa.framework in Frameworks */,
9417A9161871492100D9D37B /* XCTest.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
9417A8EA1871492000D9D37B = {
isa = PBXGroup;
children = (
6611CEBD1B45F76200AE0EE8 /* EZAudio.framework */,
9417A8FC1871492000D9D37B /* EZAudioFFTExample */,
9417A91A1871492100D9D37B /* EZAudioFFTExampleTests */,
9417A8F51871492000D9D37B /* Frameworks */,
9417A8F41871492000D9D37B /* Products */,
);
@@ -104,6 +150,7 @@
isa = PBXGroup;
children = (
9417A8F31871492000D9D37B /* EZAudioFFTExample.app */,
9417A9141871492100D9D37B /* EZAudioFFTExampleTests.xctest */,
);
name = Products;
sourceTree = "<group>";
@@ -138,6 +185,7 @@
9417A8FC1871492000D9D37B /* EZAudioFFTExample */ = {
isa = PBXGroup;
children = (
9417A92A1871493900D9D37B /* EZAudio */,
9417A9081871492100D9D37B /* AppDelegate.h */,
9417A9091871492100D9D37B /* AppDelegate.m */,
9417A9D31872130200D9D37B /* FFTViewController.h */,
@@ -162,6 +210,64 @@
name = "Supporting Files";
sourceTree = "<group>";
};
9417A91A1871492100D9D37B /* EZAudioFFTExampleTests */ = {
isa = PBXGroup;
children = (
9417A9201871492100D9D37B /* EZAudioFFTExampleTests.m */,
9417A91B1871492100D9D37B /* Supporting Files */,
);
path = EZAudioFFTExampleTests;
sourceTree = "<group>";
};
9417A91B1871492100D9D37B /* Supporting Files */ = {
isa = PBXGroup;
children = (
9417A91C1871492100D9D37B /* EZAudioFFTExampleTests-Info.plist */,
9417A91D1871492100D9D37B /* InfoPlist.strings */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
9417A92A1871493900D9D37B /* EZAudio */ = {
isa = PBXGroup;
children = (
9417A92B1871493900D9D37B /* AEFloatConverter.h */,
9417A92C1871493900D9D37B /* AEFloatConverter.m */,
9417A92D1871493900D9D37B /* EZAudio.h */,
9417A92E1871493900D9D37B /* EZAudio.m */,
9417A92F1871493900D9D37B /* EZAudioFile.h */,
9417A9301871493900D9D37B /* EZAudioFile.m */,
9417A9311871493900D9D37B /* EZAudioPlot.h */,
9417A9321871493900D9D37B /* EZAudioPlot.m */,
9417A9331871493900D9D37B /* EZAudioPlotGL.h */,
9417A9341871493900D9D37B /* EZAudioPlotGL.m */,
9417A9351871493900D9D37B /* EZAudioPlotGLKViewController.h */,
9417A9361871493900D9D37B /* EZAudioPlotGLKViewController.m */,
9417A9371871493900D9D37B /* EZMicrophone.h */,
9417A9381871493900D9D37B /* EZMicrophone.m */,
9417A9391871493900D9D37B /* EZOutput.h */,
9417A93A1871493900D9D37B /* EZOutput.m */,
9417A93B1871493900D9D37B /* EZPlot.h */,
9417A93C1871493900D9D37B /* EZPlot.m */,
9417A93D1871493900D9D37B /* EZRecorder.h */,
9417A93E1871493900D9D37B /* EZRecorder.m */,
9417A93F1871493900D9D37B /* TPCircularBuffer.c */,
9417A9401871493900D9D37B /* TPCircularBuffer.h */,
9417A9411871493900D9D37B /* VERSION */,
);
name = EZAudio;
path = ../../../../EZAudio;
sourceTree = "<group>";
};
9417A9411871493900D9D37B /* VERSION */ = {
isa = PBXGroup;
children = (
9417A9421871493900D9D37B /* CHANGELOG */,
9417A9431871493900D9D37B /* VERSION */,
);
path = VERSION;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -172,7 +278,6 @@
9417A8EF1871492000D9D37B /* Sources */,
9417A8F01871492000D9D37B /* Frameworks */,
9417A8F11871492000D9D37B /* Resources */,
6611CEC01B45F76200AE0EE8 /* Embed Frameworks */,
);
buildRules = (
);
@@ -183,6 +288,24 @@
productReference = 9417A8F31871492000D9D37B /* EZAudioFFTExample.app */;
productType = "com.apple.product-type.application";
};
9417A9131871492100D9D37B /* EZAudioFFTExampleTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 9417A9271871492100D9D37B /* Build configuration list for PBXNativeTarget "EZAudioFFTExampleTests" */;
buildPhases = (
9417A9101871492100D9D37B /* Sources */,
9417A9111871492100D9D37B /* Frameworks */,
9417A9121871492100D9D37B /* Resources */,
);
buildRules = (
);
dependencies = (
9417A9191871492100D9D37B /* PBXTargetDependency */,
);
name = EZAudioFFTExampleTests;
productName = EZAudioFFTExampleTests;
productReference = 9417A9141871492100D9D37B /* EZAudioFFTExampleTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
@@ -191,6 +314,11 @@
attributes = {
LastUpgradeCheck = 0500;
ORGANIZATIONNAME = "Syed Haris Ali";
TargetAttributes = {
9417A9131871492100D9D37B = {
TestTargetID = 9417A8F21871492000D9D37B;
};
};
};
buildConfigurationList = 9417A8EE1871492000D9D37B /* Build configuration list for PBXProject "EZAudioFFTExample" */;
compatibilityVersion = "Xcode 3.2";
@@ -206,6 +334,7 @@
projectRoot = "";
targets = (
9417A8F21871492000D9D37B /* EZAudioFFTExample */,
9417A9131871492100D9D37B /* EZAudioFFTExampleTests */,
);
};
/* End PBXProject section */
@@ -219,7 +348,17 @@
9417A9D71872130200D9D37B /* FFTViewController.xib in Resources */,
9417A90F1871492100D9D37B /* Images.xcassets in Resources */,
9417A9071871492100D9D37B /* Credits.rtf in Resources */,
9417A9501871493900D9D37B /* VERSION in Resources */,
9417A90D1871492100D9D37B /* MainMenu.xib in Resources */,
9417A94F1871493900D9D37B /* CHANGELOG in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
9417A9121871492100D9D37B /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9417A91F1871492100D9D37B /* InfoPlist.strings in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -230,14 +369,41 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9417A94D1871493900D9D37B /* EZRecorder.m in Sources */,
9417A9471871493900D9D37B /* EZAudioPlot.m in Sources */,
9417A94C1871493900D9D37B /* EZPlot.m in Sources */,
9417A9D61872130200D9D37B /* FFTViewController.m in Sources */,
9417A9441871493900D9D37B /* AEFloatConverter.m in Sources */,
9417A94A1871493900D9D37B /* EZMicrophone.m in Sources */,
9417A94B1871493900D9D37B /* EZOutput.m in Sources */,
9417A9451871493900D9D37B /* EZAudio.m in Sources */,
9417A94E1871493900D9D37B /* TPCircularBuffer.c in Sources */,
9417A9481871493900D9D37B /* EZAudioPlotGL.m in Sources */,
9417A9461871493900D9D37B /* EZAudioFile.m in Sources */,
9417A90A1871492100D9D37B /* AppDelegate.m in Sources */,
9417A9491871493900D9D37B /* EZAudioPlotGLKViewController.m in Sources */,
9417A9031871492000D9D37B /* main.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
9417A9101871492100D9D37B /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9417A9211871492100D9D37B /* EZAudioFFTExampleTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
9417A9191871492100D9D37B /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 9417A8F21871492000D9D37B /* EZAudioFFTExample */;
targetProxy = 9417A9181871492100D9D37B /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
9417A8FF1871492000D9D37B /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
@@ -263,6 +429,14 @@
name = MainMenu.xib;
sourceTree = "<group>";
};
9417A91D1871492100D9D37B /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
9417A91E1871492100D9D37B /* en */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
@@ -339,14 +513,9 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
COMBINE_HIDPI_IMAGES = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
/Users/haris/Documents/code/openSource/EZAudio/EZAudioExamples/OSX/Build/Products/Debug,
);
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "EZAudioFFTExample/EZAudioFFTExample-Prefix.pch";
INFOPLIST_FILE = "EZAudioFFTExample/EZAudioFFTExample-Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
@@ -357,19 +526,54 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
COMBINE_HIDPI_IMAGES = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
/Users/haris/Documents/code/openSource/EZAudio/EZAudioExamples/OSX/Build/Products/Debug,
);
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "EZAudioFFTExample/EZAudioFFTExample-Prefix.pch";
INFOPLIST_FILE = "EZAudioFFTExample/EZAudioFFTExample-Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
name = Release;
};
9417A9281871492100D9D37B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/EZAudioFFTExample.app/Contents/MacOS/EZAudioFFTExample";
COMBINE_HIDPI_IMAGES = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(DEVELOPER_FRAMEWORKS_DIR)",
"$(inherited)",
);
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "EZAudioFFTExample/EZAudioFFTExample-Prefix.pch";
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
INFOPLIST_FILE = "EZAudioFFTExampleTests/EZAudioFFTExampleTests-Info.plist";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUNDLE_LOADER)";
WRAPPER_EXTENSION = xctest;
};
name = Debug;
};
9417A9291871492100D9D37B /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/EZAudioFFTExample.app/Contents/MacOS/EZAudioFFTExample";
COMBINE_HIDPI_IMAGES = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(DEVELOPER_FRAMEWORKS_DIR)",
"$(inherited)",
);
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "EZAudioFFTExample/EZAudioFFTExample-Prefix.pch";
INFOPLIST_FILE = "EZAudioFFTExampleTests/EZAudioFFTExampleTests-Info.plist";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUNDLE_LOADER)";
WRAPPER_EXTENSION = xctest;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
@@ -391,6 +595,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
9417A9271871492100D9D37B /* Build configuration list for PBXNativeTarget "EZAudioFFTExampleTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
9417A9281871492100D9D37B /* Debug */,
9417A9291871492100D9D37B /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 9417A8EB1871492000D9D37B /* Project object */;
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="5053" systemVersion="13C64" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="4514" systemVersion="13A603" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="5053"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="4514"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
@@ -654,13 +654,26 @@
<view key="contentView" id="372">
<rect key="frame" x="0.0" y="0.0" width="480" height="360"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="PCv-Jm-jzO" customClass="EZAudioPlot">
<rect key="frame" x="0.0" y="0.0" width="480" height="360"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
</customView>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="PCv-Jm-jzO" secondAttribute="trailing" id="A2i-gs-EWe"/>
<constraint firstItem="PCv-Jm-jzO" firstAttribute="leading" secondItem="372" secondAttribute="leading" id="Gex-TR-aqy"/>
<constraint firstAttribute="bottom" secondItem="PCv-Jm-jzO" secondAttribute="bottom" id="Kqa-V4-EDP"/>
<constraint firstItem="PCv-Jm-jzO" firstAttribute="top" secondItem="372" secondAttribute="top" id="MeC-7d-nwe"/>
</constraints>
</view>
</window>
<customObject id="494" customClass="AppDelegate">
<connections>
<outlet property="audioPlot" destination="PCv-Jm-jzO" id="aGl-Mf-G6W"/>
<outlet property="window" destination="371" id="532"/>
</connections>
</customObject>
<customObject id="420" customClass="NSFontManager"/>
</objects>
</document>
</document>
@@ -28,7 +28,7 @@
/**
EZAudio
*/
#import <EZAudio/EZAudio.h>
#import "EZAudio.h"
/**
Accelerate
@@ -49,7 +49,7 @@
/**
EZAudioPlot for time plot
*/
@property (nonatomic,weak) IBOutlet EZAudioPlot *audioPlotTime;
@property (nonatomic,weak) IBOutlet EZAudioPlotGL *audioPlotTime;
/**
Microphone
@@ -25,181 +25,152 @@
#import "FFTViewController.h"
@interface FFTViewController ()
@property (nonatomic, assign) COMPLEX_SPLIT *A;
@property (nonatomic, assign) FFTSetup FFTSetup;
@property (nonatomic, assign) vDSP_Length log2n;
@property (nonatomic, assign) BOOL isFFTSetup;
@interface FFTViewController (){
COMPLEX_SPLIT _A;
FFTSetup _FFTSetup;
BOOL _isFFTSetup;
vDSP_Length _log2n;
}
@end
@implementation FFTViewController
@synthesize audioPlotFreq;
@synthesize audioPlotTime;
@synthesize microphone;
//------------------------------------------------------------------------------
#pragma mark - Dealloc
//------------------------------------------------------------------------------
- (void)dealloc
{
vDSP_destroy_fftsetup(self.FFTSetup);
free(self.A);
#pragma mark - Initialization
-(id)init {
self = [super initWithNibName:NSStringFromClass(self.class) bundle:nil];
if(self){
[self initializeViewController];
}
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithNibName:NSStringFromClass(self.class) bundle:nil];
if(self){
[self initializeViewController];
}
return self;
}
-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:NSStringFromClass(self.class) bundle:nil];
if(self){
[self initializeViewController];
}
return self;
}
#pragma mark - Initialize View Controller
-(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
startsImmediately:YES];
}
//------------------------------------------------------------------------------
#pragma mark - Customize the Audio Plot
//------------------------------------------------------------------------------
- (void)awakeFromNib
{
//
// Setup time domain audio plot
//
self.audioPlotTime.backgroundColor = [NSColor colorWithCalibratedRed: 0.569 green: 0.82 blue: 0.478 alpha: 1];
self.audioPlotTime.color = [NSColor colorWithCalibratedRed:1.0 green:1.0 blue:1.0 alpha:1.0];
self.audioPlotTime.shouldFill = YES;
self.audioPlotTime.shouldMirror = YES;
self.audioPlotTime.plotType = EZPlotTypeRolling;
//
// Setup frequency domain audio plot
//
self.audioPlotFreq.backgroundColor = [NSColor colorWithCalibratedRed: 0.984 green: 0.471 blue: 0.525 alpha: 1];
self.audioPlotFreq.color = [NSColor colorWithCalibratedRed:1.0 green:1.0 blue:1.0 alpha:1.0];
self.audioPlotFreq.shouldFill = YES;
self.audioPlotFreq.plotType = EZPlotTypeBuffer;
self.audioPlotFreq.shouldCenterYAxis = NO;
-(void)awakeFromNib {
// Setup time domain audio plot
self.audioPlotTime.backgroundColor = [NSColor colorWithCalibratedRed: 0.569 green: 0.82 blue: 0.478 alpha: 1];
self.audioPlotTime.color = [NSColor colorWithCalibratedRed:1.0 green:1.0 blue:1.0 alpha:1.0];
self.audioPlotTime.shouldFill = YES;
self.audioPlotTime.shouldMirror = YES;
self.audioPlotTime.plotType = EZPlotTypeRolling;
// Setup frequency domain audio plot
self.audioPlotFreq.backgroundColor = [NSColor colorWithCalibratedRed: 0.984 green: 0.471 blue: 0.525 alpha: 1];
self.audioPlotFreq.color = [NSColor colorWithCalibratedRed:1.0 green:1.0 blue:1.0 alpha:1.0];
self.audioPlotFreq.shouldFill = YES;
self.audioPlotFreq.plotType = EZPlotTypeBuffer;
//
// Create an instance of the microphone and tell it to use this view controller instance as the delegate
//
self.microphone = [EZMicrophone microphoneWithDelegate:self
startsImmediately:YES];
}
//------------------------------------------------------------------------------
#pragma mark - FFT
//------------------------------------------------------------------------------
/**
Adapted from http://batmobile.blogs.ilrt.org/fourier-transforms-on-an-iphone/
*/
- (void)createFFTWithBufferSize:(float)bufferSize
withAudioData:(float *)data
{
//
// Setup the length
//
self.log2n = log2f(bufferSize);
//
// Calculate the weights array. This is a one-off operation.
//
_FFTSetup = vDSP_create_fftsetup(self.log2n, FFT_RADIX2);
//
// For an FFT, numSamples must be a power of 2, i.e. is always even
//
int nOver2 = bufferSize/2;
//
// Populate *window with the values for a hamming window function
//
float *window = (float *)malloc(sizeof(float) * bufferSize);
vDSP_hamm_window(window, bufferSize, 0);
//
// Window the samples
//
vDSP_vmul(data, 1, window, 1, data, 1, bufferSize);
//
// Define complex buffer
//
self.A = malloc(sizeof(COMPLEX_SPLIT));
self.A->realp = (float *) malloc(nOver2 * sizeof(float));
self.A->imagp = (float *) malloc(nOver2 * sizeof(float));
-(void)createFFTWithBufferSize:(float)bufferSize withAudioData:(float*)data {
// Setup the length
_log2n = log2f(bufferSize);
// Calculate the weights array. This is a one-off operation.
_FFTSetup = vDSP_create_fftsetup(_log2n, FFT_RADIX2);
// For an FFT, numSamples must be a power of 2, i.e. is always even
int nOver2 = bufferSize/2;
// Populate *window with the values for a hamming window function
float *window = (float *)malloc(sizeof(float)*bufferSize);
vDSP_hamm_window(window, bufferSize, 0);
// Window the samples
vDSP_vmul(data, 1, window, 1, data, 1, bufferSize);
// Define complex buffer
_A.realp = (float *) malloc(nOver2*sizeof(float));
_A.imagp = (float *) malloc(nOver2*sizeof(float));
}
//------------------------------------------------------------------------------
- (void)updateFFTWithBufferSize:(float)bufferSize
withAudioData:(float*)data
{
//
// For an FFT, numSamples must be a power of 2, i.e. is always even
//
int nOver2 = bufferSize / 2;
//
// Pack samples:
// C(re) -> A[n], C(im) -> A[n+1]
//
vDSP_ctoz((COMPLEX *)data, 2, self.A, 1, nOver2);
//
// Perform a forward FFT using fftSetup and A
// Results are returned in A
//
vDSP_fft_zrip(_FFTSetup, self.A, 1, self.log2n, FFT_FORWARD);
//
// Convert COMPLEX_SPLIT A result to magnitudes
//
float amp[nOver2];
float maxMag = 0;
for(int i = 0; i < nOver2; i++)
{
// Calculate the magnitude
float mag = self.A->realp[i]*self.A->realp[i]+self.A->imagp[i]*self.A->imagp[i];
maxMag = mag > maxMag ? mag : maxMag;
}
for(int i=0; i < nOver2; i++)
{
// Calculate the magnitude
float mag = self.A->realp[i]*self.A->realp[i]+self.A->imagp[i]*self.A->imagp[i];
// Bind the value to be less than 1.0 to fit in the graph
amp[i] = [EZAudioUtilities MAP:mag leftMin:0.0 leftMax:maxMag rightMin:0.0 rightMax:1.0];
}
//
// Update the frequency domain plot
//
[self.audioPlotFreq updateBuffer:amp withBufferSize:nOver2];
-(void)updateFFTWithBufferSize:(float)bufferSize withAudioData:(float*)data {
// For an FFT, numSamples must be a power of 2, i.e. is always even
int nOver2 = bufferSize/2;
// Pack samples:
// C(re) -> A[n], C(im) -> A[n+1]
vDSP_ctoz((COMPLEX*)data, 2, &_A, 1, nOver2);
// Perform a forward FFT using fftSetup and A
// Results are returned in A
vDSP_fft_zrip(_FFTSetup, &_A, 1, _log2n, FFT_FORWARD);
// Convert COMPLEX_SPLIT A result to magnitudes
float amp[nOver2];
float maxMag = 0;
for(int i=0; i<nOver2; i++) {
// Calculate the magnitude
float mag = _A.realp[i]*_A.realp[i]+_A.imagp[i]*_A.imagp[i];
maxMag = mag > maxMag ? mag : maxMag;
}
for(int i=0; i<nOver2; i++) {
// Calculate the magnitude
float mag = _A.realp[i]*_A.realp[i]+_A.imagp[i]*_A.imagp[i];
// Bind the value to be less than 1.0 to fit in the graph
amp[i] = [EZAudio MAP:mag leftMin:0.0 leftMax:maxMag rightMin:0.0 rightMax:1.0];
}
// Update the frequency domain plot
[self.audioPlotFreq updateBuffer:amp
withBufferSize:nOver2];
}
//------------------------------------------------------------------------------
#pragma mark - EZMicrophoneDelegate
//------------------------------------------------------------------------------
-(void) microphone:(EZMicrophone *)microphone
hasAudioReceived:(float **)buffer
withBufferSize:(UInt32)bufferSize
withNumberOfChannels:(UInt32)numberOfChannels
{
__weak typeof (self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
//
// Update time domain plot
//
[weakSelf.audioPlotTime updateBuffer:buffer[0]
withBufferSize:bufferSize];
//
// Lazily perform the FFT setup
//
if (!weakSelf.isFFTSetup)
{
[weakSelf createFFTWithBufferSize:bufferSize withAudioData:buffer[0]];
weakSelf.isFFTSetup = YES;
}
withNumberOfChannels:(UInt32)numberOfChannels {
dispatch_async(dispatch_get_main_queue(), ^{
//
// Perform FFT and update FFT graph
//
[weakSelf updateFFTWithBufferSize:bufferSize
withAudioData:buffer[0]];
});
// Update time domain plot
[self.audioPlotTime updateBuffer:buffer[0]
withBufferSize:bufferSize];
// Setup the FFT if it's not already setup
if( !_isFFTSetup ){
[self createFFTWithBufferSize:bufferSize withAudioData:buffer[0]];
_isFFTSetup = YES;
}
// Get the FFT data
[self updateFFTWithBufferSize:bufferSize withAudioData:buffer[0]];
});
}
//------------------------------------------------------------------------------
@end
@end
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="7702" systemVersion="14C109" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="4514" systemVersion="13A603" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment version="1070" identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="7702"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="4514"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="FFTViewController">
@@ -13,15 +13,16 @@
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customObject id="-3" userLabel="Application"/>
<customView id="1">
<rect key="frame" x="0.0" y="0.0" width="480" height="272"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<splitView dividerStyle="thin" translatesAutoresizingMaskIntoConstraints="NO" id="78b-rz-Bpl">
<rect key="frame" x="0.0" y="0.0" width="480" height="272"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<customView fixedFrame="YES" id="Zcc-CT-67u" customClass="EZAudioPlot">
<customView fixedFrame="YES" id="Zcc-CT-67u" customClass="EZAudioPlotGL">
<rect key="frame" x="0.0" y="0.0" width="480" height="132"/>
<autoresizingMask key="autoresizingMask"/>
</customView>
@@ -44,4 +45,4 @@
</constraints>
</customView>
</objects>
</document>
</document>
@@ -5,24 +5,18 @@
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>com.sha.$(PRODUCT_NAME:rfc1034identifier)</string>
<string>com.sha.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2015 Syed Haris Ali. All rights reserved.</string>
<key>NSPrincipalClass</key>
<string></string>
<string>1</string>
</dict>
</plist>
@@ -0,0 +1,34 @@
//
// EZAudioFFTExampleTests.m
// EZAudioFFTExampleTests
//
// Created by Syed Haris Ali on 12/29/13.
// Copyright (c) 2013 Syed Haris Ali. All rights reserved.
//
#import <XCTest/XCTest.h>
@interface EZAudioFFTExampleTests : XCTestCase
@end
@implementation EZAudioFFTExampleTests
- (void)setUp
{
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
}
- (void)tearDown
{
// Put teardown code here. This method is called after the invocation of each test method in the class.
[super tearDown];
}
- (void)testExample
{
XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__);
}
@end
@@ -0,0 +1,2 @@
/* Localized versions of Info.plist keys */
@@ -7,8 +7,6 @@
objects = {
/* Begin PBXBuildFile section */
6611CEAB1B45F6F800AE0EE8 /* EZAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6611CEAA1B45F6F800AE0EE8 /* EZAudio.framework */; };
6611CEAC1B45F6F800AE0EE8 /* EZAudio.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 6611CEAA1B45F6F800AE0EE8 /* EZAudio.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
94056D97185BB0BC00EB94BA /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 94056D96185BB0BC00EB94BA /* Cocoa.framework */; };
94056DA1185BB0BC00EB94BA /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 94056D9F185BB0BC00EB94BA /* InfoPlist.strings */; };
94056DA3185BB0BC00EB94BA /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 94056DA2185BB0BC00EB94BA /* main.m */; };
@@ -16,6 +14,10 @@
94056DAA185BB0BC00EB94BA /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 94056DA9185BB0BC00EB94BA /* AppDelegate.m */; };
94056DAD185BB0BC00EB94BA /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 94056DAB185BB0BC00EB94BA /* MainMenu.xib */; };
94056DAF185BB0BC00EB94BA /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 94056DAE185BB0BC00EB94BA /* Images.xcassets */; };
94056DB6185BB0BC00EB94BA /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 94056DB5185BB0BC00EB94BA /* XCTest.framework */; };
94056DB7185BB0BC00EB94BA /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 94056D96185BB0BC00EB94BA /* Cocoa.framework */; };
94056DBF185BB0BC00EB94BA /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 94056DBD185BB0BC00EB94BA /* InfoPlist.strings */; };
94056DC1185BB0BC00EB94BA /* EZAudioOpenGLWaveformExampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 94056DC0185BB0BC00EB94BA /* EZAudioOpenGLWaveformExampleTests.m */; };
94056DCD185BB0D600EB94BA /* OpenGLWaveformViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 94056DCB185BB0D600EB94BA /* OpenGLWaveformViewController.m */; };
94056DCE185BB0D600EB94BA /* OpenGLWaveformViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 94056DCC185BB0D600EB94BA /* OpenGLWaveformViewController.xib */; };
94056DD0185BB0E200EB94BA /* GLKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 94056DCF185BB0E200EB94BA /* GLKit.framework */; };
@@ -24,24 +26,32 @@
94056DD8185BB0F400EB94BA /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 94056DD5185BB0F400EB94BA /* AudioToolbox.framework */; };
94056DD9185BB0F400EB94BA /* AudioUnit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 94056DD6185BB0F400EB94BA /* AudioUnit.framework */; };
94056DDA185BB0F400EB94BA /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 94056DD7185BB0F400EB94BA /* CoreAudio.framework */; };
9417A7171867DD2800D9D37B /* AEFloatConverter.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A6FF1867DD2800D9D37B /* AEFloatConverter.m */; };
9417A7181867DD2800D9D37B /* EZAudio.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A7011867DD2800D9D37B /* EZAudio.m */; };
9417A7191867DD2800D9D37B /* EZAudioFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A7031867DD2800D9D37B /* EZAudioFile.m */; };
9417A71A1867DD2800D9D37B /* EZAudioPlot.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A7051867DD2800D9D37B /* EZAudioPlot.m */; };
9417A71B1867DD2800D9D37B /* EZAudioPlotGL.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A7071867DD2800D9D37B /* EZAudioPlotGL.m */; };
9417A71C1867DD2800D9D37B /* EZAudioPlotGLKViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A7091867DD2800D9D37B /* EZAudioPlotGLKViewController.m */; };
9417A71D1867DD2800D9D37B /* EZMicrophone.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A70B1867DD2800D9D37B /* EZMicrophone.m */; };
9417A71E1867DD2800D9D37B /* EZOutput.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A70D1867DD2800D9D37B /* EZOutput.m */; };
9417A71F1867DD2800D9D37B /* EZPlot.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A70F1867DD2800D9D37B /* EZPlot.m */; };
9417A7201867DD2800D9D37B /* EZRecorder.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A7111867DD2800D9D37B /* EZRecorder.m */; };
9417A7211867DD2800D9D37B /* TPCircularBuffer.c in Sources */ = {isa = PBXBuildFile; fileRef = 9417A7121867DD2800D9D37B /* TPCircularBuffer.c */; };
9417A7221867DD2800D9D37B /* CHANGELOG in Resources */ = {isa = PBXBuildFile; fileRef = 9417A7151867DD2800D9D37B /* CHANGELOG */; };
9417A7231867DD2800D9D37B /* VERSION in Resources */ = {isa = PBXBuildFile; fileRef = 9417A7161867DD2800D9D37B /* VERSION */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
662428E41B451AEE0069FFD7 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
6611CEAC1B45F6F800AE0EE8 /* EZAudio.framework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
/* Begin PBXContainerItemProxy section */
94056DB8185BB0BC00EB94BA /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 94056D8B185BB0BC00EB94BA /* Project object */;
proxyType = 1;
remoteGlobalIDString = 94056D92185BB0BC00EB94BA;
remoteInfo = EZAudioOpenGLWaveformExample;
};
/* End PBXCopyFilesBuildPhase section */
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
6611CEAA1B45F6F800AE0EE8 /* EZAudio.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; name = EZAudio.framework; path = /Users/haris/Documents/code/openSource/EZAudio/EZAudioExamples/OSX/Build/Products/Debug/EZAudio.framework; sourceTree = "<absolute>"; };
94056D93185BB0BC00EB94BA /* EZAudioOpenGLWaveformExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = EZAudioOpenGLWaveformExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
94056D96185BB0BC00EB94BA /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
94056D99185BB0BC00EB94BA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
@@ -56,7 +66,11 @@
94056DA9185BB0BC00EB94BA /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = AppDelegate.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
94056DAC185BB0BC00EB94BA /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
94056DAE185BB0BC00EB94BA /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
94056DB4185BB0BC00EB94BA /* EZAudioOpenGLWaveformExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = EZAudioOpenGLWaveformExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
94056DB5185BB0BC00EB94BA /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
94056DBC185BB0BC00EB94BA /* EZAudioOpenGLWaveformExampleTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "EZAudioOpenGLWaveformExampleTests-Info.plist"; sourceTree = "<group>"; };
94056DBE185BB0BC00EB94BA /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
94056DC0185BB0BC00EB94BA /* EZAudioOpenGLWaveformExampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = EZAudioOpenGLWaveformExampleTests.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
94056DCA185BB0D600EB94BA /* OpenGLWaveformViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = OpenGLWaveformViewController.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
94056DCB185BB0D600EB94BA /* OpenGLWaveformViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = OpenGLWaveformViewController.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
94056DCC185BB0D600EB94BA /* OpenGLWaveformViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = OpenGLWaveformViewController.xib; sourceTree = "<group>"; };
@@ -66,6 +80,30 @@
94056DD5185BB0F400EB94BA /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
94056DD6185BB0F400EB94BA /* AudioUnit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioUnit.framework; path = System/Library/Frameworks/AudioUnit.framework; sourceTree = SDKROOT; };
94056DD7185BB0F400EB94BA /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; };
9417A6FE1867DD2800D9D37B /* AEFloatConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEFloatConverter.h; sourceTree = "<group>"; };
9417A6FF1867DD2800D9D37B /* AEFloatConverter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AEFloatConverter.m; sourceTree = "<group>"; };
9417A7001867DD2800D9D37B /* EZAudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudio.h; sourceTree = "<group>"; };
9417A7011867DD2800D9D37B /* EZAudio.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudio.m; sourceTree = "<group>"; };
9417A7021867DD2800D9D37B /* EZAudioFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioFile.h; sourceTree = "<group>"; };
9417A7031867DD2800D9D37B /* EZAudioFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioFile.m; sourceTree = "<group>"; };
9417A7041867DD2800D9D37B /* EZAudioPlot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioPlot.h; sourceTree = "<group>"; };
9417A7051867DD2800D9D37B /* EZAudioPlot.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioPlot.m; sourceTree = "<group>"; };
9417A7061867DD2800D9D37B /* EZAudioPlotGL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioPlotGL.h; sourceTree = "<group>"; };
9417A7071867DD2800D9D37B /* EZAudioPlotGL.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioPlotGL.m; sourceTree = "<group>"; };
9417A7081867DD2800D9D37B /* EZAudioPlotGLKViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioPlotGLKViewController.h; sourceTree = "<group>"; };
9417A7091867DD2800D9D37B /* EZAudioPlotGLKViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioPlotGLKViewController.m; sourceTree = "<group>"; };
9417A70A1867DD2800D9D37B /* EZMicrophone.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZMicrophone.h; sourceTree = "<group>"; };
9417A70B1867DD2800D9D37B /* EZMicrophone.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZMicrophone.m; sourceTree = "<group>"; };
9417A70C1867DD2800D9D37B /* EZOutput.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZOutput.h; sourceTree = "<group>"; };
9417A70D1867DD2800D9D37B /* EZOutput.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZOutput.m; sourceTree = "<group>"; };
9417A70E1867DD2800D9D37B /* EZPlot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZPlot.h; sourceTree = "<group>"; };
9417A70F1867DD2800D9D37B /* EZPlot.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZPlot.m; sourceTree = "<group>"; };
9417A7101867DD2800D9D37B /* EZRecorder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZRecorder.h; sourceTree = "<group>"; };
9417A7111867DD2800D9D37B /* EZRecorder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZRecorder.m; sourceTree = "<group>"; };
9417A7121867DD2800D9D37B /* TPCircularBuffer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = TPCircularBuffer.c; sourceTree = "<group>"; };
9417A7131867DD2800D9D37B /* TPCircularBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPCircularBuffer.h; sourceTree = "<group>"; };
9417A7151867DD2800D9D37B /* CHANGELOG */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CHANGELOG; sourceTree = "<group>"; };
9417A7161867DD2800D9D37B /* VERSION */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = VERSION; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -79,19 +117,27 @@
94056DD4185BB0EF00EB94BA /* QuartzCore.framework in Frameworks */,
94056DD2185BB0E900EB94BA /* OpenGL.framework in Frameworks */,
94056DD0185BB0E200EB94BA /* GLKit.framework in Frameworks */,
6611CEAB1B45F6F800AE0EE8 /* EZAudio.framework in Frameworks */,
94056D97185BB0BC00EB94BA /* Cocoa.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
94056DB1185BB0BC00EB94BA /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
94056DB7185BB0BC00EB94BA /* Cocoa.framework in Frameworks */,
94056DB6185BB0BC00EB94BA /* XCTest.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
94056D8A185BB0BC00EB94BA = {
isa = PBXGroup;
children = (
6611CEAA1B45F6F800AE0EE8 /* EZAudio.framework */,
94056D9C185BB0BC00EB94BA /* EZAudioOpenGLWaveformExample */,
94056DBA185BB0BC00EB94BA /* EZAudioOpenGLWaveformExampleTests */,
94056D95185BB0BC00EB94BA /* Frameworks */,
94056D94185BB0BC00EB94BA /* Products */,
);
@@ -101,6 +147,7 @@
isa = PBXGroup;
children = (
94056D93185BB0BC00EB94BA /* EZAudioOpenGLWaveformExample.app */,
94056DB4185BB0BC00EB94BA /* EZAudioOpenGLWaveformExampleTests.xctest */,
);
name = Products;
sourceTree = "<group>";
@@ -134,6 +181,7 @@
94056D9C185BB0BC00EB94BA /* EZAudioOpenGLWaveformExample */ = {
isa = PBXGroup;
children = (
9417A6FD1867DD2800D9D37B /* EZAudio */,
94056DA8185BB0BC00EB94BA /* AppDelegate.h */,
94056DA9185BB0BC00EB94BA /* AppDelegate.m */,
94056DCA185BB0D600EB94BA /* OpenGLWaveformViewController.h */,
@@ -158,6 +206,64 @@
name = "Supporting Files";
sourceTree = "<group>";
};
94056DBA185BB0BC00EB94BA /* EZAudioOpenGLWaveformExampleTests */ = {
isa = PBXGroup;
children = (
94056DC0185BB0BC00EB94BA /* EZAudioOpenGLWaveformExampleTests.m */,
94056DBB185BB0BC00EB94BA /* Supporting Files */,
);
path = EZAudioOpenGLWaveformExampleTests;
sourceTree = "<group>";
};
94056DBB185BB0BC00EB94BA /* Supporting Files */ = {
isa = PBXGroup;
children = (
94056DBC185BB0BC00EB94BA /* EZAudioOpenGLWaveformExampleTests-Info.plist */,
94056DBD185BB0BC00EB94BA /* InfoPlist.strings */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
9417A6FD1867DD2800D9D37B /* EZAudio */ = {
isa = PBXGroup;
children = (
9417A6FE1867DD2800D9D37B /* AEFloatConverter.h */,
9417A6FF1867DD2800D9D37B /* AEFloatConverter.m */,
9417A7001867DD2800D9D37B /* EZAudio.h */,
9417A7011867DD2800D9D37B /* EZAudio.m */,
9417A7021867DD2800D9D37B /* EZAudioFile.h */,
9417A7031867DD2800D9D37B /* EZAudioFile.m */,
9417A7041867DD2800D9D37B /* EZAudioPlot.h */,
9417A7051867DD2800D9D37B /* EZAudioPlot.m */,
9417A7061867DD2800D9D37B /* EZAudioPlotGL.h */,
9417A7071867DD2800D9D37B /* EZAudioPlotGL.m */,
9417A7081867DD2800D9D37B /* EZAudioPlotGLKViewController.h */,
9417A7091867DD2800D9D37B /* EZAudioPlotGLKViewController.m */,
9417A70A1867DD2800D9D37B /* EZMicrophone.h */,
9417A70B1867DD2800D9D37B /* EZMicrophone.m */,
9417A70C1867DD2800D9D37B /* EZOutput.h */,
9417A70D1867DD2800D9D37B /* EZOutput.m */,
9417A70E1867DD2800D9D37B /* EZPlot.h */,
9417A70F1867DD2800D9D37B /* EZPlot.m */,
9417A7101867DD2800D9D37B /* EZRecorder.h */,
9417A7111867DD2800D9D37B /* EZRecorder.m */,
9417A7121867DD2800D9D37B /* TPCircularBuffer.c */,
9417A7131867DD2800D9D37B /* TPCircularBuffer.h */,
9417A7141867DD2800D9D37B /* VERSION */,
);
name = EZAudio;
path = ../../../../EZAudio;
sourceTree = "<group>";
};
9417A7141867DD2800D9D37B /* VERSION */ = {
isa = PBXGroup;
children = (
9417A7151867DD2800D9D37B /* CHANGELOG */,
9417A7161867DD2800D9D37B /* VERSION */,
);
path = VERSION;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -168,7 +274,6 @@
94056D8F185BB0BC00EB94BA /* Sources */,
94056D90185BB0BC00EB94BA /* Frameworks */,
94056D91185BB0BC00EB94BA /* Resources */,
662428E41B451AEE0069FFD7 /* Embed Frameworks */,
);
buildRules = (
);
@@ -179,6 +284,24 @@
productReference = 94056D93185BB0BC00EB94BA /* EZAudioOpenGLWaveformExample.app */;
productType = "com.apple.product-type.application";
};
94056DB3185BB0BC00EB94BA /* EZAudioOpenGLWaveformExampleTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 94056DC7185BB0BC00EB94BA /* Build configuration list for PBXNativeTarget "EZAudioOpenGLWaveformExampleTests" */;
buildPhases = (
94056DB0185BB0BC00EB94BA /* Sources */,
94056DB1185BB0BC00EB94BA /* Frameworks */,
94056DB2185BB0BC00EB94BA /* Resources */,
);
buildRules = (
);
dependencies = (
94056DB9185BB0BC00EB94BA /* PBXTargetDependency */,
);
name = EZAudioOpenGLWaveformExampleTests;
productName = EZAudioOpenGLWaveformExampleTests;
productReference = 94056DB4185BB0BC00EB94BA /* EZAudioOpenGLWaveformExampleTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
@@ -187,6 +310,11 @@
attributes = {
LastUpgradeCheck = 0500;
ORGANIZATIONNAME = "Syed Haris Ali";
TargetAttributes = {
94056DB3185BB0BC00EB94BA = {
TestTargetID = 94056D92185BB0BC00EB94BA;
};
};
};
buildConfigurationList = 94056D8E185BB0BC00EB94BA /* Build configuration list for PBXProject "EZAudioOpenGLWaveformExample" */;
compatibilityVersion = "Xcode 3.2";
@@ -202,6 +330,7 @@
projectRoot = "";
targets = (
94056D92185BB0BC00EB94BA /* EZAudioOpenGLWaveformExample */,
94056DB3185BB0BC00EB94BA /* EZAudioOpenGLWaveformExampleTests */,
);
};
/* End PBXProject section */
@@ -212,10 +341,20 @@
buildActionMask = 2147483647;
files = (
94056DA1185BB0BC00EB94BA /* InfoPlist.strings in Resources */,
9417A7231867DD2800D9D37B /* VERSION in Resources */,
94056DAF185BB0BC00EB94BA /* Images.xcassets in Resources */,
94056DA7185BB0BC00EB94BA /* Credits.rtf in Resources */,
94056DCE185BB0D600EB94BA /* OpenGLWaveformViewController.xib in Resources */,
94056DAD185BB0BC00EB94BA /* MainMenu.xib in Resources */,
9417A7221867DD2800D9D37B /* CHANGELOG in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
94056DB2185BB0BC00EB94BA /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
94056DBF185BB0BC00EB94BA /* InfoPlist.strings in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -226,14 +365,41 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9417A7211867DD2800D9D37B /* TPCircularBuffer.c in Sources */,
9417A71E1867DD2800D9D37B /* EZOutput.m in Sources */,
9417A71C1867DD2800D9D37B /* EZAudioPlotGLKViewController.m in Sources */,
94056DAA185BB0BC00EB94BA /* AppDelegate.m in Sources */,
9417A7171867DD2800D9D37B /* AEFloatConverter.m in Sources */,
94056DA3185BB0BC00EB94BA /* main.m in Sources */,
9417A71A1867DD2800D9D37B /* EZAudioPlot.m in Sources */,
9417A7191867DD2800D9D37B /* EZAudioFile.m in Sources */,
9417A71B1867DD2800D9D37B /* EZAudioPlotGL.m in Sources */,
94056DCD185BB0D600EB94BA /* OpenGLWaveformViewController.m in Sources */,
9417A71F1867DD2800D9D37B /* EZPlot.m in Sources */,
9417A71D1867DD2800D9D37B /* EZMicrophone.m in Sources */,
9417A7201867DD2800D9D37B /* EZRecorder.m in Sources */,
9417A7181867DD2800D9D37B /* EZAudio.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
94056DB0185BB0BC00EB94BA /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
94056DC1185BB0BC00EB94BA /* EZAudioOpenGLWaveformExampleTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
94056DB9185BB0BC00EB94BA /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 94056D92185BB0BC00EB94BA /* EZAudioOpenGLWaveformExample */;
targetProxy = 94056DB8185BB0BC00EB94BA /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
94056D9F185BB0BC00EB94BA /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
@@ -259,6 +425,14 @@
name = MainMenu.xib;
sourceTree = "<group>";
};
94056DBD185BB0BC00EB94BA /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
94056DBE185BB0BC00EB94BA /* en */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
@@ -335,14 +509,9 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
COMBINE_HIDPI_IMAGES = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
/Users/haris/Documents/code/openSource/EZAudio/EZAudioExamples/OSX/Build/Products/Debug,
);
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "EZAudioOpenGLWaveformExample/EZAudioOpenGLWaveformExample-Prefix.pch";
INFOPLIST_FILE = "EZAudioOpenGLWaveformExample/EZAudioOpenGLWaveformExample-Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
@@ -353,19 +522,54 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
COMBINE_HIDPI_IMAGES = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
/Users/haris/Documents/code/openSource/EZAudio/EZAudioExamples/OSX/Build/Products/Debug,
);
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "EZAudioOpenGLWaveformExample/EZAudioOpenGLWaveformExample-Prefix.pch";
INFOPLIST_FILE = "EZAudioOpenGLWaveformExample/EZAudioOpenGLWaveformExample-Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
name = Release;
};
94056DC8185BB0BC00EB94BA /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/EZAudioOpenGLWaveformExample.app/Contents/MacOS/EZAudioOpenGLWaveformExample";
COMBINE_HIDPI_IMAGES = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(DEVELOPER_FRAMEWORKS_DIR)",
"$(inherited)",
);
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "EZAudioOpenGLWaveformExample/EZAudioOpenGLWaveformExample-Prefix.pch";
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
INFOPLIST_FILE = "EZAudioOpenGLWaveformExampleTests/EZAudioOpenGLWaveformExampleTests-Info.plist";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUNDLE_LOADER)";
WRAPPER_EXTENSION = xctest;
};
name = Debug;
};
94056DC9185BB0BC00EB94BA /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/EZAudioOpenGLWaveformExample.app/Contents/MacOS/EZAudioOpenGLWaveformExample";
COMBINE_HIDPI_IMAGES = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(DEVELOPER_FRAMEWORKS_DIR)",
"$(inherited)",
);
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "EZAudioOpenGLWaveformExample/EZAudioOpenGLWaveformExample-Prefix.pch";
INFOPLIST_FILE = "EZAudioOpenGLWaveformExampleTests/EZAudioOpenGLWaveformExampleTests-Info.plist";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUNDLE_LOADER)";
WRAPPER_EXTENSION = xctest;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
@@ -387,6 +591,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
94056DC7185BB0BC00EB94BA /* Build configuration list for PBXNativeTarget "EZAudioOpenGLWaveformExampleTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
94056DC8185BB0BC00EB94BA /* Debug */,
94056DC9185BB0BC00EB94BA /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 94056D8B185BB0BC00EB94BA /* Project object */;
@@ -38,6 +38,7 @@
self.openGLWaveformViewController.view.autoresizingMask = (NSViewWidthSizable|NSViewHeightSizable);
// Add in the core graphics view controller as subview
[self.window.contentView addSubview:self.openGLWaveformViewController.view];
}
@end
@@ -26,45 +26,25 @@
#import <Cocoa/Cocoa.h>
// Import EZAudio header
#import <EZAudio/EZAudio.h>
//------------------------------------------------------------------------------
#pragma mark - OpenGLWaveformViewController
//------------------------------------------------------------------------------
#import "EZAudio.h"
/**
We will allow this view controller to act as an EZMicrophoneDelegate. This is how we listen for the microphone callback.
*/
@interface OpenGLWaveformViewController : NSViewController <EZMicrophoneDelegate>
//------------------------------------------------------------------------------
#pragma mark - Components
//------------------------------------------------------------------------------
/**
The OpenGL based audio plot
*/
@property (nonatomic, weak) IBOutlet EZAudioPlotGL *audioPlot;
@property (nonatomic,weak) IBOutlet EZAudioPlotGL *audioPlot;
/**
The microphone component
*/
@property (nonatomic, strong) EZMicrophone *microphone;
@property (nonatomic,strong) EZMicrophone *microphone;
/**
The microphone pop up button (contains the menu for choosing a microphone input)
*/
@property (nonatomic, weak) IBOutlet NSPopUpButton *microphoneInputPopUpButton;
/**
The microphone input channel pop up button (contains the menu for choosing a microphone input channel)
*/
@property (nonatomic, weak) IBOutlet NSPopUpButton *microphoneInputChannelPopUpButton;
//------------------------------------------------------------------------------
#pragma mark - Actions
//------------------------------------------------------------------------------
/**
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)
*/
@@ -25,225 +25,140 @@
#import "OpenGLWaveformViewController.h"
//------------------------------------------------------------------------------
#pragma mark - OpenGLWaveformViewController
//------------------------------------------------------------------------------
@interface OpenGLWaveformViewController ()
@end
@implementation OpenGLWaveformViewController
//------------------------------------------------------------------------------
#pragma mark - Initialization
-(id)init {
self = [super initWithNibName:NSStringFromClass(self.class) bundle:nil];
if(self){
[self initializeViewController];
}
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithNibName:NSStringFromClass(self.class) bundle:nil];
if(self){
[self initializeViewController];
}
return self;
}
-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:NSStringFromClass(self.class) bundle:nil];
if(self){
[self initializeViewController];
}
return self;
}
#pragma mark - Initialize View Controller
-(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];
}
#pragma mark - Customize the Audio Plot
//------------------------------------------------------------------------------
- (void)awakeFromNib
{
//
// Customizing the audio plot's look
//
// Background color
self.audioPlot.backgroundColor = [NSColor colorWithCalibratedRed: 0.569 green: 0.82 blue: 0.478 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;
//
// Create the microphone
//
self.microphone = [EZMicrophone microphoneWithDelegate:self];
//
// Start the microphone
//
[self.microphone startFetchingAudio];
-(void)awakeFromNib {
/*
Customizing the audio plot's look
*/
// Background color
self.audioPlot.backgroundColor = [NSColor colorWithCalibratedRed: 0.569 green: 0.82 blue: 0.478 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;
/*
Start the microphone
*/
[self.microphone startFetchingAudio];
}
//------------------------------------------------------------------------------
#pragma mark - Setup
//------------------------------------------------------------------------------
- (void) reloadMicrophoneInputPopUpButtonMenu
{
NSArray *inputDevices = [EZAudioDevice inputDevices];
NSMenu *menu = [[NSMenu alloc] init];
NSMenuItem *defaultInputMenuItem;
for (EZAudioDevice *device in inputDevices)
{
NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:device.name
action:@selector(changedInput:)
keyEquivalent:@""];
item.representedObject = device;
item.target = self;
[menu addItem:item];
// If this device is the same one the microphone is using then
// we will use this menu item as the currently selected item
// in the microphone input popup button's list of items. For instance,
// if you are connected to an external display by default the external
// display's microphone might be used instead of the mac's built in
// mic.
if ([device isEqual:self.microphone.device])
{
defaultInputMenuItem = item;
}
}
self.microphoneInputPopUpButton.menu = menu;
//
// Set the selected device to the current selection on the
// microphone input popup button
//
[self.microphoneInputPopUpButton selectItem:defaultInputMenuItem];
}
//------------------------------------------------------------------------------
- (void) reloadMicrophoneInputChannelPopUpButtonMenu
{
NSMenu *menu = [[NSMenu alloc] init];
for (int i = 0; i < self.microphone.device.inputChannelCount; i++)
{
NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:@(i).stringValue
action:nil
keyEquivalent:@""];
[menu addItem:item];
}
self.microphoneInputChannelPopUpButton.menu = menu;
[self.microphoneInputChannelPopUpButton selectItemAtIndex:0];
}
//------------------------------------------------------------------------------
#pragma mark - Actions
//------------------------------------------------------------------------------
- (void)changedInput:(id)sender
{
EZAudioDevice *device = [sender representedObject];
[self.microphone setDevice:device];
-(void)changePlotType:(id)sender {
NSInteger selectedSegment = [sender selectedSegment];
switch(selectedSegment){
case 0:
[self drawBufferPlot];
break;
case 1:
[self drawRollingPlot];
break;
default:
break;
}
}
//------------------------------------------------------------------------------
- (void)changePlotType:(id)sender
{
NSInteger selectedSegment = [sender selectedSegment];
switch(selectedSegment)
{
case 0:
[self drawBufferPlot];
break;
case 1:
[self drawRollingPlot];
break;
default:
break;
}
-(void)toggleMicrophone:(id)sender {
switch([sender state]){
case NSOffState:
[self.microphone stopFetchingAudio];
break;
case NSOnState:
[self.microphone startFetchingAudio];
break;
default:
break;
}
}
//------------------------------------------------------------------------------
- (void)toggleMicrophone:(id)sender
{
switch([sender state])
{
case NSOffState:
[self.microphone stopFetchingAudio];
break;
case NSOnState:
[self.microphone startFetchingAudio];
break;
default:
break;
}
}
//------------------------------------------------------------------------------
#pragma mark - Action Extensions
//------------------------------------------------------------------------------
//
// Give the visualization of the current buffer (this is almost exactly the openFrameworks audio input example)
//
- (void)drawBufferPlot
{
self.audioPlot.plotType = EZPlotTypeBuffer;
self.audioPlot.shouldMirror = NO;
self.audioPlot.shouldFill = NO;
/*
Give the visualization of the current buffer (this is almost exactly the openFrameworks audio input eample)
*/
-(void)drawBufferPlot {
// Change the plot type to the buffer plot
self.audioPlot.plotType = EZPlotTypeBuffer;
// Don't mirror over the x-axis
self.audioPlot.shouldMirror = NO;
// Don't fill
self.audioPlot.shouldFill = NO;
}
//------------------------------------------------------------------------------
//
// Give the classic mirrored, rolling waveform look
//
- (void)drawRollingPlot
{
self.audioPlot.plotType = EZPlotTypeRolling;
self.audioPlot.shouldFill = YES;
self.audioPlot.shouldMirror = YES;
/*
Give the classic mirrored, rolling waveform look
*/
-(void)drawRollingPlot {
self.audioPlot.plotType = EZPlotTypeRolling;
self.audioPlot.shouldFill = YES;
self.audioPlot.shouldMirror = YES;
}
//------------------------------------------------------------------------------
#pragma mark - EZMicrophoneDelegate
//------------------------------------------------------------------------------
#warning Thread Safety
// Note that any callback that provides streamed audio data (like streaming microphone input) happens on a separate audio thread that should not be blocked. When we feed audio data into any of the UI components we need to explicity create a GCD block on the main thread to properly get the UI to work.
- (void)microphone:(EZMicrophone *)microphone
hasAudioReceived:(float **)buffer
withBufferSize:(UInt32)bufferSize
withNumberOfChannels:(UInt32)numberOfChannels
{
// See the Thread Safety warning above, but in a nutshell these callbacks happen on a separate audio thread. We wrap any UI updating in a GCD block on the main thread to avoid blocking that audio flow.
__weak typeof(self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(),^{
-(void)microphone:(EZMicrophone *)microphone
hasAudioReceived:(float **)buffer
withBufferSize:(UInt32)bufferSize
withNumberOfChannels:(UInt32)numberOfChannels {
// Getting audio data as an array of float buffer arrays. What does that mean? Because the audio is coming in as a stereo signal the data is split into a left and right channel. So buffer[0] corresponds to the float* data for the left channel while buffer[1] corresponds to the float* data for the right channel.
// See the Thread Safety warning above, but in a nutshell these callbacks happen on a separate audio thread. We wrap any UI updating in a GCD block on the main thread to avoid blocking that audio flow.
dispatch_async(dispatch_get_main_queue(),^{
// All the audio plot needs is the buffer data (float*) and the size. Internally the audio plot will handle all the drawing related code, history management, and freeing its own resources. Hence, one badass line of code gets you a pretty plot :)
NSInteger channel = [weakSelf.microphoneInputChannelPopUpButton indexOfSelectedItem];
[weakSelf.audioPlot updateBuffer:buffer[channel] withBufferSize:bufferSize];
});
[self.audioPlot updateBuffer:buffer[0] withBufferSize:bufferSize];
});
}
//------------------------------------------------------------------------------
- (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
[EZAudioUtilities printASBD:audioStreamBasicDescription];
-(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];
}
//------------------------------------------------------------------------------
- (void)microphone:(EZMicrophone *)microphone
hasBufferList:(AudioBufferList *)bufferList
withBufferSize:(UInt32)bufferSize
withNumberOfChannels:(UInt32)numberOfChannels
{
// Getting audio data as a buffer list that can be directly fed into the EZRecorder or EZOutput. Say whattt...
-(void)microphone:(EZMicrophone *)microphone
hasBufferList:(AudioBufferList *)bufferList
withBufferSize:(UInt32)bufferSize
withNumberOfChannels:(UInt32)numberOfChannels {
// Getting audio data as a buffer list that can be directly fed into the EZRecorder or EZOutput. Say whattt...
}
//------------------------------------------------------------------------------
- (void)microphone:(EZMicrophone *)microphone
changedDevice:(EZAudioDevice *)device
{
dispatch_async(dispatch_get_main_queue(), ^{
//
// Set up the microphone input popup button's items to select
// between different microphone inputs
//
[self reloadMicrophoneInputPopUpButtonMenu];
//
// Set up the microphone input popup button's items to select
// between different microphone input channels
//
[self reloadMicrophoneInputChannelPopUpButtonMenu];
});
}
//------------------------------------------------------------------------------
@end
@@ -1,40 +1,44 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="7706" systemVersion="14E46" 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="7706"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6185.11"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="OpenGLWaveformViewController">
<connections>
<outlet property="audioPlot" destination="foT-nv-032" id="Baw-Le-z98"/>
<outlet property="microphoneInputChannelPopUpButton" destination="Usd-lp-n8s" id="FbQ-hj-24k"/>
<outlet property="microphoneInputPopUpButton" destination="wBG-jf-wVy" id="thS-Ur-IMj"/>
<outlet property="view" destination="hFn-jA-9Se" id="cfu-I4-qhQ"/>
<outlet property="audioPlot" destination="sjT-Ri-IOJ" id="Wf8-PJ-0mY"/>
<outlet property="view" destination="Zy4-iU-8jm" id="Plx-tA-1tK"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customView id="hFn-jA-9Se">
<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>
<button translatesAutoresizingMaskIntoConstraints="NO" id="TbW-ha-PgJ">
<rect key="frame" x="339" y="44" width="123" height="18"/>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="sjT-Ri-IOJ" customClass="EZAudioPlotGL">
<rect key="frame" x="0.0" y="42" width="480" height="230"/>
</customView>
<button translatesAutoresizingMaskIntoConstraints="NO" id="0kM-1N-88d">
<rect key="frame" x="18" y="14" width="119" height="18"/>
<constraints>
<constraint firstAttribute="width" constant="119" id="xPL-Un-c5O"/>
<constraint firstAttribute="height" constant="14" id="Da9-ZF-OaJ"/>
</constraints>
<buttonCell key="cell" type="check" title="Microphone On" bezelStyle="regularSquare" imagePosition="right" state="on" inset="2" id="DaM-FV-gba">
<buttonCell key="cell" type="check" title="Microphone On" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="2YD-1T-DAq">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="toggleMicrophone:" target="-2" id="Zb5-tE-lf2"/>
<action selector="toggleMicrophone:" target="-2" id="jZt-bz-cRP"/>
</connections>
</button>
<segmentedControl verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="UPI-59-15v">
<rect key="frame" x="335" y="15" width="127" height="24"/>
<segmentedCell key="cell" borderStyle="border" alignment="left" style="rounded" trackingMode="selectOne" id="VRd-fb-9nH">
<segmentedControl verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="M6h-53-a8o">
<rect key="frame" x="333" y="11" width="129" height="24"/>
<constraints>
<constraint firstAttribute="height" constant="21" id="a5H-w4-lUa"/>
</constraints>
<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"/>
@@ -42,81 +46,20 @@
</segments>
</segmentedCell>
<connections>
<action selector="changePlotType:" target="-2" id="BMJ-vA-3ce"/>
<action selector="changePlotType:" target="-2" id="gyT-zv-Y88"/>
</connections>
</segmentedControl>
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="wBG-jf-wVy" userLabel="microphoneInputPopUpButton">
<rect key="frame" x="18" y="14" width="180" height="26"/>
<constraints>
<constraint firstAttribute="width" constant="175" id="aMw-9p-rHt"/>
</constraints>
<popUpButtonCell key="cell" type="push" title="Item 1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="cAL-rs-h1w" id="H5k-ea-wV5">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" id="15c-sS-jR2">
<items>
<menuItem title="Item 1" state="on" id="cAL-rs-h1w"/>
<menuItem title="Item 2" id="LTz-i3-N0M"/>
<menuItem title="Item 3" id="twD-3N-rcl"/>
</items>
</menu>
</popUpButtonCell>
</popUpButton>
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Usd-lp-n8s" userLabel="microphoneInputChannelPopUpButton">
<rect key="frame" x="204" y="14" width="79" height="26"/>
<constraints>
<constraint firstAttribute="width" constant="74" id="oDB-ke-aGz"/>
</constraints>
<popUpButtonCell key="cell" type="push" title="1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="Oit-zz-O9w" id="Eyb-QQ-0bg">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" id="IPB-NH-My1">
<items>
<menuItem title="1" state="on" id="Oit-zz-O9w"/>
<menuItem title="Item 2" id="v3y-Nn-80j"/>
<menuItem title="Item 3" id="IHk-VR-fPC"/>
</items>
</menu>
</popUpButtonCell>
</popUpButton>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="AuX-d2-dD9">
<rect key="frame" x="18" y="43" width="36" height="17"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Input" id="Fq3-tX-8aU">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="PMV-Zt-Wg6">
<rect key="frame" x="204" y="43" width="55" height="17"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Channel" id="PTE-g2-mTV">
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="foT-nv-032" customClass="EZAudioPlotGL">
<rect key="frame" x="0.0" y="77" width="480" height="195"/>
</customView>
</subviews>
<constraints>
<constraint firstAttribute="bottom" secondItem="wBG-jf-wVy" secondAttribute="bottom" constant="17" id="39Q-dq-Ifx"/>
<constraint firstAttribute="trailing" secondItem="foT-nv-032" secondAttribute="trailing" id="4QC-HO-oMa"/>
<constraint firstItem="foT-nv-032" firstAttribute="top" secondItem="hFn-jA-9Se" secondAttribute="top" id="5zo-Yc-OKX"/>
<constraint firstItem="UPI-59-15v" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="Usd-lp-n8s" secondAttribute="trailing" constant="11" id="AXw-KY-VEi"/>
<constraint firstItem="Usd-lp-n8s" firstAttribute="top" secondItem="PMV-Zt-Wg6" secondAttribute="bottom" constant="5" id="GZn-x5-gDa"/>
<constraint firstAttribute="trailing" secondItem="UPI-59-15v" secondAttribute="trailing" constant="20" id="Ihi-YB-8cN"/>
<constraint firstItem="AuX-d2-dD9" firstAttribute="leading" secondItem="hFn-jA-9Se" secondAttribute="leading" constant="20" id="RFU-gK-hRd"/>
<constraint firstItem="TbW-ha-PgJ" firstAttribute="top" secondItem="foT-nv-032" secondAttribute="bottom" constant="17" id="aWO-qE-S52"/>
<constraint firstAttribute="bottom" secondItem="UPI-59-15v" secondAttribute="bottom" constant="17" id="biy-0r-WsW"/>
<constraint firstItem="wBG-jf-wVy" firstAttribute="top" secondItem="AuX-d2-dD9" secondAttribute="bottom" constant="5" id="fEE-bx-NVl"/>
<constraint firstItem="UPI-59-15v" firstAttribute="top" secondItem="TbW-ha-PgJ" secondAttribute="bottom" constant="8" id="gMX-Q1-9Ly"/>
<constraint firstAttribute="trailing" secondItem="TbW-ha-PgJ" secondAttribute="trailing" constant="20" id="gek-Wl-IPz"/>
<constraint firstItem="Usd-lp-n8s" firstAttribute="leading" secondItem="wBG-jf-wVy" secondAttribute="trailing" constant="11" id="gqi-4E-1Wf"/>
<constraint firstItem="Usd-lp-n8s" firstAttribute="leading" secondItem="PMV-Zt-Wg6" secondAttribute="leading" id="lT3-RX-en8"/>
<constraint firstItem="foT-nv-032" firstAttribute="leading" secondItem="hFn-jA-9Se" secondAttribute="leading" id="o4A-j4-xiR"/>
<constraint firstItem="wBG-jf-wVy" firstAttribute="leading" secondItem="hFn-jA-9Se" secondAttribute="leading" constant="20" id="uyC-v9-bP1"/>
<constraint firstAttribute="bottom" secondItem="Usd-lp-n8s" secondAttribute="bottom" constant="17" id="yk6-Fm-gXu"/>
<constraint firstAttribute="trailing" secondItem="sjT-Ri-IOJ" secondAttribute="trailing" id="6Up-ZF-7DX"/>
<constraint firstAttribute="trailing" secondItem="M6h-53-a8o" secondAttribute="trailing" constant="20" id="HMG-wO-794"/>
<constraint firstItem="0kM-1N-88d" firstAttribute="top" secondItem="sjT-Ri-IOJ" secondAttribute="bottom" constant="12" id="OKD-YZ-l6b"/>
<constraint firstItem="sjT-Ri-IOJ" firstAttribute="leading" secondItem="Zy4-iU-8jm" secondAttribute="leading" id="cCR-2i-qMC"/>
<constraint firstItem="sjT-Ri-IOJ" firstAttribute="top" secondItem="Zy4-iU-8jm" secondAttribute="top" id="eCi-yv-OES"/>
<constraint firstItem="0kM-1N-88d" firstAttribute="leading" secondItem="Zy4-iU-8jm" secondAttribute="leading" constant="20" id="eGv-dK-bH8"/>
<constraint firstAttribute="bottom" secondItem="M6h-53-a8o" secondAttribute="bottom" constant="13" id="mLO-CB-wrB"/>
<constraint firstItem="M6h-53-a8o" firstAttribute="top" secondItem="sjT-Ri-IOJ" secondAttribute="bottom" constant="8" id="od9-ut-rVh"/>
<constraint firstAttribute="bottom" secondItem="0kM-1N-88d" secondAttribute="bottom" constant="16" id="opv-CP-vhv"/>
</constraints>
</customView>
</objects>
@@ -0,0 +1,22 @@
<?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>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>com.sha.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>
@@ -0,0 +1,51 @@
//
// EZAudioOpenGLWaveformExampleTests.m
// EZAudioOpenGLWaveformExampleTests
//
// Created by Syed Haris Ali on 12/1/13.
// Copyright (c) 2013 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 <XCTest/XCTest.h>
@interface EZAudioOpenGLWaveformExampleTests : XCTestCase
@end
@implementation EZAudioOpenGLWaveformExampleTests
- (void)setUp
{
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
}
- (void)tearDown
{
// Put teardown code here. This method is called after the invocation of each test method in the class.
[super tearDown];
}
- (void)testExample
{
XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__);
}
@end
@@ -0,0 +1,2 @@
/* Localized versions of Info.plist keys */
@@ -7,8 +7,19 @@
objects = {
/* Begin PBXBuildFile section */
6611CEBA1B45F75C00AE0EE8 /* EZAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6611CEB91B45F75C00AE0EE8 /* EZAudio.framework */; };
6611CEBB1B45F75C00AE0EE8 /* EZAudio.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 6611CEB91B45F75C00AE0EE8 /* EZAudio.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
9417A7B31867DD6600D9D37B /* AEFloatConverter.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A79B1867DD6600D9D37B /* AEFloatConverter.m */; };
9417A7B41867DD6600D9D37B /* EZAudio.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A79D1867DD6600D9D37B /* EZAudio.m */; };
9417A7B51867DD6600D9D37B /* EZAudioFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A79F1867DD6600D9D37B /* EZAudioFile.m */; };
9417A7B61867DD6600D9D37B /* EZAudioPlot.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A7A11867DD6600D9D37B /* EZAudioPlot.m */; };
9417A7B71867DD6600D9D37B /* EZAudioPlotGL.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A7A31867DD6600D9D37B /* EZAudioPlotGL.m */; };
9417A7B81867DD6600D9D37B /* EZAudioPlotGLKViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A7A51867DD6600D9D37B /* EZAudioPlotGLKViewController.m */; };
9417A7B91867DD6600D9D37B /* EZMicrophone.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A7A71867DD6600D9D37B /* EZMicrophone.m */; };
9417A7BA1867DD6600D9D37B /* EZOutput.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A7A91867DD6600D9D37B /* EZOutput.m */; };
9417A7BB1867DD6600D9D37B /* EZPlot.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A7AB1867DD6600D9D37B /* EZPlot.m */; };
9417A7BC1867DD6600D9D37B /* EZRecorder.m in Sources */ = {isa = PBXBuildFile; fileRef = 9417A7AD1867DD6600D9D37B /* EZRecorder.m */; };
9417A7BD1867DD6600D9D37B /* TPCircularBuffer.c in Sources */ = {isa = PBXBuildFile; fileRef = 9417A7AE1867DD6600D9D37B /* TPCircularBuffer.c */; };
9417A7BE1867DD6600D9D37B /* CHANGELOG in Resources */ = {isa = PBXBuildFile; fileRef = 9417A7B11867DD6600D9D37B /* CHANGELOG */; };
9417A7BF1867DD6600D9D37B /* VERSION in Resources */ = {isa = PBXBuildFile; fileRef = 9417A7B21867DD6600D9D37B /* VERSION */; };
941D71B81864C457007D52D8 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 941D71B71864C457007D52D8 /* Cocoa.framework */; };
941D71C21864C457007D52D8 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 941D71C01864C457007D52D8 /* InfoPlist.strings */; };
941D71C41864C457007D52D8 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 941D71C31864C457007D52D8 /* main.m */; };
@@ -16,6 +27,10 @@
941D71CB1864C457007D52D8 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 941D71CA1864C457007D52D8 /* AppDelegate.m */; };
941D71CE1864C457007D52D8 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 941D71CC1864C457007D52D8 /* MainMenu.xib */; };
941D71D01864C457007D52D8 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 941D71CF1864C457007D52D8 /* Images.xcassets */; };
941D71D71864C457007D52D8 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 941D71D61864C457007D52D8 /* XCTest.framework */; };
941D71D81864C457007D52D8 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 941D71B71864C457007D52D8 /* Cocoa.framework */; };
941D71E01864C457007D52D8 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 941D71DE1864C457007D52D8 /* InfoPlist.strings */; };
941D71E21864C457007D52D8 /* EZAudioPassThroughExampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 941D71E11864C457007D52D8 /* EZAudioPassThroughExampleTests.m */; };
941D72151864C4A0007D52D8 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 941D72121864C4A0007D52D8 /* AudioToolbox.framework */; };
941D72161864C4A0007D52D8 /* AudioUnit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 941D72131864C4A0007D52D8 /* AudioUnit.framework */; };
941D72171864C4A0007D52D8 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 941D72141864C4A0007D52D8 /* CoreAudio.framework */; };
@@ -26,22 +41,41 @@
941D72221864C4D7007D52D8 /* PassThroughViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 941D72201864C4D7007D52D8 /* PassThroughViewController.xib */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
6611CEBC1B45F75C00AE0EE8 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
6611CEBB1B45F75C00AE0EE8 /* EZAudio.framework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
/* Begin PBXContainerItemProxy section */
941D71D91864C457007D52D8 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 941D71AC1864C456007D52D8 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 941D71B31864C457007D52D8;
remoteInfo = EZAudioPassThroughExample;
};
/* End PBXCopyFilesBuildPhase section */
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
6611CEB91B45F75C00AE0EE8 /* EZAudio.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; name = EZAudio.framework; path = /Users/haris/Documents/code/openSource/EZAudio/EZAudioExamples/OSX/Build/Products/Debug/EZAudio.framework; sourceTree = "<absolute>"; };
9417A79A1867DD6600D9D37B /* AEFloatConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEFloatConverter.h; sourceTree = "<group>"; };
9417A79B1867DD6600D9D37B /* AEFloatConverter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AEFloatConverter.m; sourceTree = "<group>"; };
9417A79C1867DD6600D9D37B /* EZAudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudio.h; sourceTree = "<group>"; };
9417A79D1867DD6600D9D37B /* EZAudio.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudio.m; sourceTree = "<group>"; };
9417A79E1867DD6600D9D37B /* EZAudioFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioFile.h; sourceTree = "<group>"; };
9417A79F1867DD6600D9D37B /* EZAudioFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioFile.m; sourceTree = "<group>"; };
9417A7A01867DD6600D9D37B /* EZAudioPlot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioPlot.h; sourceTree = "<group>"; };
9417A7A11867DD6600D9D37B /* EZAudioPlot.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioPlot.m; sourceTree = "<group>"; };
9417A7A21867DD6600D9D37B /* EZAudioPlotGL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioPlotGL.h; sourceTree = "<group>"; };
9417A7A31867DD6600D9D37B /* EZAudioPlotGL.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioPlotGL.m; sourceTree = "<group>"; };
9417A7A41867DD6600D9D37B /* EZAudioPlotGLKViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioPlotGLKViewController.h; sourceTree = "<group>"; };
9417A7A51867DD6600D9D37B /* EZAudioPlotGLKViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioPlotGLKViewController.m; sourceTree = "<group>"; };
9417A7A61867DD6600D9D37B /* EZMicrophone.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZMicrophone.h; sourceTree = "<group>"; };
9417A7A71867DD6600D9D37B /* EZMicrophone.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZMicrophone.m; sourceTree = "<group>"; };
9417A7A81867DD6600D9D37B /* EZOutput.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZOutput.h; sourceTree = "<group>"; };
9417A7A91867DD6600D9D37B /* EZOutput.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZOutput.m; sourceTree = "<group>"; };
9417A7AA1867DD6600D9D37B /* EZPlot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZPlot.h; sourceTree = "<group>"; };
9417A7AB1867DD6600D9D37B /* EZPlot.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZPlot.m; sourceTree = "<group>"; };
9417A7AC1867DD6600D9D37B /* EZRecorder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZRecorder.h; sourceTree = "<group>"; };
9417A7AD1867DD6600D9D37B /* EZRecorder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZRecorder.m; sourceTree = "<group>"; };
9417A7AE1867DD6600D9D37B /* TPCircularBuffer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = TPCircularBuffer.c; sourceTree = "<group>"; };
9417A7AF1867DD6600D9D37B /* TPCircularBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPCircularBuffer.h; sourceTree = "<group>"; };
9417A7B11867DD6600D9D37B /* CHANGELOG */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CHANGELOG; sourceTree = "<group>"; };
9417A7B21867DD6600D9D37B /* VERSION */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = VERSION; sourceTree = "<group>"; };
941D71B41864C457007D52D8 /* EZAudioPassThroughExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = EZAudioPassThroughExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
941D71B71864C457007D52D8 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
941D71BA1864C457007D52D8 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
@@ -56,7 +90,11 @@
941D71CA1864C457007D52D8 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
941D71CD1864C457007D52D8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
941D71CF1864C457007D52D8 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
941D71D51864C457007D52D8 /* EZAudioPassThroughExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = EZAudioPassThroughExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
941D71D61864C457007D52D8 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
941D71DD1864C457007D52D8 /* EZAudioPassThroughExampleTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "EZAudioPassThroughExampleTests-Info.plist"; sourceTree = "<group>"; };
941D71DF1864C457007D52D8 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
941D71E11864C457007D52D8 /* EZAudioPassThroughExampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EZAudioPassThroughExampleTests.m; sourceTree = "<group>"; };
941D72121864C4A0007D52D8 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
941D72131864C4A0007D52D8 /* AudioUnit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioUnit.framework; path = System/Library/Frameworks/AudioUnit.framework; sourceTree = SDKROOT; };
941D72141864C4A0007D52D8 /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; };
@@ -79,19 +117,67 @@
941D72151864C4A0007D52D8 /* AudioToolbox.framework in Frameworks */,
941D72161864C4A0007D52D8 /* AudioUnit.framework in Frameworks */,
941D72171864C4A0007D52D8 /* CoreAudio.framework in Frameworks */,
6611CEBA1B45F75C00AE0EE8 /* EZAudio.framework in Frameworks */,
941D71B81864C457007D52D8 /* Cocoa.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
941D71D21864C457007D52D8 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
941D71D81864C457007D52D8 /* Cocoa.framework in Frameworks */,
941D71D71864C457007D52D8 /* XCTest.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
9417A7991867DD6600D9D37B /* EZAudio */ = {
isa = PBXGroup;
children = (
9417A79A1867DD6600D9D37B /* AEFloatConverter.h */,
9417A79B1867DD6600D9D37B /* AEFloatConverter.m */,
9417A79C1867DD6600D9D37B /* EZAudio.h */,
9417A79D1867DD6600D9D37B /* EZAudio.m */,
9417A79E1867DD6600D9D37B /* EZAudioFile.h */,
9417A79F1867DD6600D9D37B /* EZAudioFile.m */,
9417A7A01867DD6600D9D37B /* EZAudioPlot.h */,
9417A7A11867DD6600D9D37B /* EZAudioPlot.m */,
9417A7A21867DD6600D9D37B /* EZAudioPlotGL.h */,
9417A7A31867DD6600D9D37B /* EZAudioPlotGL.m */,
9417A7A41867DD6600D9D37B /* EZAudioPlotGLKViewController.h */,
9417A7A51867DD6600D9D37B /* EZAudioPlotGLKViewController.m */,
9417A7A61867DD6600D9D37B /* EZMicrophone.h */,
9417A7A71867DD6600D9D37B /* EZMicrophone.m */,
9417A7A81867DD6600D9D37B /* EZOutput.h */,
9417A7A91867DD6600D9D37B /* EZOutput.m */,
9417A7AA1867DD6600D9D37B /* EZPlot.h */,
9417A7AB1867DD6600D9D37B /* EZPlot.m */,
9417A7AC1867DD6600D9D37B /* EZRecorder.h */,
9417A7AD1867DD6600D9D37B /* EZRecorder.m */,
9417A7AE1867DD6600D9D37B /* TPCircularBuffer.c */,
9417A7AF1867DD6600D9D37B /* TPCircularBuffer.h */,
9417A7B01867DD6600D9D37B /* VERSION */,
);
name = EZAudio;
path = ../../../../EZAudio;
sourceTree = "<group>";
};
9417A7B01867DD6600D9D37B /* VERSION */ = {
isa = PBXGroup;
children = (
9417A7B11867DD6600D9D37B /* CHANGELOG */,
9417A7B21867DD6600D9D37B /* VERSION */,
);
path = VERSION;
sourceTree = "<group>";
};
941D71AB1864C456007D52D8 = {
isa = PBXGroup;
children = (
6611CEB91B45F75C00AE0EE8 /* EZAudio.framework */,
941D71BD1864C457007D52D8 /* EZAudioPassThroughExample */,
941D71DB1864C457007D52D8 /* EZAudioPassThroughExampleTests */,
941D71B61864C457007D52D8 /* Frameworks */,
941D71B51864C457007D52D8 /* Products */,
);
@@ -101,6 +187,7 @@
isa = PBXGroup;
children = (
941D71B41864C457007D52D8 /* EZAudioPassThroughExample.app */,
941D71D51864C457007D52D8 /* EZAudioPassThroughExampleTests.xctest */,
);
name = Products;
sourceTree = "<group>";
@@ -134,6 +221,7 @@
941D71BD1864C457007D52D8 /* EZAudioPassThroughExample */ = {
isa = PBXGroup;
children = (
9417A7991867DD6600D9D37B /* EZAudio */,
941D71C91864C457007D52D8 /* AppDelegate.h */,
941D71CA1864C457007D52D8 /* AppDelegate.m */,
941D721E1864C4D7007D52D8 /* PassThroughViewController.h */,
@@ -158,6 +246,24 @@
name = "Supporting Files";
sourceTree = "<group>";
};
941D71DB1864C457007D52D8 /* EZAudioPassThroughExampleTests */ = {
isa = PBXGroup;
children = (
941D71E11864C457007D52D8 /* EZAudioPassThroughExampleTests.m */,
941D71DC1864C457007D52D8 /* Supporting Files */,
);
path = EZAudioPassThroughExampleTests;
sourceTree = "<group>";
};
941D71DC1864C457007D52D8 /* Supporting Files */ = {
isa = PBXGroup;
children = (
941D71DD1864C457007D52D8 /* EZAudioPassThroughExampleTests-Info.plist */,
941D71DE1864C457007D52D8 /* InfoPlist.strings */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -168,7 +274,6 @@
941D71B01864C457007D52D8 /* Sources */,
941D71B11864C457007D52D8 /* Frameworks */,
941D71B21864C457007D52D8 /* Resources */,
6611CEBC1B45F75C00AE0EE8 /* Embed Frameworks */,
);
buildRules = (
);
@@ -179,6 +284,24 @@
productReference = 941D71B41864C457007D52D8 /* EZAudioPassThroughExample.app */;
productType = "com.apple.product-type.application";
};
941D71D41864C457007D52D8 /* EZAudioPassThroughExampleTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 941D71E81864C457007D52D8 /* Build configuration list for PBXNativeTarget "EZAudioPassThroughExampleTests" */;
buildPhases = (
941D71D11864C457007D52D8 /* Sources */,
941D71D21864C457007D52D8 /* Frameworks */,
941D71D31864C457007D52D8 /* Resources */,
);
buildRules = (
);
dependencies = (
941D71DA1864C457007D52D8 /* PBXTargetDependency */,
);
name = EZAudioPassThroughExampleTests;
productName = EZAudioPassThroughExampleTests;
productReference = 941D71D51864C457007D52D8 /* EZAudioPassThroughExampleTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
@@ -187,6 +310,11 @@
attributes = {
LastUpgradeCheck = 0500;
ORGANIZATIONNAME = "Syed Haris Ali";
TargetAttributes = {
941D71D41864C457007D52D8 = {
TestTargetID = 941D71B31864C457007D52D8;
};
};
};
buildConfigurationList = 941D71AF1864C456007D52D8 /* Build configuration list for PBXProject "EZAudioPassThroughExample" */;
compatibilityVersion = "Xcode 3.2";
@@ -202,6 +330,7 @@
projectRoot = "";
targets = (
941D71B31864C457007D52D8 /* EZAudioPassThroughExample */,
941D71D41864C457007D52D8 /* EZAudioPassThroughExampleTests */,
);
};
/* End PBXProject section */
@@ -212,10 +341,20 @@
buildActionMask = 2147483647;
files = (
941D72221864C4D7007D52D8 /* PassThroughViewController.xib in Resources */,
9417A7BF1867DD6600D9D37B /* VERSION in Resources */,
941D71C21864C457007D52D8 /* InfoPlist.strings in Resources */,
941D71D01864C457007D52D8 /* Images.xcassets in Resources */,
941D71C81864C457007D52D8 /* Credits.rtf in Resources */,
941D71CE1864C457007D52D8 /* MainMenu.xib in Resources */,
9417A7BE1867DD6600D9D37B /* CHANGELOG in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
941D71D31864C457007D52D8 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
941D71E01864C457007D52D8 /* InfoPlist.strings in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -226,14 +365,41 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
9417A7BD1867DD6600D9D37B /* TPCircularBuffer.c in Sources */,
9417A7BA1867DD6600D9D37B /* EZOutput.m in Sources */,
9417A7B81867DD6600D9D37B /* EZAudioPlotGLKViewController.m in Sources */,
941D72211864C4D7007D52D8 /* PassThroughViewController.m in Sources */,
9417A7B31867DD6600D9D37B /* AEFloatConverter.m in Sources */,
941D71CB1864C457007D52D8 /* AppDelegate.m in Sources */,
9417A7B61867DD6600D9D37B /* EZAudioPlot.m in Sources */,
9417A7B51867DD6600D9D37B /* EZAudioFile.m in Sources */,
9417A7B71867DD6600D9D37B /* EZAudioPlotGL.m in Sources */,
941D71C41864C457007D52D8 /* main.m in Sources */,
9417A7BB1867DD6600D9D37B /* EZPlot.m in Sources */,
9417A7B91867DD6600D9D37B /* EZMicrophone.m in Sources */,
9417A7BC1867DD6600D9D37B /* EZRecorder.m in Sources */,
9417A7B41867DD6600D9D37B /* EZAudio.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
941D71D11864C457007D52D8 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
941D71E21864C457007D52D8 /* EZAudioPassThroughExampleTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
941D71DA1864C457007D52D8 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 941D71B31864C457007D52D8 /* EZAudioPassThroughExample */;
targetProxy = 941D71D91864C457007D52D8 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
941D71C01864C457007D52D8 /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
@@ -259,6 +425,14 @@
name = MainMenu.xib;
sourceTree = "<group>";
};
941D71DE1864C457007D52D8 /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
941D71DF1864C457007D52D8 /* en */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
@@ -335,14 +509,9 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
COMBINE_HIDPI_IMAGES = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
/Users/haris/Documents/code/openSource/EZAudio/EZAudioExamples/OSX/Build/Products/Debug,
);
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "EZAudioPassThroughExample/EZAudioPassThroughExample-Prefix.pch";
INFOPLIST_FILE = "EZAudioPassThroughExample/EZAudioPassThroughExample-Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
@@ -353,19 +522,54 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
COMBINE_HIDPI_IMAGES = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
/Users/haris/Documents/code/openSource/EZAudio/EZAudioExamples/OSX/Build/Products/Debug,
);
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "EZAudioPassThroughExample/EZAudioPassThroughExample-Prefix.pch";
INFOPLIST_FILE = "EZAudioPassThroughExample/EZAudioPassThroughExample-Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
name = Release;
};
941D71E91864C457007D52D8 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/EZAudioPassThroughExample.app/Contents/MacOS/EZAudioPassThroughExample";
COMBINE_HIDPI_IMAGES = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(DEVELOPER_FRAMEWORKS_DIR)",
"$(inherited)",
);
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "EZAudioPassThroughExample/EZAudioPassThroughExample-Prefix.pch";
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
INFOPLIST_FILE = "EZAudioPassThroughExampleTests/EZAudioPassThroughExampleTests-Info.plist";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUNDLE_LOADER)";
WRAPPER_EXTENSION = xctest;
};
name = Debug;
};
941D71EA1864C457007D52D8 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/EZAudioPassThroughExample.app/Contents/MacOS/EZAudioPassThroughExample";
COMBINE_HIDPI_IMAGES = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(DEVELOPER_FRAMEWORKS_DIR)",
"$(inherited)",
);
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "EZAudioPassThroughExample/EZAudioPassThroughExample-Prefix.pch";
INFOPLIST_FILE = "EZAudioPassThroughExampleTests/EZAudioPassThroughExampleTests-Info.plist";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUNDLE_LOADER)";
WRAPPER_EXTENSION = xctest;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
@@ -387,6 +591,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
941D71E81864C457007D52D8 /* Build configuration list for PBXNativeTarget "EZAudioPassThroughExampleTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
941D71E91864C457007D52D8 /* Debug */,
941D71EA1864C457007D52D8 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 941D71AC1864C456007D52D8 /* Project object */;
@@ -28,35 +28,30 @@
/**
Import EZAudio
*/
#import <EZAudio/EZAudio.h>
#import "EZAudio.h"
@interface PassThroughViewController : NSViewController <EZMicrophoneDelegate>
@interface PassThroughViewController : NSViewController <EZMicrophoneDelegate,EZOutputDataSource>
//------------------------------------------------------------------------------
#pragma mark - Components
//------------------------------------------------------------------------------
/**
The OpenGL based audio plot
*/
@property (nonatomic,weak) IBOutlet EZAudioPlotGL *audioPlot;
//------------------------------------------------------------------------------
#pragma mark - Actions
//------------------------------------------------------------------------------
/**
The Microphone
*/
@property (nonatomic,strong) EZMicrophone *microphone;
#pragma mark - Actions
/**
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)
*/
-(IBAction)changePlotType:(id)sender;
//------------------------------------------------------------------------------
/**
Toggles the microphone on and off. When the microphone is on it will send its delegate (aka this view controller) the audio data in various ways (check out the EZMicrophoneDelegate documentation for more details);
*/
-(IBAction)toggleMicrophone:(id)sender;
//------------------------------------------------------------------------------
@end
@@ -25,125 +25,155 @@
#import "PassThroughViewController.h"
@interface PassThroughViewController (){
TPCircularBuffer _circularBuffer;
}
@end
@implementation PassThroughViewController
@synthesize audioPlot;
@synthesize microphone;
#pragma mark - Initialization
-(id)init {
self = [super initWithNibName:NSStringFromClass(self.class) bundle:nil];
if(self){
[self initializeView];
}
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithNibName:NSStringFromClass(self.class) bundle:nil];
if(self){
[self initializeView];
}
return self;
}
-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:NSStringFromClass(self.class) bundle:nil];
if(self){
[self initializeView];
}
return self;
}
-(void)initializeView {
/**
Initialize the circular buffer
*/
[EZAudio circularBuffer:&_circularBuffer withSize:2048];
}
//------------------------------------------------------------------------------
#pragma mark - Customize the Audio Plot
//------------------------------------------------------------------------------
- (void)awakeFromNib
{
//
// Customizing the audio plot's look
//
// Background color
self.audioPlot.backgroundColor = [NSColor colorWithCalibratedRed: 0.569 green: 0.82 blue: 0.478 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;
//
// Start the microphone
//
[EZMicrophone sharedMicrophone].delegate = self;
[[EZMicrophone sharedMicrophone] startFetchingAudio];
//
// Print out the input device being used
//
NSLog(@"Using input device: %@", [[EZMicrophone sharedMicrophone] device]);
//
// Use the microphone as the EZOutputDataSource
//
[[EZMicrophone sharedMicrophone] setOutput:[EZOutput sharedOutput]];
/**
Start the output
*/
[[EZOutput sharedOutput] startPlayback];
-(void)awakeFromNib {
/*
Customizing the audio plot's look
*/
// Background color
self.audioPlot.backgroundColor = [NSColor colorWithCalibratedRed: 0.569 green: 0.82 blue: 0.478 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;
/*
Start the microphone
*/
[EZMicrophone sharedMicrophone].microphoneDelegate = self;
[[EZMicrophone sharedMicrophone] startFetchingAudio];
/**
Start the output
*/
[[EZOutput sharedOutput] setAudioStreamBasicDescription:[EZMicrophone sharedMicrophone].audioStreamBasicDescription];
[EZOutput sharedOutput].outputDataSource = self;
[[EZOutput sharedOutput] startPlayback];
}
//------------------------------------------------------------------------------
#pragma mark - Actions
//------------------------------------------------------------------------------
- (void)changePlotType:(id)sender
{
NSInteger selectedSegment = [sender selectedSegment];
switch(selectedSegment)
{
case 0:
[self drawBufferPlot];
break;
case 1:
[self drawRollingPlot];
break;
default:
break;
}
-(void)changePlotType:(id)sender {
NSInteger selectedSegment = [sender selectedSegment];
switch(selectedSegment){
case 0:
[self drawBufferPlot];
break;
case 1:
[self drawRollingPlot];
break;
default:
break;
}
}
//------------------------------------------------------------------------------
- (void)toggleMicrophone:(id)sender
{
switch([sender state])
{
case NSOffState:
[[EZMicrophone sharedMicrophone] stopFetchingAudio];
break;
case NSOnState:
[[EZMicrophone sharedMicrophone] startFetchingAudio];
break;
default:
break;
}
-(void)toggleMicrophone:(id)sender {
switch([sender state]){
case NSOffState:
[[EZMicrophone sharedMicrophone] stopFetchingAudio];
break;
case NSOnState:
[[EZMicrophone sharedMicrophone] startFetchingAudio];
break;
default:
break;
}
}
//------------------------------------------------------------------------------
#pragma mark - Action Extensions
//------------------------------------------------------------------------------
//
// Give the visualization of the current buffer (this is almost exactly the openFrameworks audio input example)
//
- (void)drawBufferPlot
{
self.audioPlot.plotType = EZPlotTypeBuffer;
self.audioPlot.shouldMirror = NO;
self.audioPlot.shouldFill = NO;
/*
Give the visualization of the current buffer (this is almost exactly the openFrameworks audio input eample)
*/
-(void)drawBufferPlot {
// Change the plot type to the buffer plot
self.audioPlot.plotType = EZPlotTypeBuffer;
// Don't mirror over the x-axis
self.audioPlot.shouldMirror = NO;
// Don't fill
self.audioPlot.shouldFill = NO;
}
//------------------------------------------------------------------------------
//
// Give the classic mirrored, rolling waveform look
//
- (void)drawRollingPlot
{
self.audioPlot.plotType = EZPlotTypeRolling;
self.audioPlot.shouldFill = YES;
self.audioPlot.shouldMirror = YES;
/*
Give the classic mirrored, rolling waveform look
*/
-(void)drawRollingPlot {
self.audioPlot.plotType = EZPlotTypeRolling;
self.audioPlot.shouldFill = YES;
self.audioPlot.shouldMirror = YES;
}
//------------------------------------------------------------------------------
#pragma mark - EZMicrophoneDelegate
//------------------------------------------------------------------------------
-(void) microphone:(EZMicrophone *)microphone
hasAudioReceived:(float **)buffer
withBufferSize:(UInt32)bufferSize
withNumberOfChannels:(UInt32)numberOfChannels
{
__weak typeof (self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf.audioPlot updateBuffer:buffer[0]
withBufferSize:bufferSize];
});
-(void)microphone:(EZMicrophone *)microphone
hasAudioReceived:(float **)buffer
withBufferSize:(UInt32)bufferSize
withNumberOfChannels:(UInt32)numberOfChannels {
dispatch_async(dispatch_get_main_queue(), ^{
[self.audioPlot updateBuffer:buffer[0] withBufferSize:bufferSize];
});
}
//------------------------------------------------------------------------------
// Append the AudioBufferList from the microphone callback to a global circular buffer
-(void)microphone:(EZMicrophone *)microphone
hasBufferList:(AudioBufferList *)bufferList
withBufferSize:(UInt32)bufferSize
withNumberOfChannels:(UInt32)numberOfChannels {
/**
Append the audio data to a circular buffer
*/
[EZAudio appendDataToCircularBuffer:&_circularBuffer
fromAudioBufferList:bufferList];
}
#pragma mark - EZOutputDataSource
-(TPCircularBuffer *)outputShouldUseCircularBuffer:(EZOutput *)output {
return [EZMicrophone sharedMicrophone].microphoneOn ? &_circularBuffer : nil;
}
#pragma mark - Cleanup
-(void)dealloc {
[EZAudio freeCircularBuffer:&_circularBuffer];
}
@end
@@ -0,0 +1,22 @@
<?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>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>com.sha.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>
@@ -0,0 +1,34 @@
//
// EZAudioPassThroughExampleTests.m
// EZAudioPassThroughExampleTests
//
// Created by Syed Haris Ali on 12/20/13.
// Copyright (c) 2013 Syed Haris Ali. All rights reserved.
//
#import <XCTest/XCTest.h>
@interface EZAudioPassThroughExampleTests : XCTestCase
@end
@implementation EZAudioPassThroughExampleTests
- (void)setUp
{
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
}
- (void)tearDown
{
// Put teardown code here. This method is called after the invocation of each test method in the class.
[super tearDown];
}
- (void)testExample
{
XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__);
}
@end
@@ -0,0 +1,2 @@
/* Localized versions of Info.plist keys */
Binary file not shown.
@@ -7,9 +7,9 @@
objects = {
/* Begin PBXBuildFile section */
6611CEAE1B45F73100AE0EE8 /* EZAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6611CEAD1B45F73100AE0EE8 /* EZAudio.framework */; };
6611CEAF1B45F73100AE0EE8 /* EZAudio.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 6611CEAD1B45F73100AE0EE8 /* EZAudio.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
6628E2351B3A121A00020E56 /* simple-drum-beat.wav in Resources */ = {isa = PBXBuildFile; fileRef = 6628E2341B3A121A00020E56 /* simple-drum-beat.wav */; };
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 */; };
@@ -17,6 +17,10 @@
94056F0E185BD83400EB94BA /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 94056F0D185BD83400EB94BA /* AppDelegate.m */; };
94056F11185BD83400EB94BA /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 94056F0F185BD83400EB94BA /* MainMenu.xib */; };
94056F13185BD83400EB94BA /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 94056F12185BD83400EB94BA /* Images.xcassets */; };
94056F1A185BD83400EB94BA /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 94056F19185BD83400EB94BA /* XCTest.framework */; };
94056F1B185BD83400EB94BA /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 94056EFA185BD83400EB94BA /* Cocoa.framework */; };
94056F23185BD83400EB94BA /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 94056F21185BD83400EB94BA /* InfoPlist.strings */; };
94056F25185BD83400EB94BA /* EZAudioPlayFileExampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 94056F24185BD83400EB94BA /* EZAudioPlayFileExampleTests.m */; };
94056F31185BD86D00EB94BA /* PlayFileViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 94056F2F185BD86D00EB94BA /* PlayFileViewController.m */; };
94056F32185BD86D00EB94BA /* PlayFileViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 94056F30185BD86D00EB94BA /* PlayFileViewController.xib */; };
94056F5D185BDB3500EB94BA /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 94056F5C185BDB3500EB94BA /* OpenGL.framework */; };
@@ -25,25 +29,37 @@
94056F65185BDB4700EB94BA /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 94056F62185BDB4700EB94BA /* AudioToolbox.framework */; };
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 */; };
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 */; };
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 */; };
9417A7481867DD3400D9D37B /* TPCircularBuffer.c in Sources */ = {isa = PBXBuildFile; fileRef = 9417A7391867DD3400D9D37B /* TPCircularBuffer.c */; };
9417A7491867DD3400D9D37B /* CHANGELOG in Resources */ = {isa = PBXBuildFile; fileRef = 9417A73C1867DD3400D9D37B /* CHANGELOG */; };
9417A74A1867DD3400D9D37B /* VERSION in Resources */ = {isa = PBXBuildFile; fileRef = 9417A73D1867DD3400D9D37B /* VERSION */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
6611CEB01B45F73100AE0EE8 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
6611CEAF1B45F73100AE0EE8 /* EZAudio.framework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
/* Begin PBXContainerItemProxy section */
94056F1C185BD83400EB94BA /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 94056EEF185BD83400EB94BA /* Project object */;
proxyType = 1;
remoteGlobalIDString = 94056EF6185BD83400EB94BA;
remoteInfo = EZAudioPlayFileExample;
};
/* End PBXCopyFilesBuildPhase section */
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
6611CEAD1B45F73100AE0EE8 /* EZAudio.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; name = EZAudio.framework; path = /Users/haris/Documents/code/openSource/EZAudio/EZAudioExamples/OSX/Build/Products/Debug/EZAudio.framework; sourceTree = "<absolute>"; };
6628E2341B3A121A00020E56 /* simple-drum-beat.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; name = "simple-drum-beat.wav"; path = "../../../simple-drum-beat.wav"; sourceTree = "<group>"; };
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; };
@@ -58,7 +74,11 @@
94056F0D185BD83400EB94BA /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
94056F10185BD83400EB94BA /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
94056F12185BD83400EB94BA /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
94056F18185BD83400EB94BA /* EZAudioPlayFileExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = EZAudioPlayFileExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
94056F19185BD83400EB94BA /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
94056F20185BD83400EB94BA /* EZAudioPlayFileExampleTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "EZAudioPlayFileExampleTests-Info.plist"; sourceTree = "<group>"; };
94056F22185BD83400EB94BA /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
94056F24185BD83400EB94BA /* EZAudioPlayFileExampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EZAudioPlayFileExampleTests.m; sourceTree = "<group>"; };
94056F2E185BD86D00EB94BA /* PlayFileViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlayFileViewController.h; sourceTree = "<group>"; };
94056F2F185BD86D00EB94BA /* PlayFileViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PlayFileViewController.m; sourceTree = "<group>"; };
94056F30185BD86D00EB94BA /* PlayFileViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PlayFileViewController.xib; sourceTree = "<group>"; };
@@ -68,6 +88,27 @@
94056F62185BDB4700EB94BA /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
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>"; };
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>"; };
9417A72A1867DD3400D9D37B /* EZAudioFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioFile.m; sourceTree = "<group>"; };
9417A72B1867DD3400D9D37B /* EZAudioPlot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioPlot.h; sourceTree = "<group>"; };
9417A72C1867DD3400D9D37B /* EZAudioPlot.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudioPlot.m; sourceTree = "<group>"; };
9417A72D1867DD3400D9D37B /* EZAudioPlotGL.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioPlotGL.h; sourceTree = "<group>"; };
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>"; };
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>"; };
9417A7361867DD3400D9D37B /* EZPlot.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZPlot.m; sourceTree = "<group>"; };
9417A7371867DD3400D9D37B /* EZRecorder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZRecorder.h; sourceTree = "<group>"; };
9417A7381867DD3400D9D37B /* EZRecorder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZRecorder.m; sourceTree = "<group>"; };
9417A7391867DD3400D9D37B /* TPCircularBuffer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = TPCircularBuffer.c; sourceTree = "<group>"; };
9417A73A1867DD3400D9D37B /* TPCircularBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TPCircularBuffer.h; sourceTree = "<group>"; };
9417A73C1867DD3400D9D37B /* CHANGELOG */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CHANGELOG; sourceTree = "<group>"; };
9417A73D1867DD3400D9D37B /* VERSION */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = VERSION; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -81,19 +122,27 @@
94056F61185BDB3F00EB94BA /* QuartzCore.framework in Frameworks */,
94056F5F185BDB3900EB94BA /* GLKit.framework in Frameworks */,
94056F5D185BDB3500EB94BA /* OpenGL.framework in Frameworks */,
6611CEAE1B45F73100AE0EE8 /* EZAudio.framework in Frameworks */,
94056EFB185BD83400EB94BA /* Cocoa.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
94056F15185BD83400EB94BA /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
94056F1B185BD83400EB94BA /* Cocoa.framework in Frameworks */,
94056F1A185BD83400EB94BA /* XCTest.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
94056EEE185BD83400EB94BA = {
isa = PBXGroup;
children = (
6611CEAD1B45F73100AE0EE8 /* EZAudio.framework */,
94056F00185BD83400EB94BA /* EZAudioPlayFileExample */,
94056F1E185BD83400EB94BA /* EZAudioPlayFileExampleTests */,
94056EF9185BD83400EB94BA /* Frameworks */,
94056EF8185BD83400EB94BA /* Products */,
);
@@ -103,6 +152,7 @@
isa = PBXGroup;
children = (
94056EF7185BD83400EB94BA /* EZAudioPlayFileExample.app */,
94056F18185BD83400EB94BA /* EZAudioPlayFileExampleTests.xctest */,
);
name = Products;
sourceTree = "<group>";
@@ -136,13 +186,14 @@
94056F00185BD83400EB94BA /* EZAudioPlayFileExample */ = {
isa = PBXGroup;
children = (
9417A7241867DD3400D9D37B /* EZAudio */,
94056F0C185BD83400EB94BA /* AppDelegate.h */,
94056F0D185BD83400EB94BA /* AppDelegate.m */,
94056F2E185BD86D00EB94BA /* PlayFileViewController.h */,
94056F2F185BD86D00EB94BA /* PlayFileViewController.m */,
94056F30185BD86D00EB94BA /* PlayFileViewController.xib */,
9417A6D41865928C00D9D37B /* simple-drum-beat.wav */,
94056F0F185BD83400EB94BA /* MainMenu.xib */,
6628E2341B3A121A00020E56 /* simple-drum-beat.wav */,
94056F12185BD83400EB94BA /* Images.xcassets */,
94056F01185BD83400EB94BA /* Supporting Files */,
);
@@ -161,6 +212,66 @@
name = "Supporting Files";
sourceTree = "<group>";
};
94056F1E185BD83400EB94BA /* EZAudioPlayFileExampleTests */ = {
isa = PBXGroup;
children = (
94056F24185BD83400EB94BA /* EZAudioPlayFileExampleTests.m */,
94056F1F185BD83400EB94BA /* Supporting Files */,
);
path = EZAudioPlayFileExampleTests;
sourceTree = "<group>";
};
94056F1F185BD83400EB94BA /* Supporting Files */ = {
isa = PBXGroup;
children = (
94056F20185BD83400EB94BA /* EZAudioPlayFileExampleTests-Info.plist */,
94056F21185BD83400EB94BA /* InfoPlist.strings */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
9417A7241867DD3400D9D37B /* EZAudio */ = {
isa = PBXGroup;
children = (
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 */,
9417A72C1867DD3400D9D37B /* EZAudioPlot.m */,
9417A72D1867DD3400D9D37B /* EZAudioPlotGL.h */,
9417A72E1867DD3400D9D37B /* EZAudioPlotGL.m */,
9417A72F1867DD3400D9D37B /* EZAudioPlotGLKViewController.h */,
9417A7301867DD3400D9D37B /* EZAudioPlotGLKViewController.m */,
668E4F861A90607500F4B814 /* EZMicrophone.h */,
668E4F871A90607500F4B814 /* EZMicrophone.m */,
9417A7331867DD3400D9D37B /* EZOutput.h */,
9417A7341867DD3400D9D37B /* EZOutput.m */,
9417A7351867DD3400D9D37B /* EZPlot.h */,
9417A7361867DD3400D9D37B /* EZPlot.m */,
9417A7371867DD3400D9D37B /* EZRecorder.h */,
9417A7381867DD3400D9D37B /* EZRecorder.m */,
9417A7391867DD3400D9D37B /* TPCircularBuffer.c */,
9417A73A1867DD3400D9D37B /* TPCircularBuffer.h */,
9417A73B1867DD3400D9D37B /* VERSION */,
);
name = EZAudio;
path = ../../../../EZAudio;
sourceTree = "<group>";
};
9417A73B1867DD3400D9D37B /* VERSION */ = {
isa = PBXGroup;
children = (
9417A73C1867DD3400D9D37B /* CHANGELOG */,
9417A73D1867DD3400D9D37B /* VERSION */,
);
path = VERSION;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -171,7 +282,6 @@
94056EF3185BD83400EB94BA /* Sources */,
94056EF4185BD83400EB94BA /* Frameworks */,
94056EF5185BD83400EB94BA /* Resources */,
6611CEB01B45F73100AE0EE8 /* Embed Frameworks */,
);
buildRules = (
);
@@ -182,6 +292,24 @@
productReference = 94056EF7185BD83400EB94BA /* EZAudioPlayFileExample.app */;
productType = "com.apple.product-type.application";
};
94056F17185BD83400EB94BA /* EZAudioPlayFileExampleTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 94056F2B185BD83400EB94BA /* Build configuration list for PBXNativeTarget "EZAudioPlayFileExampleTests" */;
buildPhases = (
94056F14185BD83400EB94BA /* Sources */,
94056F15185BD83400EB94BA /* Frameworks */,
94056F16185BD83400EB94BA /* Resources */,
);
buildRules = (
);
dependencies = (
94056F1D185BD83400EB94BA /* PBXTargetDependency */,
);
name = EZAudioPlayFileExampleTests;
productName = EZAudioPlayFileExampleTests;
productReference = 94056F18185BD83400EB94BA /* EZAudioPlayFileExampleTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
@@ -190,6 +318,11 @@
attributes = {
LastUpgradeCheck = 0500;
ORGANIZATIONNAME = "Syed Haris Ali";
TargetAttributes = {
94056F17185BD83400EB94BA = {
TestTargetID = 94056EF6185BD83400EB94BA;
};
};
};
buildConfigurationList = 94056EF2185BD83400EB94BA /* Build configuration list for PBXProject "EZAudioPlayFileExample" */;
compatibilityVersion = "Xcode 3.2";
@@ -205,6 +338,7 @@
projectRoot = "";
targets = (
94056EF6185BD83400EB94BA /* EZAudioPlayFileExample */,
94056F17185BD83400EB94BA /* EZAudioPlayFileExampleTests */,
);
};
/* End PBXProject section */
@@ -214,15 +348,25 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
6628E2351B3A121A00020E56 /* simple-drum-beat.wav in Resources */,
94056F05185BD83400EB94BA /* InfoPlist.strings in Resources */,
94056F13185BD83400EB94BA /* Images.xcassets in Resources */,
9417A7491867DD3400D9D37B /* CHANGELOG in Resources */,
9417A6D51865928C00D9D37B /* simple-drum-beat.wav in Resources */,
94056F0B185BD83400EB94BA /* Credits.rtf in Resources */,
9417A74A1867DD3400D9D37B /* VERSION in Resources */,
94056F32185BD86D00EB94BA /* PlayFileViewController.xib in Resources */,
94056F11185BD83400EB94BA /* MainMenu.xib in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
94056F16185BD83400EB94BA /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
94056F23185BD83400EB94BA /* InfoPlist.strings in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@@ -230,14 +374,42 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
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 */,
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 */,
9417A7471867DD3400D9D37B /* EZRecorder.m in Sources */,
9417A73F1867DD3400D9D37B /* EZAudio.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
94056F14185BD83400EB94BA /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
94056F25185BD83400EB94BA /* EZAudioPlayFileExampleTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
94056F1D185BD83400EB94BA /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 94056EF6185BD83400EB94BA /* EZAudioPlayFileExample */;
targetProxy = 94056F1C185BD83400EB94BA /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
94056F03185BD83400EB94BA /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
@@ -263,6 +435,14 @@
name = MainMenu.xib;
sourceTree = "<group>";
};
94056F21185BD83400EB94BA /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
94056F22185BD83400EB94BA /* en */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
@@ -339,14 +519,9 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
COMBINE_HIDPI_IMAGES = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
/Users/haris/Documents/code/openSource/EZAudio/EZAudioExamples/OSX/Build/Products/Debug,
);
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "EZAudioPlayFileExample/EZAudioPlayFileExample-Prefix.pch";
INFOPLIST_FILE = "EZAudioPlayFileExample/EZAudioPlayFileExample-Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
@@ -357,19 +532,54 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
COMBINE_HIDPI_IMAGES = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
/Users/haris/Documents/code/openSource/EZAudio/EZAudioExamples/OSX/Build/Products/Debug,
);
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "EZAudioPlayFileExample/EZAudioPlayFileExample-Prefix.pch";
INFOPLIST_FILE = "EZAudioPlayFileExample/EZAudioPlayFileExample-Info.plist";
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
name = Release;
};
94056F2C185BD83400EB94BA /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/EZAudioPlayFileExample.app/Contents/MacOS/EZAudioPlayFileExample";
COMBINE_HIDPI_IMAGES = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(DEVELOPER_FRAMEWORKS_DIR)",
"$(inherited)",
);
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "EZAudioPlayFileExample/EZAudioPlayFileExample-Prefix.pch";
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
INFOPLIST_FILE = "EZAudioPlayFileExampleTests/EZAudioPlayFileExampleTests-Info.plist";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUNDLE_LOADER)";
WRAPPER_EXTENSION = xctest;
};
name = Debug;
};
94056F2D185BD83400EB94BA /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/EZAudioPlayFileExample.app/Contents/MacOS/EZAudioPlayFileExample";
COMBINE_HIDPI_IMAGES = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(DEVELOPER_FRAMEWORKS_DIR)",
"$(inherited)",
);
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "EZAudioPlayFileExample/EZAudioPlayFileExample-Prefix.pch";
INFOPLIST_FILE = "EZAudioPlayFileExampleTests/EZAudioPlayFileExampleTests-Info.plist";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUNDLE_LOADER)";
WRAPPER_EXTENSION = xctest;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
@@ -391,6 +601,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
94056F2B185BD83400EB94BA /* Build configuration list for PBXNativeTarget "EZAudioPlayFileExampleTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
94056F2C185BD83400EB94BA /* Debug */,
94056F2D185BD83400EB94BA /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 94056EEF185BD83400EB94BA /* Project object */;
@@ -26,7 +26,10 @@
#import <Cocoa/Cocoa.h>
// Import EZAudio header
#import <EZAudio/EZAudio.h>
#import "EZAudio.h"
#import "EZAudioFile.h"
#import "EZOutput.h"
#import "EZAudioPlot.h"
/**
Here's the default audio file included with the example
@@ -36,97 +39,52 @@
/**
Using the EZOutputDataSource to provide output data to the EZOutput component.
*/
@interface PlayFileViewController : NSViewController <NSOpenSavePanelDelegate,
EZAudioPlayerDelegate>
@interface PlayFileViewController : NSViewController <NSOpenSavePanelDelegate,EZAudioFileDelegate,EZOutputDataSource>
#pragma mark - Components
/**
The EZAudioFile representing of the currently selected audio file
*/
@property (nonatomic, strong) EZAudioFile *audioFile;
/**
The EZOutput component used to output the audio file's audio data.
*/
@property (nonatomic, strong) EZAudioPlayer *player;
@property (nonatomic,strong) EZAudioFile *audioFile;
/**
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
/**
A label to display the current file path with the waveform shown
*/
@property (nonatomic, weak) IBOutlet NSTextField *filePathLabel;
/**
A checkbox button to that allows you to specify if the audio player should loop.
*/
@property (nonatomic, weak) IBOutlet NSButton *loopCheckboxButton;
/**
A label to display the audio file's current position.
*/
@property (nonatomic, weak) IBOutlet NSTextField *positionLabel;
@property (nonatomic,weak) IBOutlet NSTextField *filePathLabel;
/**
A slider to indicate the current frame position in the audio file
*/
@property (nonatomic, weak) IBOutlet NSSlider *positionSlider;
@property (nonatomic,weak) IBOutlet NSSlider *framePositionSlider;
/**
A label to display the value of the rolling history length of the audio plot.
A slider to adjust the sample rate.
*/
@property (nonatomic, weak) IBOutlet NSTextField *rollingHistoryLengthLabel;
/**
A slider to adjust the rolling history length of the audio plot.
*/
@property (nonatomic, weak) IBOutlet NSSlider *rollingHistoryLengthSlider;
/**
A slider to adjust the volume.
*/
@property (nonatomic, weak) IBOutlet NSSlider *volumeSlider;
/**
A label to display the volume of the audio plot.
*/
@property (nonatomic, weak) IBOutlet NSTextField *volumeLabel;
@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;
/**
The microphone pop up button (contains the menu for choosing a microphone input)
*/
@property (nonatomic, weak) IBOutlet NSPopUpButton *outputDevicePopUpButton;
#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)
*/
-(IBAction)changePlotType:(id)sender;
/**
Changes the length of the rolling history of the audio plot.
*/
- (IBAction)changeRollingHistoryLength:(id)sender;
/**
Switches the loop state on the audio player regarding whether the current playing audio file should loop back to the beginning when it finishes.
*/
- (IBAction)changeShouldLoop:(id)sender;
/**
Changes the volume of the audio coming out of the EZOutput.
*/
- (IBAction)changeVolume:(id)sender;
/**
Prompts the file manager and loads in a new audio file into the EZAudioFile representation.
*/
@@ -31,390 +31,260 @@
@end
@implementation PlayFileViewController
@synthesize audioFile;
@synthesize eof = _eof;
@synthesize framePositionSlider;
//------------------------------------------------------------------------------
#pragma mark - Dealloc
//------------------------------------------------------------------------------
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
#pragma mark - Initialization
-(id)init {
self = [super initWithNibName:NSStringFromClass(self.class) bundle:nil];
if(self){
[self initializeViewController];
}
return self;
}
-(id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithNibName:NSStringFromClass(self.class) bundle:nil];
if(self){
[self initializeViewController];
}
return self;
}
-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:NSStringFromClass(self.class) bundle:nil];
if(self){
[self initializeViewController];
}
return self;
}
#pragma mark - Initialize View Controller
-(void)initializeViewController {
}
//------------------------------------------------------------------------------
#pragma mark - Customize the Audio Plot
//------------------------------------------------------------------------------
- (void)awakeFromNib
{
//
// Customizing the audio plot's look
//
// Background color
self.audioPlot.backgroundColor = [NSColor colorWithCalibratedRed: 0.816 green: 0.349 blue: 0.255 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;
//
// Create EZOutput to play audio data
//
self.player = [EZAudioPlayer audioPlayerWithDelegate:self];
self.player.shouldLoop = YES;
//
// Reload the menu for the output device selector popup button
//
[self reloadOutputDevicePopUpButtonMenu];
//
// Configure UI components
//
self.volumeSlider.floatValue = [self.player volume];
self.volumeLabel.floatValue = [self.player volume];
self.rollingHistoryLengthSlider.intValue = [self.audioPlot rollingHistoryLength];
self.rollingHistoryLengthLabel.intValue = [self.audioPlot rollingHistoryLength];
self.loopCheckboxButton.state = [self.player shouldLoop];
//
// Listen for state changes to the EZAudioPlayer
//
[self setupNotifications];
//
// Try opening the sample file
//
[self openFileWithFilePathURL:[NSURL fileURLWithPath:kAudioFileDefault]];
-(void)awakeFromNib {
/*
Customizing the audio plot's look
*/
// Background color
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.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.audioPlotLeft.plotType = EZPlotTypeBuffer;
self.audioPlotRight.plotType = EZPlotTypeBuffer;
// Fill
self.audioPlotLeft.shouldFill = YES;
self.audioPlotRight.shouldFill = YES;
// Mirror
self.audioPlotLeft.shouldMirror = YES;
self.audioPlotRight.shouldMirror = YES;
/*
Try opening the sample file
*/
[self openFileWithFilePathURL:[NSURL fileURLWithPath:kAudioFileDefault]];
}
//------------------------------------------------------------------------------
#pragma mark - Notifications
//------------------------------------------------------------------------------
- (void)setupNotifications
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(audioPlayerDidChangeAudioFile:)
name:EZAudioPlayerDidChangeAudioFileNotification
object:self.player];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(audioPlayerDidChangeOutputDevice:)
name:EZAudioPlayerDidChangeOutputDeviceNotification
object:self.player];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(audioPlayerDidChangePlayState:)
name:EZAudioPlayerDidChangePlayStateNotification
object:self.player];
// This notification will only trigger if the player's shouldLoop property is set to NO
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(audioPlayerDidReachEndOfFile:)
name:EZAudioPlayerDidReachEndOfFileNotification
object:self.player];
}
//------------------------------------------------------------------------------
- (void)audioPlayerDidChangeAudioFile:(NSNotification *)notification
{
EZAudioPlayer *player = [notification object];
NSLog(@"Player changed audio file: %@", [player audioFile]);
}
//------------------------------------------------------------------------------
- (void)audioPlayerDidChangeOutputDevice:(NSNotification *)notification
{
EZAudioPlayer *player = [notification object];
NSLog(@"Player changed output device: %@", [player device]);
}
//------------------------------------------------------------------------------
- (void)audioPlayerDidChangePlayState:(NSNotification *)notification
{
EZAudioPlayer *player = [notification object];
NSLog(@"Player change play state, isPlaying: %i", [player isPlaying]);
}
//------------------------------------------------------------------------------
- (void)audioPlayerDidReachEndOfFile:(NSNotification *)notification
{
NSLog(@"Player did reach end of file!");
[self.playButton setState:NSOffState];
}
//------------------------------------------------------------------------------
#pragma mark - Actions
//------------------------------------------------------------------------------
- (void)changedOutput:(NSMenuItem *)item
{
EZAudioDevice *device = [item representedObject];
[self.player setDevice:device];
-(void)changePlotType:(id)sender {
NSInteger selectedSegment = [sender selectedSegment];
switch(selectedSegment){
case 0:
[self drawBufferPlot];
break;
case 1:
[self drawRollingPlot];
break;
default:
break;
}
}
//------------------------------------------------------------------------------
- (void)changePlotType:(id)sender
-(void)changeOutputSamplingFrequency:(id)sender
{
NSInteger selectedSegment = [sender selectedSegment];
switch(selectedSegment)
{
case 0:
[self drawBufferPlot];
break;
case 1:
[self drawRollingPlot];
break;
default:
break;
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;
openDlg.canChooseDirectories = NO;
openDlg.delegate = self;
if( [openDlg runModal] == NSOKButton ){
NSArray *selectedFiles = [openDlg URLs];
NSLog(@"selected files: %@", selectedFiles);
[self openFileWithFilePathURL:selectedFiles.firstObject];
}
}
-(void)play:(id)sender {
if( ![[EZOutput sharedOutput] isPlaying] ){
if( self.eof ){
[self.audioFile seekToFrame:0];
}
[EZOutput sharedOutput].outputDataSource = self;
[[EZOutput sharedOutput] startPlayback];
}
else {
[EZOutput sharedOutput].outputDataSource = nil;
[[EZOutput sharedOutput] stopPlayback];
}
}
//------------------------------------------------------------------------------
- (void)changeShouldLoop:(id)sender
{
NSInteger state = [(NSButton *)sender state];
[self.player setShouldLoop:state];
-(void)seekToFrame:(id)sender {
[self.audioFile seekToFrame:(SInt64)[(NSSlider*)sender doubleValue]];
}
//------------------------------------------------------------------------------
- (void)changeVolume:(id)sender
{
float value = [(NSSlider *)sender floatValue];
[self.player setVolume:value];
self.volumeLabel.floatValue = value;
}
//------------------------------------------------------------------------------
- (void)changeRollingHistoryLength:(id)sender
{
float value = [(NSSlider *)sender floatValue];
self.audioPlot.rollingHistoryLength = (int)value;
self.rollingHistoryLengthLabel.floatValue = value;
}
//------------------------------------------------------------------------------
- (void)openFile:(id)sender
{
NSOpenPanel* openDlg = [NSOpenPanel openPanel];
openDlg.canChooseFiles = YES;
openDlg.canChooseDirectories = NO;
openDlg.delegate = self;
if ([openDlg runModal] == NSOKButton)
{
NSArray *selectedFiles = [openDlg URLs];
[self openFileWithFilePathURL:selectedFiles.firstObject];
}
}
//------------------------------------------------------------------------------
-(void)play:(id)sender
{
if (![self.player isPlaying])
{
if (self.eof)
{
[self.audioFile seekToFrame:0];
}
if (self.audioPlot.plotType == EZPlotTypeBuffer && self.audioPlot.shouldFill == YES)
{
self.audioPlot.plotType = EZPlotTypeRolling;
}
[self.player play];
}
else
{
[self.player pause];
}
}
//------------------------------------------------------------------------------
-(void)seekToFrame:(id)sender
{
double value = [(NSSlider*)sender doubleValue];
[self.player seekToFrame:(SInt64)value];
self.positionLabel.doubleValue = value;
}
//------------------------------------------------------------------------------
#pragma mark - Action Extensions
//------------------------------------------------------------------------------
/*
Give the visualization of the current buffer (this is almost exactly the openFrameworks audio input example)
*/
-(void)drawBufferPlot
{
// Change the plot type to the buffer plot
self.audioPlot.plotType = EZPlotTypeBuffer;
// Don't fill
self.audioPlot.shouldFill = NO;
// Don't mirror over the x-axis
self.audioPlot.shouldMirror = NO;
-(void)drawBufferPlot {
// Change the plot type to the buffer plot
self.audioPlotLeft.plotType = EZPlotTypeBuffer;
// Don't fill
self.audioPlotLeft.shouldFill = NO;
// Don't mirror over the x-axis
self.audioPlotLeft.shouldMirror = NO;
}
//------------------------------------------------------------------------------
/*
Give the classic mirrored, rolling waveform look
*/
-(void)drawRollingPlot
{
// Change the plot type to the rolling plot
self.audioPlot.plotType = EZPlotTypeRolling;
// Fill the waveform
self.audioPlot.shouldFill = YES;
// Mirror over the x-axis
self.audioPlot.shouldMirror = YES;
-(void)drawRollingPlot {
// Change the plot type to the rolling plot
self.audioPlotLeft.plotType = EZPlotTypeRolling;
// Fill the waveform
self.audioPlotLeft.shouldFill = YES;
// Mirror over the x-axis
self.audioPlotLeft.shouldMirror = YES;
}
//------------------------------------------------------------------------------
-(void)openFileWithFilePathURL:(NSURL*)filePathURL
{
//
// Stop playback
//
[self.player pause];
//
// Clear the audio plot
//
[self.audioPlot clear];
-(void)openFileWithFilePathURL:(NSURL*)filePathURL {
//
// Load the audio file and customize the UI
//
self.audioFile = [EZAudioFile audioFileWithURL:filePathURL];
self.eof = NO;
self.filePathLabel.stringValue = filePathURL.lastPathComponent;
self.positionSlider.minValue = 0.0f;
self.positionSlider.maxValue = (double)self.audioFile.totalFrames;
self.playButton.state = NSOffState;
self.plotSegmentControl.selectedSegment = 1;
// Stop playback
[[EZOutput sharedOutput] stopPlayback];
//
// Change back to a buffer plot, but mirror and fill the waveform
//
self.audioPlot.plotType = EZPlotTypeBuffer;
self.audioPlot.shouldFill = YES;
self.audioPlot.shouldMirror = YES;
AudioStreamBasicDescription asbd;
self.audioFile = [EZAudioFile audioFileWithURL:filePathURL
delegate:self
permission:EZAudioFilePermissionRead
fileFormat:asbd];
[[EZOutput sharedOutput] setAudioStreamBasicDescription:self.audioFile.clientFormat];
//
// Plot the whole waveform
//
__weak typeof (self) weakSelf = self;
[self.audioFile getWaveformDataWithNumberOfPoints:1024
completion:^(float **waveformData,
int length)
{
[weakSelf.audioPlot updateBuffer:waveformData[0]
withBufferSize:length];
}];
//
// Play the audio file
//
[self.player setAudioFile:self.audioFile];
self.eof = NO;
self.filePathLabel.stringValue = filePathURL.lastPathComponent;
self.framePositionSlider.minValue = 0.0f;
self.framePositionSlider.maxValue = (double)self.audioFile.totalFrames;
self.playButton.state = NSOffState;
self.plotSegmentControl.selectedSegment = 1;
// Set the client format from the EZAudioFile on the output
#pragma mark Mess Around With Audio Stream Basic Description Here!
self.sampleRateSlider.floatValue = self.audioFile.clientFormat.mSampleRate;
// Plot the whole waveform
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.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)reloadOutputDevicePopUpButtonMenu
{
NSArray *outputDevices = [EZAudioDevice outputDevices];
NSMenu *menu = [[NSMenu alloc] init];
NSMenuItem *defaultOutputDeviceItem;
for (EZAudioDevice *device in outputDevices)
{
NSMenuItem *item = [[NSMenuItem alloc] initWithTitle:device.name
action:@selector(changedOutput:)
keyEquivalent:@""];
item.representedObject = device;
item.target = self;
[menu addItem:item];
// If this device is the same one the microphone is using then
// we will use this menu item as the currently selected item
// in the microphone input popup button's list of items. For instance,
// if you are connected to an external display by default the external
// display's microphone might be used instead of the mac's built in
// mic.
if ([device isEqual:[self.player device]])
{
defaultOutputDeviceItem = item;
}
-(void)audioFile:(EZAudioFile *)audioFile
updatedPosition:(SInt64)framePosition {
dispatch_async(dispatch_get_main_queue(), ^{
if( ![self.framePositionSlider.cell isHighlighted] ){
self.framePositionSlider.floatValue = (float)framePosition;
}
self.outputDevicePopUpButton.menu = menu;
//
// Set the selected device to the current selection on the
// microphone input popup button
//
[self.outputDevicePopUpButton selectItem:defaultOutputDeviceItem];
});
}
//------------------------------------------------------------------------------
#pragma mark - EZAudioPlayerDelegate
//------------------------------------------------------------------------------
- (void) audioPlayer:(EZAudioPlayer *)audioPlayer
playedAudio:(float **)buffer
withBufferSize:(UInt32)bufferSize
withNumberOfChannels:(UInt32)numberOfChannels
inAudioFile:(EZAudioFile *)audioFile
#pragma mark - EZOutputDataSource
-(void) output:(EZOutput*)output
shouldFillAudioBufferList:(AudioBufferList*)audioBufferList
withNumberOfFrames:(UInt32)frames
{
__weak typeof (self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf.audioPlot updateBuffer:buffer[0]
withBufferSize:bufferSize];
});
if( self.audioFile )
{
UInt32 bufferSize;
[self.audioFile readFrames:frames
audioBufferList:audioBufferList
bufferSize:&bufferSize
eof:&_eof];
if( _eof )
{
[self seekToFrame:0];
}
}
}
//------------------------------------------------------------------------------
- (void)audioPlayer:(EZAudioPlayer *)audioPlayer
updatedPosition:(SInt64)framePosition
inAudioFile:(EZAudioFile *)audioFile
{
__weak typeof (self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
if (!weakSelf.positionSlider.highlighted)
{
weakSelf.positionSlider.floatValue = (float)framePosition;
weakSelf.positionLabel.integerValue = framePosition;
}
});
}
//------------------------------------------------------------------------------
#pragma mark - NSOpenSavePanelDelegate
//------------------------------------------------------------------------------
/**
Here's an example how to filter the open panel to only show the supported file types by the EZAudioFile (which are just the audio file types supported by Core Audio).
*/
- (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename
{
NSString *ext = [filename pathExtension];
NSArray *fileTypes = [EZAudioFile supportedAudioFileTypes];
BOOL isDirectory = [ext isEqualToString:@""];
return [fileTypes containsObject:ext] || isDirectory;
-(BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename {
NSString* ext = [filename pathExtension];
if ([ext isEqualToString:@""] || [ext isEqualToString:@"/"] || ext == nil || ext == NULL || [ext length] < 1) {
return YES;
}
NSArray *fileTypes = [EZAudioFile supportedAudioFileTypes];
NSEnumerator* tagEnumerator = [fileTypes objectEnumerator];
NSString* allowedExt;
while ((allowedExt = [tagEnumerator nextObject]))
{
if ([ext caseInsensitiveCompare:allowedExt] == NSOrderedSame)
{
return YES;
}
}
return NO;
}
//------------------------------------------------------------------------------
@end
@@ -1,39 +1,30 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="7706" systemVersion="14E46" 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="7706"/>
<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="checkboxButton" destination="5xb-MK-C8b" id="fx2-y9-qXH"/>
<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="loopCheckboxButton" destination="5xb-MK-C8b" id="Rlt-Z9-Zy8"/>
<outlet property="outputDevicePopUpButton" destination="0LV-Bi-dGz" id="QTQ-qq-Ro8"/>
<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="positionLabel" destination="KYm-Io-VNv" id="Fhs-Ya-szS"/>
<outlet property="positionSlider" destination="CFP-v0-TzQ" id="EGD-qT-48R"/>
<outlet property="rollingHistoryLengthLabel" destination="vKe-ey-hXI" id="UiN-5S-TOn"/>
<outlet property="rollingHistoryLengthSlider" destination="vj5-qT-JkR" id="sEj-iE-yTV"/>
<outlet property="sampleRateSlider" destination="rRH-oS-VV3" id="8ij-Ff-CZK"/>
<outlet property="view" destination="Xpo-HP-Ost" id="zlj-bW-4iz"/>
<outlet property="volumeLabel" destination="3ul-3w-l3S" id="sXM-mC-tN0"/>
<outlet property="volumeSlider" destination="rRH-oS-VV3" id="kql-X5-amB"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customView id="Xpo-HP-Ost">
<rect key="frame" x="0.0" y="-1" width="480" height="366"/>
<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="146"/>
</customView>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="2Ma-jj-U3z">
<rect key="frame" x="12" y="320" width="125" height="32"/>
<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"/>
@@ -43,15 +34,15 @@
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="0eT-7c-7fJ">
<rect key="frame" x="140" y="332" width="36" height="16"/>
<textFieldCell key="cell" lineBreakMode="truncatingMiddle" sendsActionOnEndEditing="YES" alignment="left" title="Label" id="vXQ-HF-vLX">
<font key="font" metaFont="cellTitle"/>
<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"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="OQp-Lr-dlS">
<rect key="frame" x="12" y="287" width="125" height="32"/>
<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"/>
@@ -61,9 +52,9 @@
</connections>
</button>
<segmentedControl verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="bZW-tA-C61">
<rect key="frame" x="335" y="292" width="129" height="24"/>
<rect key="frame" x="333" y="345" width="129" height="24"/>
<constraints>
<constraint firstAttribute="width" constant="125" id="A3V-sd-Jab"/>
<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"/>
@@ -77,177 +68,55 @@
</connections>
</segmentedControl>
<slider verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="CFP-v0-TzQ">
<rect key="frame" x="117" y="242" width="269" height="20"/>
<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="vj5-qT-JkR">
<rect key="frame" x="117" y="217" width="269" height="20"/>
<sliderCell key="cell" continuous="YES" alignment="left" minValue="128" maxValue="1024" doubleValue="128" tickMarkPosition="above" sliderType="linear" id="uRZ-Kf-cgJ"/>
<connections>
<action selector="changeRollingHistoryLength:" target="-2" id="eYD-H1-n52"/>
</connections>
</slider>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="7AB-VA-xL3">
<rect key="frame" x="16" y="269" width="53" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="left" title="Volume:" id="GAa-Hp-OlV">
<font key="font" metaFont="systemBold" size="12"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Fw5-pm-4w0">
<rect key="frame" x="16" y="244" width="58" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="left" title="Position:" id="9hW-4Z-OEW">
<font key="font" metaFont="systemBold" size="12"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="Aa9-nc-WHJ">
<rect key="frame" x="16" y="219" width="96" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="left" title="Rolling Length:" id="Mfs-zu-sCx">
<font key="font" metaFont="systemBold" size="12"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="3ul-3w-l3S">
<rect key="frame" x="390" y="269" width="72" height="16"/>
<constraints>
<constraint firstAttribute="width" constant="68" id="eyi-2x-AqF"/>
</constraints>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="left" title="0.00" id="vlK-Hb-Yca">
<numberFormatter key="formatter" formatterBehavior="custom10_4" minimumIntegerDigits="1" maximumIntegerDigits="1" minimumFractionDigits="2" maximumFractionDigits="2" id="bBU-vS-tEB">
<metadata>
<real key="inspectorSampleValue" value="44"/>
</metadata>
</numberFormatter>
<font key="font" metaFont="cellTitle"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<slider verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="rRH-oS-VV3">
<rect key="frame" x="117" y="267" width="269" height="20"/>
<sliderCell key="cell" continuous="YES" state="on" alignment="left" maxValue="1" doubleValue="0.5" tickMarkPosition="above" sliderType="linear" id="xbX-Ce-da5"/>
<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="changeVolume:" target="-2" id="iKx-7d-34D"/>
<action selector="changeOutputSamplingFrequency:" target="-2" id="yWM-Ei-ztA"/>
</connections>
</slider>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="vKe-ey-hXI">
<rect key="frame" x="390" y="219" width="72" height="16"/>
<constraints>
<constraint firstAttribute="width" constant="68" id="7dV-vH-IBh"/>
</constraints>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="left" title="0" id="JiA-3H-vb2">
<numberFormatter key="formatter" formatterBehavior="custom10_4" minimumIntegerDigits="0" maximumIntegerDigits="42" id="AYM-Tu-k5w">
<metadata>
<real key="inspectorSampleValue" value="44"/>
</metadata>
</numberFormatter>
<font key="font" metaFont="cellTitle"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<popUpButton verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="0LV-Bi-dGz" userLabel="microphoneInputPopUpButton">
<rect key="frame" x="16" y="161" width="180" height="26"/>
<constraints>
<constraint firstAttribute="width" constant="175" id="fDF-j7-LMD"/>
</constraints>
<popUpButtonCell key="cell" type="push" title="Item 1" bezelStyle="rounded" alignment="left" lineBreakMode="truncatingTail" state="on" borderStyle="borderAndBezel" imageScaling="proportionallyDown" inset="2" selectedItem="a7m-V2-Mw8" id="VLU-oW-zia">
<behavior key="behavior" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="menu"/>
<menu key="menu" id="uLv-18-vra">
<items>
<menuItem title="Item 1" state="on" id="a7m-V2-Mw8"/>
<menuItem title="Item 2" id="qJe-zH-SrZ"/>
<menuItem title="Item 3" id="zlE-dQ-R4x"/>
</items>
</menu>
</popUpButtonCell>
</popUpButton>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="RRH-G6-xkQ">
<rect key="frame" x="16" y="194" width="50" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Output:" id="2OQ-1o-1vp">
<font key="font" metaFont="systemBold" size="12"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="KYm-Io-VNv">
<rect key="frame" x="390" y="244" width="72" height="16"/>
<constraints>
<constraint firstAttribute="width" constant="68" id="fOV-38-VqQ"/>
</constraints>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="left" title="0" id="gfS-wb-pFu">
<numberFormatter key="formatter" formatterBehavior="custom10_4" minimumIntegerDigits="0" maximumIntegerDigits="42" id="py5-BY-fQN">
<metadata>
<real key="inspectorSampleValue" value="44"/>
</metadata>
</numberFormatter>
<font key="font" metaFont="cellTitle"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button translatesAutoresizingMaskIntoConstraints="NO" id="5xb-MK-C8b">
<rect key="frame" x="410" y="162" width="54" height="18"/>
<buttonCell key="cell" type="check" title="Loop" bezelStyle="regularSquare" imagePosition="right" state="on" inset="2" id="O83-sN-k0z">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="changeShouldLoop:" target="-2" id="lJN-Pe-JWA"/>
</connections>
</button>
<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="vKe-ey-hXI" firstAttribute="baseline" secondItem="vj5-qT-JkR" secondAttribute="baseline" id="03u-fe-Q2r"/>
<constraint firstItem="vj5-qT-JkR" firstAttribute="leading" secondItem="Aa9-nc-WHJ" secondAttribute="trailing" constant="9" id="0QT-jZ-rPL"/>
<constraint firstItem="rRH-oS-VV3" firstAttribute="baseline" secondItem="7AB-VA-xL3" secondAttribute="baseline" id="4WU-L2-5TI"/>
<constraint firstItem="3ul-3w-l3S" firstAttribute="trailing" secondItem="bZW-tA-C61" secondAttribute="trailing" constant="-2" id="75o-B9-UmY"/>
<constraint firstItem="vj5-qT-JkR" firstAttribute="baseline" secondItem="Aa9-nc-WHJ" secondAttribute="baseline" id="8ia-fE-3Ej"/>
<constraint firstItem="Lz1-Gs-1lD" firstAttribute="top" secondItem="5xb-MK-C8b" secondAttribute="bottom" constant="18" id="CG6-jk-Mmj"/>
<constraint firstAttribute="bottom" secondItem="Lz1-Gs-1lD" secondAttribute="bottom" id="Cdp-xg-Udu"/>
<constraint firstItem="0eT-7c-7fJ" firstAttribute="leading" secondItem="2Ma-jj-U3z" secondAttribute="trailing" constant="11" id="F4y-lz-2p6"/>
<constraint firstItem="7AB-VA-xL3" firstAttribute="top" secondItem="OQp-Lr-dlS" secondAttribute="bottom" constant="9" id="GbY-7Z-81V"/>
<constraint firstItem="Lz1-Gs-1lD" firstAttribute="leading" secondItem="Xpo-HP-Ost" secondAttribute="leading" id="HW1-t3-mGg"/>
<constraint firstItem="OQp-Lr-dlS" firstAttribute="leading" secondItem="2Ma-jj-U3z" secondAttribute="leading" id="Ira-0b-xzU"/>
<constraint firstItem="rRH-oS-VV3" firstAttribute="baseline" secondItem="3ul-3w-l3S" secondAttribute="baseline" id="KOD-cZ-e52"/>
<constraint firstItem="CFP-v0-TzQ" firstAttribute="baseline" secondItem="Fw5-pm-4w0" secondAttribute="baseline" id="PbC-KG-EzE"/>
<constraint firstItem="2Ma-jj-U3z" firstAttribute="leading" secondItem="Xpo-HP-Ost" secondAttribute="leading" constant="18" id="PiQ-KC-eta"/>
<constraint firstItem="Fw5-pm-4w0" firstAttribute="top" secondItem="7AB-VA-xL3" secondAttribute="bottom" constant="9" id="SPP-F6-chs"/>
<constraint firstItem="vj5-qT-JkR" firstAttribute="trailing" secondItem="CFP-v0-TzQ" secondAttribute="trailing" id="URd-7z-JBO"/>
<constraint firstAttribute="trailing" secondItem="5xb-MK-C8b" secondAttribute="trailing" constant="18" id="WdY-l2-cmp"/>
<constraint firstItem="RRH-G6-xkQ" firstAttribute="leading" secondItem="Xpo-HP-Ost" secondAttribute="leading" constant="18" id="WiW-CU-B7N"/>
<constraint firstAttribute="trailing" secondItem="Lz1-Gs-1lD" secondAttribute="trailing" id="ay9-Mt-iFx"/>
<constraint firstItem="rRH-oS-VV3" firstAttribute="leading" secondItem="CFP-v0-TzQ" secondAttribute="leading" id="cWn-4f-E04"/>
<constraint firstItem="Fw5-pm-4w0" firstAttribute="leading" secondItem="Aa9-nc-WHJ" secondAttribute="leading" id="cnU-xS-2CO"/>
<constraint firstItem="KYm-Io-VNv" firstAttribute="leading" secondItem="3ul-3w-l3S" secondAttribute="leading" id="dac-nA-d4U"/>
<constraint firstItem="0LV-Bi-dGz" firstAttribute="top" secondItem="RRH-G6-xkQ" secondAttribute="bottom" constant="9" id="dnZ-Rx-iiV"/>
<constraint firstItem="Aa9-nc-WHJ" firstAttribute="top" secondItem="Fw5-pm-4w0" secondAttribute="bottom" constant="9" id="fc6-dV-Lxf"/>
<constraint firstItem="vKe-ey-hXI" firstAttribute="leading" secondItem="vj5-qT-JkR" secondAttribute="trailing" constant="8" symbolic="YES" id="fuB-es-weU"/>
<constraint firstItem="2Ma-jj-U3z" firstAttribute="top" secondItem="Xpo-HP-Ost" secondAttribute="top" constant="18" id="hDY-vI-eWO"/>
<constraint firstItem="OQp-Lr-dlS" firstAttribute="trailing" secondItem="2Ma-jj-U3z" secondAttribute="trailing" id="hXB-Gp-9wN"/>
<constraint firstAttribute="trailing" secondItem="bZW-tA-C61" secondAttribute="trailing" constant="18" id="iUL-br-ASL"/>
<constraint firstItem="OQp-Lr-dlS" firstAttribute="top" secondItem="bZW-tA-C61" secondAttribute="top" id="kny-zR-hiF"/>
<constraint firstItem="Lz1-Gs-1lD" firstAttribute="top" secondItem="0LV-Bi-dGz" secondAttribute="bottom" constant="18" id="l6M-H0-bYA"/>
<constraint firstItem="7AB-VA-xL3" firstAttribute="leading" secondItem="OQp-Lr-dlS" secondAttribute="leading" id="omq-Zw-Gvk"/>
<constraint firstItem="KYm-Io-VNv" firstAttribute="baseline" secondItem="CFP-v0-TzQ" secondAttribute="baseline" id="q8e-0e-Xqt"/>
<constraint firstItem="0eT-7c-7fJ" firstAttribute="top" secondItem="2Ma-jj-U3z" secondAttribute="top" id="snu-Ma-cHX"/>
<constraint firstItem="OQp-Lr-dlS" firstAttribute="top" secondItem="2Ma-jj-U3z" secondAttribute="bottom" constant="12" symbolic="YES" id="tkR-bR-msf"/>
<constraint firstItem="0LV-Bi-dGz" firstAttribute="leading" secondItem="Xpo-HP-Ost" secondAttribute="leading" constant="18" id="tmm-8d-ldM"/>
<constraint firstItem="3ul-3w-l3S" firstAttribute="leading" secondItem="rRH-oS-VV3" secondAttribute="trailing" constant="8" id="uF6-oM-5lN"/>
<constraint firstItem="Fw5-pm-4w0" firstAttribute="leading" secondItem="7AB-VA-xL3" secondAttribute="leading" id="vNA-c3-Gcy"/>
<constraint firstItem="RRH-G6-xkQ" firstAttribute="top" secondItem="Aa9-nc-WHJ" secondAttribute="bottom" constant="9" id="w92-Np-SYg"/>
<constraint firstItem="rRH-oS-VV3" firstAttribute="trailing" secondItem="CFP-v0-TzQ" secondAttribute="trailing" id="wdS-Vt-caT"/>
<constraint firstItem="vj5-qT-JkR" firstAttribute="leading" secondItem="CFP-v0-TzQ" secondAttribute="leading" id="wek-S9-BzW"/>
<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="191" y="409"/>
<point key="canvasLocation" x="241" y="331.5"/>
</customView>
</objects>
</document>
@@ -0,0 +1,22 @@
<?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>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>com.sha.${PRODUCT_NAME:rfc1034identifier}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>
@@ -0,0 +1,51 @@
//
// EZAudioPlayFileExampleTests.m
// EZAudioPlayFileExampleTests
//
// Created by Syed Haris Ali on 12/1/13.
// Copyright (c) 2013 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 <XCTest/XCTest.h>
@interface EZAudioPlayFileExampleTests : XCTestCase
@end
@implementation EZAudioPlayFileExampleTests
- (void)setUp
{
[super setUp];
// Put setup code here. This method is called before the invocation of each test method in the class.
}
- (void)tearDown
{
// Put teardown code here. This method is called after the invocation of each test method in the class.
[super tearDown];
}
- (void)testExample
{
XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__);
}
@end
@@ -0,0 +1,2 @@
/* Localized versions of Info.plist keys */

Some files were not shown because too many files have changed in this diff Show More