Compare commits

...

27 Commits

Author SHA1 Message Date
Syed Haris Ali ab9fb5b9bd bumped podspec and README to 0.4.0 and removed getting started reference (moving all to github) 2015-06-29 19:43:46 -07:00
Syed Haris Ali f98d520efd Merge pull request #171 from syedhali/output_rewrite
Rewrote EZOutput using AUGraph
2015-06-29 19:41:54 -07:00
Syed Haris Ali 6a44909a2f Used EZOutputDelegate for audio received callback and fixed minor bgs 2015-06-29 19:33:24 -07:00
Syed Haris Ali c85d1d9d2f so much documentation 2015-06-29 19:13:01 -07:00
Syed Haris Ali d03a5f76e6 added lots of documentation 2015-06-29 19:09:52 -07:00
Syed Haris Ali 6d7625027f added more documentation and pan property 2015-06-29 18:42:47 -07:00
Syed Haris Ali cce5ee0af1 minor tweak 2015-06-29 18:26:31 -07:00
Syed Haris Ali cc868e79dd little style tweak 2015-06-29 18:15:27 -07:00
Syed Haris Ali d2ad3d2034 added clean up for float converter 2015-06-29 18:14:10 -07:00
Syed Haris Ali 3fdb33209d added float converter 2015-06-29 18:11:56 -07:00
Syed Haris Ali e16c775be8 fixed broken IB connection 2015-06-29 17:52:45 -07:00
Syed Haris Ali 59a3a22c6a added plot clear 2015-06-29 17:51:53 -07:00
Syed Haris Ali 4c7719f168 added output selector to OSX play file example 2015-06-29 17:47:10 -07:00
Syed Haris Ali 5789954cb9 updated examples that explicitly use EZOuput and added volume controls 2015-06-29 17:32:03 -07:00
Syed Haris Ali c34c7c9f89 added documentation for Core Audio properties 2015-06-29 16:28:20 -07:00
Syed Haris Ali 6745265dd5 rewrote EZOuput and added output device support for iOS and OSX 2015-06-29 16:17:08 -07:00
Syed Haris Ali c7727f8586 Changed totalDuration references to just duration 2015-06-28 17:05:18 -07:00
Syed Haris Ali c8ee8a4f7d updated podspec dependencies to explicitly depend on TPCircularBuffer and ignore files in EZAudio folder 2015-06-28 17:01:13 -07:00
Syed Haris Ali 2e90d0e6f1 updated podspec 2015-06-28 16:55:25 -07:00
Syed Haris Ali abe23bed12 Merge pull request #169 from syedhali/ha-audiofile_rewrite
Updated EZAudioFile for thread-safety and added more properties
2015-06-28 16:54:43 -07:00
Syed Haris Ali 95d9a1e7ba finished documentation 2015-06-28 16:51:19 -07:00
Syed Haris Ali c55423759b adding additional documentation and updated play file example for iOS 2015-06-28 16:44:30 -07:00
Syed Haris Ali f2b61d340e minor tweak 2015-06-28 15:41:43 -07:00
Syed Haris Ali cc663ec82d removed AEFloatConverter and updated iOS examples 2015-06-28 15:39:25 -07:00
Syed Haris Ali c96a65831d fixed block name 2015-06-28 15:28:11 -07:00
Syed Haris Ali 0073f555bc added documentation 2015-06-28 15:27:21 -07:00
Syed Haris Ali 62055f1782 Updated EZAudioFile 2015-06-28 15:17:04 -07:00
40 changed files with 2386 additions and 1522 deletions
+4 -3
View File
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "EZAudio"
s.version = "0.2.0"
s.version = "0.4.0"
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"
@@ -10,8 +10,9 @@ Pod::Spec.new do |s|
s.osx.deployment_target = '10.8'
s.source = { :git => "https://github.com/syedhali/EZAudio.git", :tag => s.version }
s.source_files = 'EZAudio/*.{h,m,c}'
s.exclude_files = 'EZAudio/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.dependency 'TPCircularBuffer', '~> 0.0'
s.requires_arc = true;
end
end
-124
View File
@@ -1,124 +0,0 @@
//
// AEFloatConverter.h
// The Amazing Audio Engine
//
// Created by Michael Tyson on 25/10/2012.
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
#ifdef __cplusplus
extern "C" {
#endif
#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
/*!
* Universal converter to float format
*
* Use this class to easily convert arbitrary audio formats to floating point
* for use with utilities like the Accelerate framework.
*/
@interface AEFloatConverter : NSObject
/*!
* Initialize
*
* @param sourceFormat The audio format to use
*/
- (id)initWithSourceFormat:(AudioStreamBasicDescription)sourceFormat;
/*!
* Convert audio to floating-point
*
* This C function, safe to use in a Core Audio realtime thread context, will take
* an audio buffer list of audio in the format you provided at initialisation, and
* convert it into a noninterleaved float array.
*
* @param converter Pointer to the converter object.
* @param sourceBuffer An audio buffer list containing the source audio.
* @param targetBuffers An array of floating-point arrays to store the converted float audio into.
* Note that you must provide the correct number of arrays, to match the number of channels.
* @param frames The number of frames to convert.
* @return YES on success; NO on failure
*/
BOOL AEFloatConverterToFloat(AEFloatConverter* converter, AudioBufferList *sourceBuffer, float * const * targetBuffers, UInt32 frames);
/*!
* Convert audio to floating-point, in a buffer list
*
* This C function, safe to use in a Core Audio realtime thread context, will take
* an audio buffer list of audio in the format you provided at initialisation, and
* convert it into a noninterleaved float format.
*
* @param converter Pointer to the converter object.
* @param sourceBuffer An audio buffer list containing the source audio.
* @param targetBuffer An audio buffer list to store the converted floating-point audio.
* @param frames The number of frames to convert.
* @return YES on success; NO on failure
*/
BOOL AEFloatConverterToFloatBufferList(AEFloatConverter* converter, AudioBufferList *sourceBuffer, AudioBufferList *targetBuffer, UInt32 frames);
/*!
* Convert audio from floating-point
*
* This C function, safe to use in a Core Audio realtime thread context, will take
* an audio buffer list of audio in the format you provided at initialisation, and
* convert it into a float array.
*
* @param converter Pointer to the converter object.
* @param sourceBuffers An array of floating-point arrays containing the floating-point audio to convert.
* Note that you must provide the correct number of arrays, to match the number of channels.
* @param targetBuffer An audio buffer list to store the converted audio into.
* @param frames The number of frames to convert.
* @return YES on success; NO on failure
*/
BOOL AEFloatConverterFromFloat(AEFloatConverter* converter, float * const * sourceBuffers, AudioBufferList *targetBuffer, UInt32 frames);
/*!
* Convert audio from floating-point, in a buffer list
*
* This C function, safe to use in a Core Audio realtime thread context, will take
* an audio buffer list of audio in the format you provided at initialisation, and
* convert it into a float array.
*
* @param converter Pointer to the converter object.
* @param sourceBuffer An audio buffer list containing the source audio.
* @param targetBuffer An audio buffer list to store the converted audio into.
* @param frames The number of frames to convert.
* @return YES on success; NO on failure
*/
BOOL AEFloatConverterFromFloatBufferList(AEFloatConverter* converter, AudioBufferList *sourceBuffer, AudioBufferList *targetBuffer, UInt32 frames);
/*!
* The AudioStreamBasicDescription representing the converted floating-point format
*/
@property (nonatomic, readonly) AudioStreamBasicDescription floatingPointAudioDescription;
/*!
* The source audio format set at initialization
*/
@property (nonatomic, readonly) AudioStreamBasicDescription sourceFormat;
@end
#ifdef __cplusplus
}
#endif
-211
View File
@@ -1,211 +0,0 @@
//
// AEFloatConverter.m
// The Amazing Audio Engine
//
// Created by Michael Tyson on 25/10/2012.
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
#import "AEFloatConverter.h"
#define checkResult(result,operation) (_checkResult((result),(operation),strrchr(__FILE__, '/')+1,__LINE__))
static inline BOOL _checkResult(OSStatus result, const char *operation, const char* file, int line) {
if (result != noErr) {
NSLog(@"%s:%d: %s result %d %08X %4.4s", file, line, operation, (int)result, (int)result, (char*)&result);
return NO;
}
return YES;
}
#define kNoMoreDataErr -2222
struct complexInputDataProc_t {
AudioBufferList *sourceBuffer;
};
@interface AEFloatConverter () {
AudioStreamBasicDescription _sourceAudioDescription;
AudioStreamBasicDescription _floatAudioDescription;
AudioConverterRef _toFloatConverter;
AudioConverterRef _fromFloatConverter;
AudioBufferList *_scratchFloatBufferList;
}
static OSStatus complexInputDataProc(AudioConverterRef inAudioConverter,
UInt32 *ioNumberDataPackets,
AudioBufferList *ioData,
AudioStreamPacketDescription **outDataPacketDescription,
void *inUserData);
@end
@implementation AEFloatConverter
@synthesize sourceFormat = _sourceAudioDescription;
-(id)initWithSourceFormat:(AudioStreamBasicDescription)sourceFormat {
if (!(self = [super init])) return nil;
_floatAudioDescription.mFormatID = kAudioFormatLinearPCM;
_floatAudioDescription.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked | kAudioFormatFlagIsNonInterleaved;
_floatAudioDescription.mChannelsPerFrame = sourceFormat.mChannelsPerFrame;
_floatAudioDescription.mBytesPerPacket = sizeof(float);
_floatAudioDescription.mFramesPerPacket = 1;
_floatAudioDescription.mBytesPerFrame = sizeof(float);
_floatAudioDescription.mBitsPerChannel = 8 * sizeof(float);
_floatAudioDescription.mSampleRate = sourceFormat.mSampleRate;
_sourceAudioDescription = sourceFormat;
if (memcmp(&sourceFormat, &_floatAudioDescription, sizeof(AudioStreamBasicDescription)) != 0) {
checkResult(AudioConverterNew(&sourceFormat, &_floatAudioDescription, &_toFloatConverter), "AudioConverterNew");
checkResult(AudioConverterNew(&_floatAudioDescription, &sourceFormat, &_fromFloatConverter), "AudioConverterNew");
_scratchFloatBufferList = (AudioBufferList*)malloc(sizeof(AudioBufferList) + (_floatAudioDescription.mChannelsPerFrame-1)*sizeof(AudioBuffer));
_scratchFloatBufferList->mNumberBuffers = _floatAudioDescription.mChannelsPerFrame;
for ( int i=0; i<_scratchFloatBufferList->mNumberBuffers; i++) {
_scratchFloatBufferList->mBuffers[i].mNumberChannels = 1;
}
}
return self;
}
-(void)dealloc {
if (_toFloatConverter) AudioConverterDispose(_toFloatConverter);
if (_fromFloatConverter) AudioConverterDispose(_fromFloatConverter);
if (_scratchFloatBufferList) free(_scratchFloatBufferList);
// [super dealloc];
}
BOOL AEFloatConverterToFloat(AEFloatConverter* THIS, AudioBufferList *sourceBuffer, float * const * targetBuffers, UInt32 frames) {
if (frames == 0) return YES;
if (THIS->_toFloatConverter) {
UInt32 priorDataByteSize = sourceBuffer->mBuffers[0].mDataByteSize;
for ( int i=0; i<sourceBuffer->mNumberBuffers; i++) {
sourceBuffer->mBuffers[i].mDataByteSize = frames * THIS->_sourceAudioDescription.mBytesPerFrame;
}
for ( int i=0; i<THIS->_scratchFloatBufferList->mNumberBuffers; i++) {
THIS->_scratchFloatBufferList->mBuffers[i].mData = targetBuffers[i];
THIS->_scratchFloatBufferList->mBuffers[i].mDataByteSize = frames * sizeof(float);
}
OSStatus result = AudioConverterFillComplexBuffer(THIS->_toFloatConverter,
complexInputDataProc,
&(struct complexInputDataProc_t) { .sourceBuffer = sourceBuffer },
&frames,
THIS->_scratchFloatBufferList,
NULL);
for ( int i=0; i<sourceBuffer->mNumberBuffers; i++) {
sourceBuffer->mBuffers[i].mDataByteSize = priorDataByteSize;
}
if (!checkResult(result, "AudioConverterConvertComplexBuffer")) {
return NO;
}
} else {
for ( int i=0; i<sourceBuffer->mNumberBuffers; i++) {
memcpy(targetBuffers[i], sourceBuffer->mBuffers[i].mData, frames * sizeof(float));
}
}
return YES;
}
BOOL AEFloatConverterToFloatBufferList(AEFloatConverter* converter, AudioBufferList *sourceBuffer, AudioBufferList *targetBuffer, UInt32 frames) {
assert(targetBuffer->mNumberBuffers == converter->_floatAudioDescription.mChannelsPerFrame);
float *targetBuffers[targetBuffer->mNumberBuffers];
for ( int i=0; i<targetBuffer->mNumberBuffers; i++) {
targetBuffers[i] = (float*)targetBuffer->mBuffers[i].mData;
}
return AEFloatConverterToFloat(converter, sourceBuffer, targetBuffers, frames);
}
BOOL AEFloatConverterFromFloat(AEFloatConverter* THIS, float * const * sourceBuffers, AudioBufferList *targetBuffer, UInt32 frames) {
if (frames == 0) return YES;
if (THIS->_fromFloatConverter) {
for ( int i=0; i<THIS->_scratchFloatBufferList->mNumberBuffers; i++) {
THIS->_scratchFloatBufferList->mBuffers[i].mData = sourceBuffers[i];
THIS->_scratchFloatBufferList->mBuffers[i].mDataByteSize = frames * sizeof(float);
}
UInt32 priorDataByteSize = targetBuffer->mBuffers[0].mDataByteSize;
for ( int i=0; i<targetBuffer->mNumberBuffers; i++) {
targetBuffer->mBuffers[i].mDataByteSize = frames * THIS->_sourceAudioDescription.mBytesPerFrame;
}
OSStatus result = AudioConverterFillComplexBuffer(THIS->_fromFloatConverter,
complexInputDataProc,
&(struct complexInputDataProc_t) { .sourceBuffer = THIS->_scratchFloatBufferList },
&frames,
targetBuffer,
NULL);
for ( int i=0; i<targetBuffer->mNumberBuffers; i++) {
targetBuffer->mBuffers[i].mDataByteSize = priorDataByteSize;
}
if (!checkResult(result, "AudioConverterConvertComplexBuffer")) {
return NO;
}
} else {
for ( int i=0; i<targetBuffer->mNumberBuffers; i++) {
memcpy(targetBuffer->mBuffers[i].mData, sourceBuffers[i], frames * sizeof(float));
}
}
return YES;
}
BOOL AEFloatConverterFromFloatBufferList(AEFloatConverter* converter, AudioBufferList *sourceBuffer, AudioBufferList *targetBuffer, UInt32 frames) {
assert(sourceBuffer->mNumberBuffers == converter->_floatAudioDescription.mChannelsPerFrame);
float *sourceBuffers[sourceBuffer->mNumberBuffers];
for ( int i=0; i<sourceBuffer->mNumberBuffers; i++) {
sourceBuffers[i] = (float*)sourceBuffer->mBuffers[i].mData;
}
return AEFloatConverterFromFloat(converter, sourceBuffers, targetBuffer, frames);
}
static OSStatus complexInputDataProc(AudioConverterRef inAudioConverter,
UInt32 *ioNumberDataPackets,
AudioBufferList *ioData,
AudioStreamPacketDescription **outDataPacketDescription,
void *inUserData) {
struct complexInputDataProc_t *arg = (struct complexInputDataProc_t*)inUserData;
if (!arg->sourceBuffer) {
return kNoMoreDataErr;
}
memcpy(ioData, arg->sourceBuffer, sizeof(AudioBufferList) + (arg->sourceBuffer->mNumberBuffers-1)*sizeof(AudioBuffer));
arg->sourceBuffer = NULL;
return noErr;
}
-(AudioStreamBasicDescription)floatingPointAudioDescription {
return _floatAudioDescription;
}
@end
+25 -9
View File
@@ -33,6 +33,14 @@
*/
+ (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
/**
@@ -42,6 +50,13 @@
*/
+ (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;
//------------------------------------------------------------------------------
/**
@@ -52,6 +67,16 @@
+ (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
/**
@@ -63,15 +88,6 @@
//------------------------------------------------------------------------------
/**
Enumerates all the available output devices and returns the result in an NSArray of EZAudioDevice instances.
- OSX only
@return An NSArray of output EZAudioDevice instances.
*/
+ (NSArray *)outputDevices;
//------------------------------------------------------------------------------
/**
Enumerates all the available devices.
- OSX only
+61
View File
@@ -49,6 +49,19 @@
//------------------------------------------------------------------------------
+ (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];
@@ -61,6 +74,18 @@
//------------------------------------------------------------------------------
+ (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)
@@ -101,6 +126,42 @@
//------------------------------------------------------------------------------
+ (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];
+214 -87
View File
@@ -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
@@ -25,10 +25,26 @@
#import <Foundation/Foundation.h>
#import <AudioToolbox/AudioToolbox.h>
#import "EZAudioFloatData.h"
//------------------------------------------------------------------------------
@class EZAudio;
@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);
//------------------------------------------------------------------------------
#pragma mark - EZAudioFileDelegate
//------------------------------------------------------------------------------
/**
The EZAudioFileDelegate provides event callbacks for the EZAudioFile object. These type of events are triggered by reads and seeks on the file and gives feedback such as the audio data read as a float array for visualizations and the new seek position for UI updating.
*/
@@ -42,102 +58,147 @@
@param bufferSize The length of the buffers float arrays
@param numberOfChannels The number of channels. 2 for stereo, 1 for mono.
*/
-(void) audioFile:(EZAudioFile*)audioFile
readAudio:(float**)buffer
withBufferSize:(UInt32)bufferSize
withNumberOfChannels:(UInt32)numberOfChannels;
- (void) audioFile:(EZAudioFile *)audioFile
readAudio:(float **)buffer
withBufferSize:(UInt32)bufferSize
withNumberOfChannels:(UInt32)numberOfChannels;
//------------------------------------------------------------------------------
/**
Occurs when the audio file's internal seek position has been updated by the EZAudioFile functions `readFrames:audioBufferList:bufferSize:eof:` or `audioFile:updatedPosition:`.
@param audioFile The instance of the EZAudio in which the change occured
@param framePosition The new frame index as a 64-bit signed integer
*/
-(void)audioFile:(EZAudioFile*)audioFile
updatedPosition:(SInt64)framePosition;
- (void)audioFile:(EZAudioFile *)audioFile updatedPosition:(SInt64)framePosition;
@end
//------------------------------------------------------------------------------
#pragma mark - EZAudioFile
//------------------------------------------------------------------------------
/**
The EZAudioFile provides a lightweight and intuitive way to asynchronously interact with audio files. These interactions included reading audio data, seeking within an audio file, getting information about the file, and pulling the waveform data for visualizing the contents of the audio file. The EZAudioFileDelegate provides event callbacks for when reads, seeks, and various updates happen within the audio file to allow the caller to interact with the action in meaningful ways. Common use cases here could be to read the audio file's data as AudioBufferList structures for output (see EZOutput) and visualizing the audio file's data as a float array using an audio plot (see EZAudioPlot).
*/
@interface EZAudioFile : NSObject
#pragma mark - Blocks
/**
A block used when returning back the waveform data. The waveform data itself will be an array of float values and the length indicates the total length of the float array.
@param waveformData An array of float values representing the amplitude data from the audio waveform
@param length The length of the waveform data's float array
*/
typedef void (^WaveformDataCompletionBlock)(float *waveformData, UInt32 length);
//------------------------------------------------------------------------------
#pragma mark - Properties
//------------------------------------------------------------------------------
/**
A EZAudioFileDelegate for the audio file that is used to return events such as new seek positions within the file and the read audio data as a float array.
*/
@property (nonatomic,assign) id<EZAudioFileDelegate> audioFileDelegate;
@property (nonatomic, weak) id<EZAudioFileDelegate> delegate;
//------------------------------------------------------------------------------
#pragma mark - Initialization
//------------------------------------------------------------------------------
/**
The resolution of the waveform data. This value specifies how the recommendedDrawingFrameRate chooses itself. A low value like 128 will render a waveform containing 128 points representing a low resolution waveform while a high value like 4096 will render a high quality waveform. Higher resolutions provide more detail, but take more work to render in the audio waveform plots (EZAudioPlot or EZAudioPlotGL) while lower resolutions providel less detail, but work better for displaying many at a time (like in a UITableView)
*/
@property (nonatomic,assign) UInt32 waveformResolution;
#pragma mark - Initializers
///-----------------------------------------------------------
/// @name Initializers
///-----------------------------------------------------------
@name Initialization
*/
/**
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.
@return The newly created EZAudioFile instance. nil if the file path does not exist.
*/
-(EZAudioFile*)initWithURL:(NSURL*)url;
- (instancetype)initWithURL:(NSURL *)url;
/**
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.
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
@param url The file path reference of the audio file as an NSURL.
@return The newly created EZAudioFile instance.
*/
-(EZAudioFile*)initWithURL:(NSURL*)url
andDelegate:(id<EZAudioFileDelegate>)delegate;
- (instancetype)initWithURL:(NSURL *)url
delegate:(id<EZAudioFileDelegate>)delegate;
//------------------------------------------------------------------------------
/**
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.
@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 format in its file format, but your app wants to read the samples at a sample rate of 44.1 kHz so it can iterate with other components (like a audio processing graph) without any weird playback effects. If this initializer is not used then a non-interleaved float format will be assumed.
@return The newly created EZAudioFile instance.
*/
- (instancetype)initWithURL:(NSURL *)url
delegate:(id<EZAudioFileDelegate>)delegate
clientFormat:(AudioStreamBasicDescription)clientFormat;
//------------------------------------------------------------------------------
#pragma mark - Class Initializers
///-----------------------------------------------------------
/// @name Class Initializers
///-----------------------------------------------------------
//------------------------------------------------------------------------------
/**
@name Class Initializers
*/
/**
Class method that creates a new instance of the EZAudioFile using a file path URL.
@param url The file path reference of the audio file as an NSURL.
@return The newly created EZAudioFile instance.
*/
+(EZAudioFile*)audioFileWithURL:(NSURL*)url;
+ (instancetype)audioFileWithURL:(NSURL *)url;
//------------------------------------------------------------------------------
/**
Class method that creates a new instance of the EZAudioFile using a file path URL and allows specifying an EZAudioFileDelegate.
@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 with a delegate conforming to the EZAudioFileDelegate protocol.
@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
@return The newly created EZAudioFile instance.
*/
+(EZAudioFile*)audioFileWithURL:(NSURL*)url
andDelegate:(id<EZAudioFileDelegate>)delegate;
+ (instancetype)audioFileWithURL:(NSURL *)url
delegate:(id<EZAudioFileDelegate>)delegate;
//------------------------------------------------------------------------------
/**
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.
@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.
@return The newly created EZAudioFile instance.
*/
+ (instancetype)audioFileWithURL:(NSURL *)url
delegate:(id<EZAudioFileDelegate>)delegate
clientFormat:(AudioStreamBasicDescription)clientFormat;
//------------------------------------------------------------------------------
#pragma mark - Class Methods
///-----------------------------------------------------------
/// @name Class Methods
///-----------------------------------------------------------
//------------------------------------------------------------------------------
/**
@name Class Methods
*/
/**
A class method that subclasses can override to specify the default client format that will be used to read audio data from this file. A client format is different from the file format in that it is the format of the other components interacting with this file. For instance, the file on disk could be a 22.5 kHz, float format, but we might have an audio processing graph that has a 44.1 kHz, signed integer format that we'd like to interact with. The client format lets us set that 44.1 kHz format on the audio file to properly read samples from it with any interpolation or format conversion that must take place done automatically within the EZAudioFile `readFrames:audioBufferList:bufferSize:eof:` method. 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.
*/
+(NSArray*)supportedAudioFileTypes;
+ (NSArray *)supportedAudioFileTypes;
//------------------------------------------------------------------------------
#pragma mark - Events
///-----------------------------------------------------------
/// @name Reading The Audio File
///-----------------------------------------------------------
//------------------------------------------------------------------------------
/**
@name Reading From The Audio File
*/
/**
Reads a specified number of frames from the audio file. In addition, this will notify the EZAudioFileDelegate (if specified) of the read data as a float array with the audioFile:readAudio:withBufferSize:withNumberOfChannels: event and the new seek position within the file with the audioFile:updatedPosition: event.
@@ -146,96 +207,162 @@ typedef void (^WaveformDataCompletionBlock)(float *waveformData, UInt32 length);
@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;
///-----------------------------------------------------------
/// @name Seeking Through The Audio File
///-----------------------------------------------------------
//------------------------------------------------------------------------------
/**
@name Seeking Through The Audio File
*/
/**
Seeks through an audio file to a specified frame. This will notify the EZAudioFileDelegate (if specified) with the audioFile:updatedPosition: function.
@param frame The new frame position to seek to as a SInt64.
*/
-(void)seekToFrame:(SInt64)frame;
- (void)seekToFrame:(SInt64)frame;
//------------------------------------------------------------------------------
#pragma mark - Getters
///-----------------------------------------------------------
/// @name Getting Information About The Audio File
///-----------------------------------------------------------
//------------------------------------------------------------------------------
/**
@name Getting Information About The Audio File
*/
/**
Provides the AudioStreamBasicDescription structure used within the app. The file's format will be converted to this format and then sent back as either a float array or a `AudioBufferList` pointer. Use this when communicating with other EZAudio components.
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!
@return An AudioStreamBasicDescription structure describing the format of the audio file.
*/
-(AudioStreamBasicDescription)clientFormat;
@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;
//------------------------------------------------------------------------------
/**
Provides the AudioStreamBasicDescription structure containing the format of the file.
@return An AudioStreamBasicDescription structure describing the format of the audio file.
*/
-(AudioStreamBasicDescription)fileFormat;
@property (readonly) AudioStreamBasicDescription fileFormat;
//------------------------------------------------------------------------------
/**
Provides the frame index (a.k.a the seek positon) within the audio file as an integer. This can be helpful when seeking through the audio file.
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.
@return The current frame index within the audio file as a SInt64.
*/
-(SInt64)frameIndex;
@property (readonly) SInt64 frameIndex;
//------------------------------------------------------------------------------
/**
Provides a dictionary containing the metadata (ID3) tags that are included in the header for the audio file. Typically this contains stuff like artist, title, release year, etc.
@return An NSDictionary containing the metadata for the audio file.
*/
-(NSDictionary *)metadata;
@property (readonly) 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.
*/
-(Float32)totalDuration;
@property (readonly) NSTimeInterval totalDuration __attribute__((deprecated));;
//------------------------------------------------------------------------------
/**
Provides the total frame count of the audio file.
@return The total number of frames in the audio file as a SInt64.
Provides the total frame count of the audio file in the client format.
@return The total number of frames in the audio file in the AudioStreamBasicDescription representing the client format as a SInt64.
*/
-(SInt64)totalFrames;
@property (readonly) SInt64 totalClientFrames;
//------------------------------------------------------------------------------
/**
Provides the total frame count of the audio file in the file format.
@return The total number of frames in the audio file in the AudioStreamBasicDescription representing the file format as a SInt64.
*/
@property (readonly) SInt64 totalFrames;
//------------------------------------------------------------------------------
/**
Provides the NSURL for the audio file.
@return An NSURL representing the path of the EZAudioFile instance.
*/
-(NSURL*)url;
@property (nonatomic, copy, readonly) NSURL *url;
//------------------------------------------------------------------------------
#pragma mark - Helpers
///-----------------------------------------------------------
/// @name Manipulating The Audio Data
///-----------------------------------------------------------
//------------------------------------------------------------------------------
/**
Tells the caller whether the EZAudioFile has cached waveform data that was loaded via the getWaveformDataWithCompletionBlock: function.
* @return A BOOL indicating whether there is cached waveform data
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.
*/
-(BOOL)hasLoadedAudioData;
- (EZAudioFloatData *)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.
*/
- (EZAudioFloatData *)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.
*/
- (void)getWaveformDataWithCompletionBlock:(EZAudioWaveformDataCompletionBlock)completion;
//------------------------------------------------------------------------------
/**
Asynchronously pulls the waveform amplitude data into a float array for the receiver.
@param waveformDataCompletionBlock A WaveformDataCompletionBlock that executes when the waveform data has been extracted. Provides the waveform data as a float array and the length of the array.
@param numberOfPoints A UInt32 representing the number of data points you need. The higher the number of points the more detailed the waveform will be.
@param completion A EZAudioWaveformDataCompletionBlock that executes when the waveform data has been extracted. Provides a `EZAudioFloatData` instance containing the waveform data for all audio channels.
*/
-(void)getWaveformDataWithCompletionBlock:(WaveformDataCompletionBlock)waveformDataCompletionBlock;
- (void)getWaveformDataWithNumberOfPoints:(UInt32)numberOfPoints
completion:(EZAudioWaveformDataCompletionBlock)completion;
/**
Provides the minimum number of buffers that would be required with the constant frames read rate provided.
@param frameRate A constant frame rate to use when calculating the number of buffers needed as a UInt32.
@return The minimum number of buffers required for the constant frames read rate provided as a UInt32.
*/
-(UInt32)minBuffersWithFrameRate:(UInt32)frameRate;
/**
Provides a frame rate to use when drawing and averaging a bin of values to create each point in a graph. The ideal amount of end buffers seems to be between 1000-3000 so we determine a frame rate per audio file that can achieve a high degree of detail for the entire waveform.
@return A frame rate value as a UInt32 to use when reading frames in a file.
*/
-(UInt32)recommendedDrawingFrameRate;
//------------------------------------------------------------------------------
@end
+626 -320
View File
@@ -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,343 +24,649 @@
// THE SOFTWARE.
#import "EZAudioFile.h"
#import "AEFloatConverter.h"
#import "EZAudioUtilities.h"
#define kEZAudioFileWaveformDefaultResolution (1024)
//------------------------------------------------------------------------------
@interface EZAudioFile (){
// Reading from the audio file
ExtAudioFileRef _audioFile;
AudioStreamBasicDescription _clientFormat;
AudioStreamBasicDescription _fileFormat;
float **_floatBuffers;
AEFloatConverter *_floatConverter;
SInt64 _frameIndex;
CFURLRef _sourceURL;
Float32 _totalDuration;
SInt64 _totalFrames;
// Waveform Data
float *_waveformData;
UInt32 _waveformFrameRate;
UInt32 _waveformTotalBuffers;
}
#import "EZAudio.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
@synthesize audioFileDelegate = _audioFileDelegate;
@synthesize waveformResolution = _waveformResolution;
#pragma mark - Initializers
-(EZAudioFile*)initWithURL:(NSURL*)url {
self = [super init];
if(self){
_sourceURL = (__bridge CFURLRef)url;
[self _configureAudioFile];
}
return self;
}
//------------------------------------------------------------------------------
#pragma mark - Dealloc
//------------------------------------------------------------------------------
-(EZAudioFile *)initWithURL:(NSURL *)url andDelegate:(id<EZAudioFileDelegate>)delegate {
self = [self initWithURL:url];
if(self){
self.audioFileDelegate = delegate;
}
return self;
}
#pragma mark - Class Initializers
+(EZAudioFile*)audioFileWithURL:(NSURL*)url {
return [[EZAudioFile alloc] initWithURL:url];
}
+(EZAudioFile *)audioFileWithURL:(NSURL *)url andDelegate:(id<EZAudioFileDelegate>)delegate {
return [[EZAudioFile alloc] initWithURL:url andDelegate:delegate];
}
#pragma mark - Class Methods
+(NSArray *)supportedAudioFileTypes {
return @[ @"aac",
@"caf",
@"aif",
@"aiff",
@"aifc",
@"mp3",
@"mp4",
@"m4a",
@"snd",
@"au",
@"sd2",
@"wav" ];
}
#pragma mark - Private Configuation
-(void)_configureAudioFile {
// Source URL should not be nil
NSAssert(_sourceURL,@"Source URL was not specified correctly.");
// Try to open the file for reading
[EZAudioUtilities checkResult:ExtAudioFileOpenURL(_sourceURL,&_audioFile)
operation:"Failed to open audio file for reading"];
// Try pulling the stream description
UInt32 size = sizeof(_fileFormat);
[EZAudioUtilities checkResult:ExtAudioFileGetProperty(_audioFile,kExtAudioFileProperty_FileDataFormat, &size, &_fileFormat)
operation:"Failed to get audio stream basic description of input file"];
// Try pulling the total frame size
size = sizeof(_totalFrames);
[EZAudioUtilities checkResult:ExtAudioFileGetProperty(_audioFile,kExtAudioFileProperty_FileLengthFrames, &size, &_totalFrames)
operation:"Failed to get total frames of input file"];
_totalFrames = MAX(1, _totalFrames);
// Total duration
_totalDuration = _totalFrames / _fileFormat.mSampleRate;
// Set the client format on the stream
switch (_fileFormat.mChannelsPerFrame) {
case 1:
_clientFormat = [EZAudioUtilities monoFloatFormatWithSampleRate:_fileFormat.mSampleRate];
break;
case 2:
_clientFormat = [EZAudioUtilities stereoFloatInterleavedFormatWithSampleRate:_fileFormat.mSampleRate];
break;
default:
break;
}
[EZAudioUtilities checkResult:ExtAudioFileSetProperty(_audioFile,
kExtAudioFileProperty_ClientDataFormat,
sizeof (AudioStreamBasicDescription),
&_clientFormat)
operation:"Couldn't set client data format on input ext file"];
// Allocate the float buffers
_floatConverter = [[AEFloatConverter alloc] initWithSourceFormat:_clientFormat];
size_t sizeToAllocate = sizeof(float*) * _clientFormat.mChannelsPerFrame;
sizeToAllocate = MAX(8, sizeToAllocate);
_floatBuffers = (float**)malloc( sizeToAllocate);
UInt32 outputBufferSize = 32 * 1024; // 32 KB
for ( int i=0; i< _clientFormat.mChannelsPerFrame; i++) {
_floatBuffers[i] = (float*)malloc(outputBufferSize);
}
[EZAudioUtilities printASBD:_fileFormat];
// There's no waveform data yet
_waveformData = NULL;
// Set the default resolution for the waveform data
_waveformResolution = kEZAudioFileWaveformDefaultResolution;
}
#pragma mark - Events
-(void)readFrames:(UInt32)frames
audioBufferList:(AudioBufferList *)audioBufferList
bufferSize:(UInt32 *)bufferSize
eof:(BOOL *)eof {
[EZAudioUtilities checkResult:ExtAudioFileRead(_audioFile,
&frames,
audioBufferList)
operation:"Failed to read audio data from audio file"];
*bufferSize = audioBufferList->mBuffers[0].mDataByteSize/sizeof(float);
*eof = frames == 0;
_frameIndex += frames;
if (self.audioFileDelegate){
if ([self.audioFileDelegate respondsToSelector:@selector(audioFile:updatedPosition:)]){
[self.audioFileDelegate audioFile:self
updatedPosition:_frameIndex];
}
if ([self.audioFileDelegate respondsToSelector:@selector(audioFile:readAudio:withBufferSize:withNumberOfChannels:)]){
AEFloatConverterToFloat(_floatConverter,audioBufferList,_floatBuffers,frames);
[self.audioFileDelegate audioFile:self
readAudio:_floatBuffers
withBufferSize:frames
withNumberOfChannels:_clientFormat.mChannelsPerFrame];
}
}
}
-(void)seekToFrame:(SInt64)frame {
[EZAudioUtilities checkResult:ExtAudioFileSeek(_audioFile,frame)
operation:"Failed to seek frame position within audio file"];
_frameIndex = frame;
if (self.audioFileDelegate){
if ([self.audioFileDelegate respondsToSelector:@selector(audioFile:updatedPosition:)]){
[self.audioFileDelegate audioFile:self updatedPosition:_frameIndex];
}
}
}
#pragma mark - Getters
-(BOOL)hasLoadedAudioData {
return _waveformData != NULL;
}
-(void)getWaveformDataWithCompletionBlock:(WaveformDataCompletionBlock)waveformDataCompletionBlock {
SInt64 currentFramePosition = _frameIndex;
if (_waveformData != NULL){
waveformDataCompletionBlock( _waveformData, _waveformTotalBuffers);
return;
}
_waveformFrameRate = [self recommendedDrawingFrameRate];
_waveformTotalBuffers = [self minBuffersWithFrameRate:_waveformFrameRate];
_waveformData = (float*)malloc(sizeof(float)*_waveformTotalBuffers);
if (self.totalFrames == 0){
waveformDataCompletionBlock( _waveformData, _waveformTotalBuffers);
return;
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0ul), ^{
for( int i = 0; i < _waveformTotalBuffers; i++){
// Take a snapshot of each buffer through the audio file to form the waveform
AudioBufferList *bufferList = [EZAudioUtilities audioBufferListWithNumberOfFrames:_waveformFrameRate
numberOfChannels:_clientFormat.mChannelsPerFrame
interleaved:YES];
UInt32 bufferSize;
BOOL eof;
// Read in the specified number of frames
[EZAudioUtilities checkResult:ExtAudioFileRead(_audioFile,
&_waveformFrameRate,
bufferList)
operation:"Failed to read audio data from audio file"];
bufferSize = bufferList->mBuffers[0].mDataByteSize/sizeof(float);
bufferSize = MAX(1, bufferSize);
eof = _waveformFrameRate == 0;
_frameIndex += _waveformFrameRate;
// Calculate RMS of each buffer
float rms = [EZAudioUtilities RMS:bufferList->mBuffers[0].mData
length:bufferSize];
_waveformData[i] = rms;
// Since we malloc'ed, we should cleanup
[EZAudioUtilities freeBufferList:bufferList];
}
// Seek the audio file back to the beginning
[EZAudioUtilities checkResult:ExtAudioFileSeek(_audioFile,currentFramePosition)
operation:"Failed to seek frame position within audio file"];
_frameIndex = currentFramePosition;
// Once we're done send off the waveform data
dispatch_async(dispatch_get_main_queue(), ^{
waveformDataCompletionBlock( _waveformData, _waveformTotalBuffers);
});
});
}
-(AudioStreamBasicDescription)clientFormat {
return _clientFormat;
}
-(AudioStreamBasicDescription)fileFormat {
return _fileFormat;
}
-(SInt64)frameIndex {
return _frameIndex;
}
-(NSDictionary *)metadata
- (void)dealloc
{
AudioFileID audioFileID;
UInt32 propSize = sizeof(audioFileID);
[EZAudioUtilities checkResult:ExtAudioFileGetProperty(_audioFile,
kExtAudioFileProperty_AudioFile,
&propSize,
&audioFileID)
operation:"Failed to get audio file id"];
CFDictionaryRef metadata;
UInt32 isWritable;
[EZAudioUtilities checkResult:AudioFileGetPropertyInfo(audioFileID,
kAudioFilePropertyInfoDictionary,
&propSize,
&isWritable)
operation:"Failed to get the size of the metadata dictionary"];
[EZAudioUtilities checkResult:AudioFileGetProperty(audioFileID,
kAudioFilePropertyInfoDictionary,
&propSize,
&metadata)
operation:"Failed to get metadata dictionary"];
return (__bridge NSDictionary *)metadata;
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);
}
-(Float32)totalDuration {
return _totalDuration;
}
//------------------------------------------------------------------------------
#pragma mark - Initialization
//------------------------------------------------------------------------------
-(SInt64)totalFrames {
return _totalFrames;
}
-(NSURL *)url {
return (__bridge NSURL*)_sourceURL;
}
#pragma mark - Setters
-(void)setWaveformResolution:(UInt32)waveformResolution {
if (_waveformResolution != waveformResolution){
_waveformResolution = waveformResolution;
if (_waveformData){
free(_waveformData);
_waveformData = NULL;
- (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;
}
#pragma mark - Helpers
-(UInt32)minBuffersWithFrameRate:(UInt32)frameRate {
frameRate = frameRate > 0 ? frameRate : 1;
UInt32 val = (UInt32) _totalFrames / frameRate + 1;
return MAX(1, val);
//------------------------------------------------------------------------------
- (instancetype)initWithURL:(NSURL *)url
{
return [self initWithURL:url delegate:nil];
}
-(UInt32)recommendedDrawingFrameRate {
UInt32 val = 1;
if(_waveformResolution > 0){
val = (UInt32) _totalFrames / _waveformResolution;
if(val > 1)
--val;
}
return MAX(1, val);
//------------------------------------------------------------------------------
- (instancetype)initWithURL:(NSURL *)url
delegate:(id<EZAudioFileDelegate>)delegate
{
return [self initWithURL:url
delegate:delegate
clientFormat:[self.class defaultClientFormat]];
}
#pragma mark - Cleanup
-(void)dealloc {
if (_waveformData){
free(_waveformData);
_waveformData = NULL;
}
// if (_floatBuffers){
// free(_floatBuffers);
// _floatBuffers = NULL;
// }
_frameIndex = 0;
_waveformFrameRate = 0;
_waveformTotalBuffers = 0;
if (_audioFile){
[EZAudioUtilities checkResult:ExtAudioFileDispose(_audioFile)
operation:"Failed to dispose of audio file"];
}
//------------------------------------------------------------------------------
- (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 - 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];
}
//------------------------------------------------------------------------------
@end
+1 -1
View File
@@ -60,7 +60,7 @@ OSStatus EZAudioFloatConverterCallback(AudioConverterRef inAudioConv
AudioBufferList *sourceBuffer = (AudioBufferList *)inUserData;
memcpy(ioData,
sourceBuffer,
sizeof(AudioBufferList) + (sourceBuffer->mNumberBuffers - 1)*sizeof(AudioBuffer));
sizeof(AudioBufferList) + (sourceBuffer->mNumberBuffers - 1) * sizeof(AudioBuffer));
return noErr;
}
+12 -8
View File
@@ -77,7 +77,8 @@
self = [super init];
if(self){
[self _configureAudioPlayer];
self.audioFile = [EZAudioFile audioFileWithURL:url andDelegate:self];
self.audioFile = [[EZAudioFile alloc] initWithURL:url];
self.audioFile.delegate = self;
self.audioPlayerDelegate = audioPlayerDelegate;
}
return self;
@@ -148,7 +149,7 @@
leftMin:0
leftMax:self.audioFile.totalFrames
rightMin:0
rightMax:self.audioFile.totalDuration];
rightMax:self.audioFile.duration];
}
-(BOOL)endOfFile {
@@ -171,7 +172,7 @@
-(float)totalDuration {
NSAssert(_audioFile,@"No audio file to perform the seek on, check that EZAudioFile is not nil");
return _audioFile.totalDuration;
return _audioFile.duration;
}
-(SInt64)totalFrames {
@@ -187,17 +188,18 @@
#pragma mark - Setters
-(void)setAudioFile:(EZAudioFile *)audioFile {
if (_audioFile){
_audioFile.audioFileDelegate = nil;
_audioFile.delegate = nil;
}
_eof = NO;
_audioFile = [EZAudioFile audioFileWithURL:audioFile.url andDelegate:self];
_audioFile = [EZAudioFile audioFileWithURL:audioFile.url];
_audioFile.delegate = self;
NSAssert(_output,@"No output was found, this should by default be the EZOutput shared instance");
[_output setAudioStreamBasicDescription:self.audioFile.clientFormat];
[_output setInputFormat:self.audioFile.clientFormat];
}
-(void)setOutput:(EZOutput*)output {
_output = output;
_output.outputDataSource = self;
_output.dataSource = self;
}
#pragma mark - Methods
@@ -276,9 +278,10 @@ withNumberOfChannels:(UInt32)numberOfChannels {
}
#pragma mark - EZOutputDataSource
-(void) output:(EZOutput *)output
-(OSStatus) output:(EZOutput *)output
shouldFillAudioBufferList:(AudioBufferList *)audioBufferList
withNumberOfFrames:(UInt32)frames
timestamp:(const AudioTimeStamp *)timestamp
{
if (self.audioFile)
{
@@ -292,6 +295,7 @@ withNumberOfChannels:(UInt32)numberOfChannels {
[self seekToFrame:0];
}
}
return noErr;
}
@end
+1
View File
@@ -146,6 +146,7 @@ UInt32 const EZAudioPlotDefaultMaxHistoryBufferLength = 8192;
#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];
+11
View File
@@ -46,6 +46,17 @@ typedef struct
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
//------------------------------------------------------------------------------
+5 -4
View File
@@ -36,7 +36,7 @@
//------------------------------------------------------------------------------
/**
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 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 audio data itself is sent back to the delegate in various forms:
@@ -55,10 +55,9 @@
///-----------------------------------------------------------
/**
Called anytime the input device changes on an `EZMicrophone` instance. Mac only.
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.
@param notification Incase the device changed because of a notification (like from AVAudioSession) then we provide that notification to give the full context of the change.
*/
- (void)microphone:(EZMicrophone *)microphone changedDevice:(EZAudioDevice *)device;
@@ -77,7 +76,7 @@
///-----------------------------------------------------------
/**
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.
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 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).
@@ -304,7 +303,9 @@
*/
- (AudioUnit *)audioUnit;
//------------------------------------------------------------------------------
#pragma mark - Setters
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Customizing The Microphone Stream Format
+5 -3
View File
@@ -535,21 +535,23 @@ static OSStatus EZAudioMicrophoneCallback(void *inRefCon,
- (void)setOutput:(EZOutput *)output
{
_output = output;
[_output setAudioStreamBasicDescription:self.audioStreamBasicDescription];
_output.outputDataSource = self;
_output.inputFormat = self.audioStreamBasicDescription;
_output.dataSource = self;
}
//------------------------------------------------------------------------------
#pragma mark - EZOutputDataSource
//------------------------------------------------------------------------------
- (void) output:(EZOutput *)output
- (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;
}
//------------------------------------------------------------------------------
+249 -74
View File
@@ -30,72 +30,102 @@
#import <AudioUnit/AudioUnit.h>
#endif
#import "TPCircularBuffer.h"
@class EZAudioDevice;
@class EZOutput;
//------------------------------------------------------------------------------
#pragma mark - Constants
//------------------------------------------------------------------------------
FOUNDATION_EXPORT UInt32 const EZOutputMaximumFramesPerSlice;
FOUNDATION_EXPORT Float64 const EZOutputDefaultSampleRate;
//------------------------------------------------------------------------------
#pragma mark - EZOutputDataSource
//------------------------------------------------------------------------------
/**
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:`
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 Pulling The Audio Data
/// @name Providing 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;
@required
/**
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.
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.
@return A pointer to the AudioBufferList structure holding the audio data. If nil or NULL, will output silence.
@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.
*/
-(void) output:(EZOutput *)output
shouldFillAudioBufferList:(AudioBufferList*)audioBufferList
withNumberOfFrames:(UInt32)frames;
- (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.
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 - Properties
/**
The EZOutputDataSource that provides the required AudioBufferList to the output callback function
*/
@property (nonatomic,assign) id<EZOutputDataSource>outputDataSource;
//------------------------------------------------------------------------------
#pragma mark - Initializers
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Initializers
///-----------------------------------------------------------
@@ -105,29 +135,38 @@ inNumberFrames:(UInt32)inNumberFrames
@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;
- (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 audioStreamBasicDescription The AudioStreamBasicDescription of the EZOutput.
@warning AudioStreamBasicDescriptions that are invalid will cause the EZOutput to fail to initialize
@param inputFormat The AudioStreamBasicDescription of the EZOutput.
@warning AudioStreamBasicDescription input formats must be linear PCM!
@return A newly created instance of the EZOutput class.
*/
-(id) initWithDataSource:(id<EZOutputDataSource>)dataSource
withAudioStreamBasicDescription:(AudioStreamBasicDescription)audioStreamBasicDescription;
- (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.
*/
+(EZOutput*)outputWithDataSource:(id<EZOutputDataSource>)dataSource;
+ (instancetype)outputWithDataSource:(id<EZOutputDataSource>)dataSource;
/**
Class method to create a new instance of the EZOutput and allows the caller to specify an EZOutputDataSource.
@@ -136,10 +175,13 @@ inNumberFrames:(UInt32)inNumberFrames
@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;
+ (instancetype)outputWithDataSource:(id<EZOutputDataSource>)dataSource
inputFormat:(AudioStreamBasicDescription)inputFormat;
//------------------------------------------------------------------------------
#pragma mark - Singleton
//------------------------------------------------------------------------------
///-----------------------------------------------------------
/// @name Shared Instance
///-----------------------------------------------------------
@@ -148,9 +190,123 @@ inNumberFrames:(UInt32)inNumberFrames
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;
+ (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
//------------------------------------------------------------------------------
#pragma mark - Events
///-----------------------------------------------------------
/// @name Starting/Stopping The Output
///-----------------------------------------------------------
@@ -158,44 +314,63 @@ inNumberFrames:(UInt32)inNumberFrames
/**
Starts pulling audio data from the EZOutputDataSource to the default device output.
*/
-(void)startPlayback;
- (void)startPlayback;
///-----------------------------------------------------------
/**
Stops pulling audio data from the EZOutputDataSource to the default device output.
*/
-(void)stopPlayback;
- (void)stopPlayback;
//------------------------------------------------------------------------------
#pragma mark - Subclass
//------------------------------------------------------------------------------
#pragma mark - Getters
///-----------------------------------------------------------
/// @name Getting The Output Audio Format
/// @name Subclass
///-----------------------------------------------------------
/**
Provides the AudioStreamBasicDescription structure containing the format of the microphone's audio.
@return An AudioStreamBasicDescription structure describing the format of the microphone's audio.
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`.
*/
-(AudioStreamBasicDescription)audioStreamBasicDescription;
- (OSStatus)connectOutputOfSourceNode:(AUNode)sourceNode
sourceNodeOutputBus:(UInt32)sourceNodeOutputBus
toDestinationNode:(AUNode)destinationNode
destinationNodeInputBus:(UInt32)destinationNodeInputBus
inGraph:(AUGraph)graph;
///-----------------------------------------------------------
/// @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
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.
*/
-(BOOL)isPlaying;
- (AudioStreamBasicDescription)defaultClientFormat;
#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.
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.
*/
-(void)setAudioStreamBasicDescription:(AudioStreamBasicDescription)asbd;
- (AudioStreamBasicDescription)defaultInputFormat;
@end
//------------------------------------------------------------------------------
/**
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
+688 -305
View File
File diff suppressed because it is too large Load Diff
@@ -21,7 +21,6 @@
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
66755A301B3B790D0013E67E /* AEFloatConverter.m in Sources */ = {isa = PBXBuildFile; fileRef = 66755A0C1B3B790D0013E67E /* AEFloatConverter.m */; };
66755A311B3B790D0013E67E /* EZAudio.m in Sources */ = {isa = PBXBuildFile; fileRef = 66755A0E1B3B790D0013E67E /* EZAudio.m */; };
66755A321B3B790D0013E67E /* EZAudioDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = 66755A101B3B790D0013E67E /* EZAudioDevice.m */; };
66755A331B3B790D0013E67E /* EZAudioDisplayLink.m in Sources */ = {isa = PBXBuildFile; fileRef = 66755A121B3B790D0013E67E /* EZAudioDisplayLink.m */; };
@@ -72,8 +71,6 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
66755A0B1B3B790D0013E67E /* AEFloatConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEFloatConverter.h; sourceTree = "<group>"; };
66755A0C1B3B790D0013E67E /* AEFloatConverter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AEFloatConverter.m; sourceTree = "<group>"; };
66755A0D1B3B790D0013E67E /* EZAudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudio.h; sourceTree = "<group>"; };
66755A0E1B3B790D0013E67E /* EZAudio.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudio.m; sourceTree = "<group>"; };
66755A0F1B3B790D0013E67E /* EZAudioDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioDevice.h; sourceTree = "<group>"; };
@@ -168,8 +165,6 @@
66755A0A1B3B790D0013E67E /* EZAudio */ = {
isa = PBXGroup;
children = (
66755A0B1B3B790D0013E67E /* AEFloatConverter.h */,
66755A0C1B3B790D0013E67E /* AEFloatConverter.m */,
66755A0D1B3B790D0013E67E /* EZAudio.h */,
66755A0E1B3B790D0013E67E /* EZAudio.m */,
66755A0F1B3B790D0013E67E /* EZAudioDevice.h */,
@@ -433,7 +428,6 @@
66755A3A1B3B790D0013E67E /* EZAudioPlotGLKViewController.m in Sources */,
66755A341B3B790D0013E67E /* EZAudioFile.m in Sources */,
66755A3D1B3B790D0013E67E /* EZOutput.m in Sources */,
66755A301B3B790D0013E67E /* AEFloatConverter.m in Sources */,
66755A3B1B3B790D0013E67E /* EZAudioUtilities.m in Sources */,
66755A3E1B3B790D0013E67E /* EZPlot.m in Sources */,
94373038185B931C00F315F0 /* AppDelegate.m in Sources */,
@@ -7,7 +7,6 @@
objects = {
/* Begin PBXBuildFile section */
66755B861B3B79380013E67E /* AEFloatConverter.m in Sources */ = {isa = PBXBuildFile; fileRef = 66755B621B3B79380013E67E /* AEFloatConverter.m */; };
66755B871B3B79380013E67E /* EZAudio.m in Sources */ = {isa = PBXBuildFile; fileRef = 66755B641B3B79380013E67E /* EZAudio.m */; };
66755B881B3B79380013E67E /* EZAudioDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = 66755B661B3B79380013E67E /* EZAudioDevice.m */; };
66755B891B3B79380013E67E /* EZAudioDisplayLink.m in Sources */ = {isa = PBXBuildFile; fileRef = 66755B681B3B79380013E67E /* EZAudioDisplayLink.m */; };
@@ -59,8 +58,6 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
66755B611B3B79380013E67E /* AEFloatConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEFloatConverter.h; sourceTree = "<group>"; };
66755B621B3B79380013E67E /* AEFloatConverter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AEFloatConverter.m; sourceTree = "<group>"; };
66755B631B3B79380013E67E /* EZAudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudio.h; sourceTree = "<group>"; };
66755B641B3B79380013E67E /* EZAudio.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudio.m; sourceTree = "<group>"; };
66755B651B3B79380013E67E /* EZAudioDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioDevice.h; sourceTree = "<group>"; };
@@ -157,8 +154,6 @@
66755B601B3B79380013E67E /* EZAudio */ = {
isa = PBXGroup;
children = (
66755B611B3B79380013E67E /* AEFloatConverter.h */,
66755B621B3B79380013E67E /* AEFloatConverter.m */,
66755B631B3B79380013E67E /* EZAudio.h */,
66755B641B3B79380013E67E /* EZAudio.m */,
66755B651B3B79380013E67E /* EZAudioDevice.h */,
@@ -406,7 +401,6 @@
66755B901B3B79380013E67E /* EZAudioPlotGLKViewController.m in Sources */,
66755B8A1B3B79380013E67E /* EZAudioFile.m in Sources */,
66755B931B3B79380013E67E /* EZOutput.m in Sources */,
66755B861B3B79380013E67E /* AEFloatConverter.m in Sources */,
66755B911B3B79380013E67E /* EZAudioUtilities.m in Sources */,
66755B941B3B79380013E67E /* EZPlot.m in Sources */,
9417A90A1871492100D9D37B /* AppDelegate.m in Sources */,
@@ -7,7 +7,6 @@
objects = {
/* Begin PBXBuildFile section */
66755A691B3B79130013E67E /* AEFloatConverter.m in Sources */ = {isa = PBXBuildFile; fileRef = 66755A451B3B79120013E67E /* AEFloatConverter.m */; };
66755A6A1B3B79130013E67E /* EZAudio.m in Sources */ = {isa = PBXBuildFile; fileRef = 66755A471B3B79130013E67E /* EZAudio.m */; };
66755A6B1B3B79130013E67E /* EZAudioDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = 66755A491B3B79130013E67E /* EZAudioDevice.m */; };
66755A6C1B3B79130013E67E /* EZAudioDisplayLink.m in Sources */ = {isa = PBXBuildFile; fileRef = 66755A4B1B3B79130013E67E /* EZAudioDisplayLink.m */; };
@@ -58,8 +57,6 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
66755A441B3B79120013E67E /* AEFloatConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEFloatConverter.h; sourceTree = "<group>"; };
66755A451B3B79120013E67E /* AEFloatConverter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AEFloatConverter.m; sourceTree = "<group>"; };
66755A461B3B79130013E67E /* EZAudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudio.h; sourceTree = "<group>"; };
66755A471B3B79130013E67E /* EZAudio.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudio.m; sourceTree = "<group>"; };
66755A481B3B79130013E67E /* EZAudioDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioDevice.h; sourceTree = "<group>"; };
@@ -154,8 +151,6 @@
66755A431B3B79120013E67E /* EZAudio */ = {
isa = PBXGroup;
children = (
66755A441B3B79120013E67E /* AEFloatConverter.h */,
66755A451B3B79120013E67E /* AEFloatConverter.m */,
66755A461B3B79130013E67E /* EZAudio.h */,
66755A471B3B79130013E67E /* EZAudio.m */,
66755A481B3B79130013E67E /* EZAudioDevice.h */,
@@ -402,7 +397,6 @@
66755A731B3B79130013E67E /* EZAudioPlotGLKViewController.m in Sources */,
66755A6D1B3B79130013E67E /* EZAudioFile.m in Sources */,
66755A761B3B79130013E67E /* EZOutput.m in Sources */,
66755A691B3B79130013E67E /* AEFloatConverter.m in Sources */,
66755A741B3B79130013E67E /* EZAudioUtilities.m in Sources */,
66755A771B3B79130013E67E /* EZPlot.m in Sources */,
94056DA3185BB0BC00EB94BA /* main.m in Sources */,
@@ -7,7 +7,6 @@
objects = {
/* Begin PBXBuildFile section */
66755B4D1B3B79310013E67E /* AEFloatConverter.m in Sources */ = {isa = PBXBuildFile; fileRef = 66755B291B3B79310013E67E /* AEFloatConverter.m */; };
66755B4E1B3B79310013E67E /* EZAudio.m in Sources */ = {isa = PBXBuildFile; fileRef = 66755B2B1B3B79310013E67E /* EZAudio.m */; };
66755B4F1B3B79310013E67E /* EZAudioDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = 66755B2D1B3B79310013E67E /* EZAudioDevice.m */; };
66755B501B3B79310013E67E /* EZAudioDisplayLink.m in Sources */ = {isa = PBXBuildFile; fileRef = 66755B2F1B3B79310013E67E /* EZAudioDisplayLink.m */; };
@@ -58,8 +57,6 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
66755B281B3B79310013E67E /* AEFloatConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEFloatConverter.h; sourceTree = "<group>"; };
66755B291B3B79310013E67E /* AEFloatConverter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AEFloatConverter.m; sourceTree = "<group>"; };
66755B2A1B3B79310013E67E /* EZAudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudio.h; sourceTree = "<group>"; };
66755B2B1B3B79310013E67E /* EZAudio.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudio.m; sourceTree = "<group>"; };
66755B2C1B3B79310013E67E /* EZAudioDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioDevice.h; sourceTree = "<group>"; };
@@ -154,8 +151,6 @@
66755B271B3B79310013E67E /* EZAudio */ = {
isa = PBXGroup;
children = (
66755B281B3B79310013E67E /* AEFloatConverter.h */,
66755B291B3B79310013E67E /* AEFloatConverter.m */,
66755B2A1B3B79310013E67E /* EZAudio.h */,
66755B2B1B3B79310013E67E /* EZAudio.m */,
66755B2C1B3B79310013E67E /* EZAudioDevice.h */,
@@ -402,7 +397,6 @@
66755B571B3B79310013E67E /* EZAudioPlotGLKViewController.m in Sources */,
66755B511B3B79310013E67E /* EZAudioFile.m in Sources */,
66755B5A1B3B79310013E67E /* EZOutput.m in Sources */,
66755B4D1B3B79310013E67E /* AEFloatConverter.m in Sources */,
66755B581B3B79310013E67E /* EZAudioUtilities.m in Sources */,
66755B5B1B3B79310013E67E /* EZPlot.m in Sources */,
941D71CB1864C457007D52D8 /* AppDelegate.m in Sources */,
@@ -30,7 +30,7 @@
*/
#import "EZAudio.h"
@interface PassThroughViewController : NSViewController <EZMicrophoneDelegate,EZOutputDataSource>
@interface PassThroughViewController : NSViewController <EZMicrophoneDelegate>
//------------------------------------------------------------------------------
#pragma mark - Components
@@ -8,7 +8,6 @@
/* Begin PBXBuildFile section */
6628E2351B3A121A00020E56 /* simple-drum-beat.wav in Resources */ = {isa = PBXBuildFile; fileRef = 6628E2341B3A121A00020E56 /* simple-drum-beat.wav */; };
66755AA21B3B791C0013E67E /* AEFloatConverter.m in Sources */ = {isa = PBXBuildFile; fileRef = 66755A7E1B3B791C0013E67E /* AEFloatConverter.m */; };
66755AA31B3B791C0013E67E /* EZAudio.m in Sources */ = {isa = PBXBuildFile; fileRef = 66755A801B3B791C0013E67E /* EZAudio.m */; };
66755AA41B3B791C0013E67E /* EZAudioDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = 66755A821B3B791C0013E67E /* EZAudioDevice.m */; };
66755AA51B3B791C0013E67E /* EZAudioDisplayLink.m in Sources */ = {isa = PBXBuildFile; fileRef = 66755A841B3B791C0013E67E /* EZAudioDisplayLink.m */; };
@@ -60,8 +59,6 @@
/* Begin PBXFileReference section */
6628E2341B3A121A00020E56 /* simple-drum-beat.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; name = "simple-drum-beat.wav"; path = "../../../simple-drum-beat.wav"; sourceTree = "<group>"; };
66755A7D1B3B791C0013E67E /* AEFloatConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEFloatConverter.h; sourceTree = "<group>"; };
66755A7E1B3B791C0013E67E /* AEFloatConverter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AEFloatConverter.m; sourceTree = "<group>"; };
66755A7F1B3B791C0013E67E /* EZAudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudio.h; sourceTree = "<group>"; };
66755A801B3B791C0013E67E /* EZAudio.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudio.m; sourceTree = "<group>"; };
66755A811B3B791C0013E67E /* EZAudioDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioDevice.h; sourceTree = "<group>"; };
@@ -156,8 +153,6 @@
66755A7C1B3B791C0013E67E /* EZAudio */ = {
isa = PBXGroup;
children = (
66755A7D1B3B791C0013E67E /* AEFloatConverter.h */,
66755A7E1B3B791C0013E67E /* AEFloatConverter.m */,
66755A7F1B3B791C0013E67E /* EZAudio.h */,
66755A801B3B791C0013E67E /* EZAudio.m */,
66755A811B3B791C0013E67E /* EZAudioDevice.h */,
@@ -406,7 +401,6 @@
66755AAC1B3B791C0013E67E /* EZAudioPlotGLKViewController.m in Sources */,
66755AA61B3B791C0013E67E /* EZAudioFile.m in Sources */,
66755AAF1B3B791C0013E67E /* EZOutput.m in Sources */,
66755AA21B3B791C0013E67E /* AEFloatConverter.m in Sources */,
66755AAD1B3B791C0013E67E /* EZAudioUtilities.m in Sources */,
66755AB01B3B791C0013E67E /* EZPlot.m in Sources */,
94056F0E185BD83400EB94BA /* AppDelegate.m in Sources */,
@@ -36,13 +36,21 @@
/**
Using the EZOutputDataSource to provide output data to the EZOutput component.
*/
@interface PlayFileViewController : NSViewController <NSOpenSavePanelDelegate,EZAudioFileDelegate,EZOutputDataSource>
@interface PlayFileViewController : NSViewController <NSOpenSavePanelDelegate,
EZAudioFileDelegate,
EZOutputDataSource,
EZOutputDelegate>
#pragma mark - Components
/**
The EZAudioFile representing of the currently selected audio file
*/
@property (nonatomic,strong) EZAudioFile *audioFile;
@property (nonatomic, strong) EZAudioFile *audioFile;
/**
The EZOutput component used to output the audio file's audio data.
*/
@property (nonatomic, strong) EZOutput *output;
/**
The CoreGraphics based audio plot
@@ -56,7 +64,7 @@
@property (nonatomic, weak) IBOutlet NSTextField *filePathLabel;
/**
<#Description#>
A label to display the audio file's current position.
*/
@property (nonatomic, weak) IBOutlet NSTextField *positionLabel;
@@ -66,36 +74,36 @@
@property (nonatomic, weak) IBOutlet NSSlider *positionSlider;
/**
<#Description#>
A label to display the value of the rolling history length of the audio plot.
*/
@property (nonatomic, weak) IBOutlet NSTextField *rollingHistoryLengthLabel;
/**
<#Description#>
A slider to adjust the rolling history length of the audio plot.
*/
@property (nonatomic, weak) IBOutlet NSSlider *rollingHistoryLengthSlider;
/**
A slider to adjust the sample rate.
A slider to adjust the volume.
*/
@property (nonatomic, weak) IBOutlet NSSlider *sampleRateSlider;
@property (nonatomic, weak) IBOutlet NSSlider *volumeSlider;
/**
A slider to adjust the sample rate.
A label to display the volume of the audio plot.
*/
@property (nonatomic, weak) IBOutlet NSTextField *sampleRateLabel;
@property (nonatomic, weak) IBOutlet NSTextField *volumeLabel;
/**
A BOOL indicating whether or not we've reached the end of the file
*/
@property (nonatomic,assign) BOOL eof;
#pragma mark - Actions
/**
Changes the sampling frequency on the output unit
The microphone pop up button (contains the menu for choosing a microphone input)
*/
-(IBAction)changeOutputSamplingFrequency:(id)sender;
@property (nonatomic, weak) IBOutlet NSPopUpButton *outputDevicePopUpButton;
#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)
*/
@@ -106,6 +114,11 @@
*/
- (IBAction)changeRollingHistoryLength:(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.
*/
@@ -52,11 +52,24 @@
// Mirror
self.audioPlot.shouldMirror = YES;
//
// Create EZOutput to play audio data
//
self.output = [EZOutput outputWithDataSource:self];
self.output.delegate = self;
//
// Reload the menu for the output device selector popup button
//
[self reloadOutputDevicePopUpButtonMenu];
//
// Configure UI components
//
self.rollingHistoryLengthSlider.intValue = self.audioPlot.rollingHistoryLength;
self.rollingHistoryLengthLabel.intValue = self.audioPlot.rollingHistoryLength;
self.volumeSlider.floatValue = [self.output volume];
self.volumeLabel.floatValue = [self.output volume];
self.rollingHistoryLengthSlider.intValue = [self.audioPlot rollingHistoryLength];
self.rollingHistoryLengthLabel.intValue = [self.audioPlot rollingHistoryLength];
//
// Try opening the sample file
@@ -68,6 +81,14 @@
#pragma mark - Actions
//------------------------------------------------------------------------------
- (void)changedOutput:(NSMenuItem *)item
{
EZAudioDevice *device = [item representedObject];
[self.output setDevice:device];
}
//------------------------------------------------------------------------------
- (void)changePlotType:(id)sender
{
NSInteger selectedSegment = [sender selectedSegment];
@@ -86,13 +107,11 @@
//------------------------------------------------------------------------------
- (void)changeOutputSamplingFrequency:(id)sender
- (void)changeVolume:(id)sender
{
float sampleRate = ((NSSlider *)sender).floatValue;
AudioStreamBasicDescription asbd = [[EZOutput sharedOutput] audioStreamBasicDescription];
asbd.mSampleRate = sampleRate;
[[EZOutput sharedOutput] setAudioStreamBasicDescription:asbd];
self.sampleRateLabel.floatValue = sampleRate;
float value = [(NSSlider *)sender floatValue];
[self.output setVolume:value];
self.volumeLabel.floatValue = value;
}
//------------------------------------------------------------------------------
@@ -123,7 +142,7 @@
-(void)play:(id)sender
{
if (![[EZOutput sharedOutput] isPlaying])
if (![self.output isPlaying])
{
if (self.eof)
{
@@ -133,13 +152,11 @@
{
self.audioPlot.plotType = EZPlotTypeRolling;
}
[EZOutput sharedOutput].outputDataSource = self;
[[EZOutput sharedOutput] startPlayback];
[self.output startPlayback];
}
else
{
[EZOutput sharedOutput].outputDataSource = nil;
[[EZOutput sharedOutput] stopPlayback];
[self.output stopPlayback];
}
}
@@ -191,7 +208,7 @@
//
// Stop playback
//
[[EZOutput sharedOutput] stopPlayback];
[self.output stopPlayback];
//
// Clear the audio plot
@@ -201,7 +218,7 @@
//
// Load the audio file and customize the UI
//
self.audioFile = [EZAudioFile audioFileWithURL:filePathURL andDelegate:self];
self.audioFile = [EZAudioFile audioFileWithURL:filePathURL delegate:self];
self.eof = NO;
self.filePathLabel.stringValue = filePathURL.lastPathComponent;
self.positionSlider.minValue = 0.0f;
@@ -212,10 +229,7 @@
//
// Set the client format from the EZAudioFile on the output
//
Float64 sampleRate = self.audioFile.clientFormat.mSampleRate;
self.sampleRateSlider.floatValue = sampleRate;
self.sampleRateLabel.floatValue = sampleRate;
[[EZOutput sharedOutput] setAudioStreamBasicDescription:self.audioFile.clientFormat];
[self.output setInputFormat:self.audioFile.clientFormat];
//
// Change back to a buffer plot, but mirror and fill the waveform
@@ -223,42 +237,63 @@
self.audioPlot.plotType = EZPlotTypeBuffer;
self.audioPlot.shouldFill = YES;
self.audioPlot.shouldMirror = YES;
[self.audioPlot clear];
//
// Plot the whole waveform
//
__weak typeof (self) weakSelf = self;
[self.audioFile getWaveformDataWithCompletionBlock:^(float *waveformData,
UInt32 length)
[self.audioFile getWaveformDataWithNumberOfPoints:256
completion:^(float **waveformData,
int length)
{
[weakSelf.audioPlot updateBuffer:waveformData
[weakSelf.audioPlot updateBuffer:waveformData[0]
withBufferSize:length];
}];
}
//------------------------------------------------------------------------------
- (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.output device]])
{
defaultOutputDeviceItem = item;
}
}
self.outputDevicePopUpButton.menu = menu;
//
// Set the selected device to the current selection on the
// microphone input popup button
//
[self.outputDevicePopUpButton selectItem:defaultOutputDeviceItem];
}
//------------------------------------------------------------------------------
#pragma mark - EZAudioFileDelegate
//------------------------------------------------------------------------------
-(void) audioFile:(EZAudioFile *)audioFile
readAudio:(float **)buffer
withBufferSize:(UInt32)bufferSize
withNumberOfChannels:(UInt32)numberOfChannels
-(void)audioFile:(EZAudioFile *)audioFile updatedPosition:(SInt64)framePosition
{
if ([[EZOutput sharedOutput] isPlaying])
{
__weak typeof (self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf.audioPlot updateBuffer:buffer[0]
withBufferSize:bufferSize];
});
}
}
//------------------------------------------------------------------------------
-(void)audioFile:(EZAudioFile *)audioFile
updatedPosition:(SInt64)framePosition {
__weak typeof (self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
if (![weakSelf.positionSlider.cell isHighlighted])
@@ -273,9 +308,10 @@
#pragma mark - EZOutputDataSource
//------------------------------------------------------------------------------
-(void) output:(EZOutput *)output
-(OSStatus) output:(EZOutput *)output
shouldFillAudioBufferList:(AudioBufferList *)audioBufferList
withNumberOfFrames:(UInt32)frames
timestamp:(const AudioTimeStamp *)timestamp
{
if (self.audioFile)
{
@@ -289,6 +325,23 @@
[self seekToFrame:0];
}
}
return noErr;
}
//------------------------------------------------------------------------------
#pragma mark - EZOutputDelegate
//------------------------------------------------------------------------------
- (void) output:(EZOutput *)output
playedAudio:(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];
});
}
//------------------------------------------------------------------------------
@@ -9,15 +9,16 @@
<connections>
<outlet property="audioPlot" destination="Lz1-Gs-1lD" id="V5w-yH-ZVR"/>
<outlet property="filePathLabel" destination="0eT-7c-7fJ" id="IGv-mA-5Hw"/>
<outlet property="outputDevicePopUpButton" destination="0LV-Bi-dGz" id="QTQ-qq-Ro8"/>
<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="sampleRateLabel" destination="3ul-3w-l3S" id="5CA-Au-tFa"/>
<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"/>
@@ -27,7 +28,7 @@
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="Lz1-Gs-1lD" customClass="EZAudioPlot">
<rect key="frame" x="0.0" y="0.0" width="480" height="210"/>
<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"/>
@@ -74,22 +75,22 @@
</connections>
</segmentedControl>
<slider verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="CFP-v0-TzQ">
<rect key="frame" x="117" y="242" width="330" height="20"/>
<sliderCell key="cell" alignment="left" maxValue="100" doubleValue="9.3380614657210401" tickMarkPosition="above" sliderType="linear" id="gPc-pN-dmP"/>
<rect key="frame" x="117" y="242" width="269" 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="330" height="20"/>
<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="85" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="left" title="Sample Rate:" id="GAa-Hp-OlV">
<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"/>
@@ -112,9 +113,12 @@
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="3ul-3w-l3S">
<rect key="frame" x="452" y="269" width="12" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="0" id="vlK-Hb-Yca">
<numberFormatter key="formatter" formatterBehavior="custom10_4" minimumIntegerDigits="0" maximumIntegerDigits="42" id="bBU-vS-tEB">
<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>
@@ -125,77 +129,109 @@
</textFieldCell>
</textField>
<slider verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="rRH-oS-VV3">
<rect key="frame" x="117" y="267" width="330" height="20"/>
<sliderCell key="cell" state="on" alignment="left" minValue="8000" maxValue="88200" doubleValue="44100" tickMarkPosition="above" sliderType="linear" id="xbX-Ce-da5"/>
<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"/>
<connections>
<action selector="changeOutputSamplingFrequency:" target="-2" id="yWM-Ei-ztA"/>
<action selector="changeVolume:" target="-2" id="iKx-7d-34D"/>
</connections>
</slider>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="KYm-Io-VNv">
<rect key="frame" x="452" y="244" width="12" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" 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="titleBar" 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="vKe-ey-hXI">
<rect key="frame" x="451" y="219" width="12" height="16"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="0" id="JiA-3H-vb2">
<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="titleBar" size="12"/>
<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>
</subviews>
<constraints>
<constraint firstItem="CFP-v0-TzQ" firstAttribute="baseline" secondItem="Fw5-pm-4w0" secondAttribute="baseline" id="5Iy-Vv-CQo"/>
<constraint firstItem="vKe-ey-hXI" firstAttribute="leading" secondItem="vj5-qT-JkR" secondAttribute="trailing" constant="8" symbolic="YES" id="7ZR-wn-RFM"/>
<constraint firstItem="vj5-qT-JkR" firstAttribute="trailing" secondItem="CFP-v0-TzQ" secondAttribute="trailing" id="7rr-13-nCn"/>
<constraint firstItem="vKe-ey-hXI" firstAttribute="baseline" secondItem="vj5-qT-JkR" secondAttribute="baseline" id="9n9-7Q-Uc5"/>
<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 firstAttribute="bottom" secondItem="Lz1-Gs-1lD" secondAttribute="bottom" id="Cdp-xg-Udu"/>
<constraint firstItem="rRH-oS-VV3" firstAttribute="baseline" secondItem="3ul-3w-l3S" secondAttribute="baseline" id="E1v-Nu-5O1"/>
<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="7AB-VA-xL3" firstAttribute="baseline" secondItem="rRH-oS-VV3" secondAttribute="baseline" id="LQV-fR-ji0"/>
<constraint firstItem="CFP-v0-TzQ" firstAttribute="leading" secondItem="vj5-qT-JkR" secondAttribute="leading" id="May-qJ-5nB"/>
<constraint firstItem="3ul-3w-l3S" firstAttribute="trailing" secondItem="bZW-tA-C61" secondAttribute="trailing" id="Opz-An-TkJ"/>
<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="KYm-Io-VNv" firstAttribute="baseline" secondItem="CFP-v0-TzQ" secondAttribute="baseline" id="agk-Cm-yAm"/>
<constraint firstItem="vj5-qT-JkR" firstAttribute="trailing" secondItem="CFP-v0-TzQ" secondAttribute="trailing" id="URd-7z-JBO"/>
<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="vj5-qT-JkR" firstAttribute="baseline" secondItem="Aa9-nc-WHJ" secondAttribute="baseline" id="cBS-XB-jof"/>
<constraint firstAttribute="trailing" secondItem="KYm-Io-VNv" secondAttribute="trailing" constant="18" id="cRX-mp-xiq"/>
<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 firstAttribute="trailing" secondItem="vKe-ey-hXI" secondAttribute="trailing" constant="19" id="kgc-wp-ndy"/>
<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="Aa9-nc-WHJ" secondAttribute="bottom" constant="9" id="oZE-Hx-FR4"/>
<constraint firstItem="rRH-oS-VV3" firstAttribute="trailing" secondItem="CFP-v0-TzQ" secondAttribute="trailing" id="oa1-ct-9bT"/>
<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="vj5-qT-JkR" firstAttribute="leading" secondItem="Aa9-nc-WHJ" secondAttribute="trailing" constant="9" id="qa7-m8-7wP"/>
<constraint firstAttribute="trailing" secondItem="3ul-3w-l3S" secondAttribute="trailing" constant="18" id="rtO-ix-cUt"/>
<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="3ul-3w-l3S" firstAttribute="leading" secondItem="KYm-Io-VNv" secondAttribute="leading" id="tKc-D3-J6f"/>
<constraint firstItem="OQp-Lr-dlS" firstAttribute="top" secondItem="2Ma-jj-U3z" secondAttribute="bottom" constant="12" symbolic="YES" id="tkR-bR-msf"/>
<constraint firstItem="3ul-3w-l3S" firstAttribute="leading" secondItem="rRH-oS-VV3" secondAttribute="trailing" constant="9" id="udS-FO-wJL"/>
<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-oS-VV3" firstAttribute="leading" secondItem="CFP-v0-TzQ" secondAttribute="leading" id="yJz-Qg-823"/>
<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"/>
</constraints>
<point key="canvasLocation" x="191" y="409"/>
</customView>
@@ -7,7 +7,6 @@
objects = {
/* Begin PBXBuildFile section */
66755ADB1B3B79230013E67E /* AEFloatConverter.m in Sources */ = {isa = PBXBuildFile; fileRef = 66755AB71B3B79220013E67E /* AEFloatConverter.m */; };
66755ADC1B3B79230013E67E /* EZAudio.m in Sources */ = {isa = PBXBuildFile; fileRef = 66755AB91B3B79220013E67E /* EZAudio.m */; };
66755ADD1B3B79230013E67E /* EZAudioDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = 66755ABB1B3B79220013E67E /* EZAudioDevice.m */; };
66755ADE1B3B79230013E67E /* EZAudioDisplayLink.m in Sources */ = {isa = PBXBuildFile; fileRef = 66755ABD1B3B79220013E67E /* EZAudioDisplayLink.m */; };
@@ -59,8 +58,6 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
66755AB61B3B79220013E67E /* AEFloatConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEFloatConverter.h; sourceTree = "<group>"; };
66755AB71B3B79220013E67E /* AEFloatConverter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AEFloatConverter.m; sourceTree = "<group>"; };
66755AB81B3B79220013E67E /* EZAudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudio.h; sourceTree = "<group>"; };
66755AB91B3B79220013E67E /* EZAudio.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudio.m; sourceTree = "<group>"; };
66755ABA1B3B79220013E67E /* EZAudioDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioDevice.h; sourceTree = "<group>"; };
@@ -157,8 +154,6 @@
66755AB51B3B79220013E67E /* EZAudio */ = {
isa = PBXGroup;
children = (
66755AB61B3B79220013E67E /* AEFloatConverter.h */,
66755AB71B3B79220013E67E /* AEFloatConverter.m */,
66755AB81B3B79220013E67E /* EZAudio.h */,
66755AB91B3B79220013E67E /* EZAudio.m */,
66755ABA1B3B79220013E67E /* EZAudioDevice.h */,
@@ -406,7 +401,6 @@
66755AE51B3B79230013E67E /* EZAudioPlotGLKViewController.m in Sources */,
66755ADF1B3B79230013E67E /* EZAudioFile.m in Sources */,
66755AE81B3B79230013E67E /* EZOutput.m in Sources */,
66755ADB1B3B79230013E67E /* AEFloatConverter.m in Sources */,
66755AE61B3B79230013E67E /* EZAudioUtilities.m in Sources */,
66755AE91B3B79230013E67E /* EZPlot.m in Sources */,
94056E20185BB3D800EB94BA /* AppDelegate.m in Sources */,
@@ -7,7 +7,6 @@
objects = {
/* Begin PBXBuildFile section */
66755B141B3B792A0013E67E /* AEFloatConverter.m in Sources */ = {isa = PBXBuildFile; fileRef = 66755AF01B3B792A0013E67E /* AEFloatConverter.m */; };
66755B151B3B792A0013E67E /* EZAudio.m in Sources */ = {isa = PBXBuildFile; fileRef = 66755AF21B3B792A0013E67E /* EZAudio.m */; };
66755B161B3B792A0013E67E /* EZAudioDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = 66755AF41B3B792A0013E67E /* EZAudioDevice.m */; };
66755B171B3B792A0013E67E /* EZAudioDisplayLink.m in Sources */ = {isa = PBXBuildFile; fileRef = 66755AF61B3B792A0013E67E /* EZAudioDisplayLink.m */; };
@@ -59,8 +58,6 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
66755AEF1B3B792A0013E67E /* AEFloatConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEFloatConverter.h; sourceTree = "<group>"; };
66755AF01B3B792A0013E67E /* AEFloatConverter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AEFloatConverter.m; sourceTree = "<group>"; };
66755AF11B3B792A0013E67E /* EZAudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudio.h; sourceTree = "<group>"; };
66755AF21B3B792A0013E67E /* EZAudio.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudio.m; sourceTree = "<group>"; };
66755AF31B3B792A0013E67E /* EZAudioDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioDevice.h; sourceTree = "<group>"; };
@@ -156,8 +153,6 @@
66755AEE1B3B792A0013E67E /* EZAudio */ = {
isa = PBXGroup;
children = (
66755AEF1B3B792A0013E67E /* AEFloatConverter.h */,
66755AF01B3B792A0013E67E /* AEFloatConverter.m */,
66755AF11B3B792A0013E67E /* EZAudio.h */,
66755AF21B3B792A0013E67E /* EZAudio.m */,
66755AF31B3B792A0013E67E /* EZAudioDevice.h */,
@@ -406,7 +401,6 @@
66755B1E1B3B792A0013E67E /* EZAudioPlotGLKViewController.m in Sources */,
66755B181B3B792A0013E67E /* EZAudioFile.m in Sources */,
66755B211B3B792A0013E67E /* EZOutput.m in Sources */,
66755B141B3B792A0013E67E /* AEFloatConverter.m in Sources */,
66755B1F1B3B792A0013E67E /* EZAudioUtilities.m in Sources */,
66755B221B3B792A0013E67E /* EZPlot.m in Sources */,
94056EDE185BCC0200EB94BA /* WaveformFromFileViewController.m in Sources */,
@@ -124,10 +124,11 @@
// Plot the whole waveform
//
__weak typeof (self) weakSelf = self;
[self.audioFile getWaveformDataWithCompletionBlock:^(float *waveformData,
UInt32 length)
[self.audioFile getWaveformDataWithNumberOfPoints:1024
completion:^(float **waveformData,
int length)
{
[weakSelf.audioPlot updateBuffer:waveformData
[weakSelf.audioPlot updateBuffer:waveformData[0]
withBufferSize:length];
}];
}
@@ -7,7 +7,6 @@
objects = {
/* Begin PBXBuildFile section */
667558A11B3B604B0013E67E /* AEFloatConverter.m in Sources */ = {isa = PBXBuildFile; fileRef = 6675587D1B3B604B0013E67E /* AEFloatConverter.m */; };
667558A21B3B604B0013E67E /* EZAudio.m in Sources */ = {isa = PBXBuildFile; fileRef = 6675587F1B3B604B0013E67E /* EZAudio.m */; };
667558A31B3B604B0013E67E /* EZAudioDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = 667558811B3B604B0013E67E /* EZAudioDevice.m */; };
667558A41B3B604B0013E67E /* EZAudioDisplayLink.m in Sources */ = {isa = PBXBuildFile; fileRef = 667558831B3B604B0013E67E /* EZAudioDisplayLink.m */; };
@@ -56,8 +55,6 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
6675587C1B3B604B0013E67E /* AEFloatConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEFloatConverter.h; sourceTree = "<group>"; };
6675587D1B3B604B0013E67E /* AEFloatConverter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AEFloatConverter.m; sourceTree = "<group>"; };
6675587E1B3B604B0013E67E /* EZAudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudio.h; sourceTree = "<group>"; };
6675587F1B3B604B0013E67E /* EZAudio.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudio.m; sourceTree = "<group>"; };
667558801B3B604B0013E67E /* EZAudioDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioDevice.h; sourceTree = "<group>"; };
@@ -146,8 +143,6 @@
6675587B1B3B604B0013E67E /* EZAudio */ = {
isa = PBXGroup;
children = (
6675587C1B3B604B0013E67E /* AEFloatConverter.h */,
6675587D1B3B604B0013E67E /* AEFloatConverter.m */,
6675587E1B3B604B0013E67E /* EZAudio.h */,
6675587F1B3B604B0013E67E /* EZAudio.m */,
667558801B3B604B0013E67E /* EZAudioDevice.h */,
@@ -378,7 +373,6 @@
667558AB1B3B604B0013E67E /* EZAudioPlotGLKViewController.m in Sources */,
667558A51B3B604B0013E67E /* EZAudioFile.m in Sources */,
667558AE1B3B604B0013E67E /* EZOutput.m in Sources */,
667558A11B3B604B0013E67E /* AEFloatConverter.m in Sources */,
667558AC1B3B604B0013E67E /* EZAudioUtilities.m in Sources */,
667558AF1B3B604B0013E67E /* EZPlot.m in Sources */,
94056F8E185E593500EB94BA /* AppDelegate.m in Sources */,
@@ -8,7 +8,6 @@
/* Begin PBXBuildFile section */
6628E2331B39F7CC00020E56 /* MainStoryboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6628E2321B39F7CC00020E56 /* MainStoryboard.storyboard */; };
667559131B3B78720013E67E /* AEFloatConverter.m in Sources */ = {isa = PBXBuildFile; fileRef = 667558EF1B3B78720013E67E /* AEFloatConverter.m */; };
667559141B3B78720013E67E /* EZAudio.m in Sources */ = {isa = PBXBuildFile; fileRef = 667558F11B3B78720013E67E /* EZAudio.m */; };
667559151B3B78720013E67E /* EZAudioDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = 667558F31B3B78720013E67E /* EZAudioDevice.m */; };
667559161B3B78720013E67E /* EZAudioDisplayLink.m in Sources */ = {isa = PBXBuildFile; fileRef = 667558F51B3B78720013E67E /* EZAudioDisplayLink.m */; };
@@ -58,8 +57,6 @@
/* Begin PBXFileReference section */
6628E2321B39F7CC00020E56 /* MainStoryboard.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = MainStoryboard.storyboard; sourceTree = "<group>"; };
667558EE1B3B78720013E67E /* AEFloatConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEFloatConverter.h; sourceTree = "<group>"; };
667558EF1B3B78720013E67E /* AEFloatConverter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AEFloatConverter.m; sourceTree = "<group>"; };
667558F01B3B78720013E67E /* EZAudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudio.h; sourceTree = "<group>"; };
667558F11B3B78720013E67E /* EZAudio.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudio.m; sourceTree = "<group>"; };
667558F21B3B78720013E67E /* EZAudioDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioDevice.h; sourceTree = "<group>"; };
@@ -149,8 +146,6 @@
667558ED1B3B78720013E67E /* EZAudio */ = {
isa = PBXGroup;
children = (
667558EE1B3B78720013E67E /* AEFloatConverter.h */,
667558EF1B3B78720013E67E /* AEFloatConverter.m */,
667558F01B3B78720013E67E /* EZAudio.h */,
667558F11B3B78720013E67E /* EZAudio.m */,
667558F21B3B78720013E67E /* EZAudioDevice.h */,
@@ -382,7 +377,6 @@
6675591D1B3B78720013E67E /* EZAudioPlotGLKViewController.m in Sources */,
667559171B3B78720013E67E /* EZAudioFile.m in Sources */,
667559201B3B78720013E67E /* EZOutput.m in Sources */,
667559131B3B78720013E67E /* AEFloatConverter.m in Sources */,
6675591E1B3B78720013E67E /* EZAudioUtilities.m in Sources */,
667559211B3B78720013E67E /* EZPlot.m in Sources */,
9417A9D01871E97D00D9D37B /* FFTViewController.m in Sources */,
@@ -7,7 +7,6 @@
objects = {
/* Begin PBXBuildFile section */
667559F71B3B78940013E67E /* AEFloatConverter.m in Sources */ = {isa = PBXBuildFile; fileRef = 667559D31B3B78940013E67E /* AEFloatConverter.m */; };
667559F81B3B78940013E67E /* EZAudio.m in Sources */ = {isa = PBXBuildFile; fileRef = 667559D51B3B78940013E67E /* EZAudio.m */; };
667559F91B3B78940013E67E /* EZAudioDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = 667559D71B3B78940013E67E /* EZAudioDevice.m */; };
667559FA1B3B78940013E67E /* EZAudioDisplayLink.m in Sources */ = {isa = PBXBuildFile; fileRef = 667559D91B3B78940013E67E /* EZAudioDisplayLink.m */; };
@@ -56,8 +55,6 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
667559D21B3B78940013E67E /* AEFloatConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEFloatConverter.h; sourceTree = "<group>"; };
667559D31B3B78940013E67E /* AEFloatConverter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AEFloatConverter.m; sourceTree = "<group>"; };
667559D41B3B78940013E67E /* EZAudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudio.h; sourceTree = "<group>"; };
667559D51B3B78940013E67E /* EZAudio.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudio.m; sourceTree = "<group>"; };
667559D61B3B78940013E67E /* EZAudioDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioDevice.h; sourceTree = "<group>"; };
@@ -146,8 +143,6 @@
667559D11B3B78940013E67E /* EZAudio */ = {
isa = PBXGroup;
children = (
667559D21B3B78940013E67E /* AEFloatConverter.h */,
667559D31B3B78940013E67E /* AEFloatConverter.m */,
667559D41B3B78940013E67E /* EZAudio.h */,
667559D51B3B78940013E67E /* EZAudio.m */,
667559D61B3B78940013E67E /* EZAudioDevice.h */,
@@ -378,7 +373,6 @@
66755A011B3B78940013E67E /* EZAudioPlotGLKViewController.m in Sources */,
667559FB1B3B78940013E67E /* EZAudioFile.m in Sources */,
66755A041B3B78940013E67E /* EZOutput.m in Sources */,
667559F71B3B78940013E67E /* AEFloatConverter.m in Sources */,
66755A021B3B78940013E67E /* EZAudioUtilities.m in Sources */,
66755A051B3B78940013E67E /* EZPlot.m in Sources */,
94056FFC185E5EAF00EB94BA /* AppDelegate.m in Sources */,
@@ -8,7 +8,6 @@
/* Begin PBXBuildFile section */
6628E2311B39F7C300020E56 /* MainStoryboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6628E2301B39F7C300020E56 /* MainStoryboard.storyboard */; };
6675594C1B3B78790013E67E /* AEFloatConverter.m in Sources */ = {isa = PBXBuildFile; fileRef = 667559281B3B78790013E67E /* AEFloatConverter.m */; };
6675594D1B3B78790013E67E /* EZAudio.m in Sources */ = {isa = PBXBuildFile; fileRef = 6675592A1B3B78790013E67E /* EZAudio.m */; };
6675594E1B3B78790013E67E /* EZAudioDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = 6675592C1B3B78790013E67E /* EZAudioDevice.m */; };
6675594F1B3B78790013E67E /* EZAudioDisplayLink.m in Sources */ = {isa = PBXBuildFile; fileRef = 6675592E1B3B78790013E67E /* EZAudioDisplayLink.m */; };
@@ -57,8 +56,6 @@
/* Begin PBXFileReference section */
6628E2301B39F7C300020E56 /* MainStoryboard.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = MainStoryboard.storyboard; sourceTree = "<group>"; };
667559271B3B78790013E67E /* AEFloatConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEFloatConverter.h; sourceTree = "<group>"; };
667559281B3B78790013E67E /* AEFloatConverter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AEFloatConverter.m; sourceTree = "<group>"; };
667559291B3B78790013E67E /* EZAudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudio.h; sourceTree = "<group>"; };
6675592A1B3B78790013E67E /* EZAudio.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudio.m; sourceTree = "<group>"; };
6675592B1B3B78790013E67E /* EZAudioDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioDevice.h; sourceTree = "<group>"; };
@@ -146,8 +143,6 @@
667559261B3B78790013E67E /* EZAudio */ = {
isa = PBXGroup;
children = (
667559271B3B78790013E67E /* AEFloatConverter.h */,
667559281B3B78790013E67E /* AEFloatConverter.m */,
667559291B3B78790013E67E /* EZAudio.h */,
6675592A1B3B78790013E67E /* EZAudio.m */,
6675592B1B3B78790013E67E /* EZAudioDevice.h */,
@@ -378,7 +373,6 @@
667559561B3B78790013E67E /* EZAudioPlotGLKViewController.m in Sources */,
667559501B3B78790013E67E /* EZAudioFile.m in Sources */,
667559591B3B78790013E67E /* EZOutput.m in Sources */,
6675594C1B3B78790013E67E /* AEFloatConverter.m in Sources */,
667559571B3B78790013E67E /* EZAudioUtilities.m in Sources */,
6675595A1B3B78790013E67E /* EZPlot.m in Sources */,
9417A61A1864D4DC00D9D37B /* main.m in Sources */,
@@ -8,7 +8,6 @@
/* Begin PBXBuildFile section */
6628E22B1B39F7A800020E56 /* MainStoryboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6628E22A1B39F7A800020E56 /* MainStoryboard.storyboard */; };
667558DA1B3B60B80013E67E /* AEFloatConverter.m in Sources */ = {isa = PBXBuildFile; fileRef = 667558B61B3B60B80013E67E /* AEFloatConverter.m */; };
667558DB1B3B60B80013E67E /* EZAudio.m in Sources */ = {isa = PBXBuildFile; fileRef = 667558B81B3B60B80013E67E /* EZAudio.m */; };
667558DC1B3B60B80013E67E /* EZAudioDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = 667558BA1B3B60B80013E67E /* EZAudioDevice.m */; };
667558DD1B3B60B80013E67E /* EZAudioDisplayLink.m in Sources */ = {isa = PBXBuildFile; fileRef = 667558BC1B3B60B80013E67E /* EZAudioDisplayLink.m */; };
@@ -58,8 +57,6 @@
/* Begin PBXFileReference section */
6628E22A1B39F7A800020E56 /* MainStoryboard.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = MainStoryboard.storyboard; sourceTree = "<group>"; };
667558B51B3B60B80013E67E /* AEFloatConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEFloatConverter.h; sourceTree = "<group>"; };
667558B61B3B60B80013E67E /* AEFloatConverter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AEFloatConverter.m; sourceTree = "<group>"; };
667558B71B3B60B80013E67E /* EZAudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudio.h; sourceTree = "<group>"; };
667558B81B3B60B80013E67E /* EZAudio.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudio.m; sourceTree = "<group>"; };
667558B91B3B60B80013E67E /* EZAudioDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioDevice.h; sourceTree = "<group>"; };
@@ -148,8 +145,6 @@
667558B41B3B60B80013E67E /* EZAudio */ = {
isa = PBXGroup;
children = (
667558B51B3B60B80013E67E /* AEFloatConverter.h */,
667558B61B3B60B80013E67E /* AEFloatConverter.m */,
667558B71B3B60B80013E67E /* EZAudio.h */,
667558B81B3B60B80013E67E /* EZAudio.m */,
667558B91B3B60B80013E67E /* EZAudioDevice.h */,
@@ -382,7 +377,6 @@
667558E41B3B60B80013E67E /* EZAudioPlotGLKViewController.m in Sources */,
667558DE1B3B60B80013E67E /* EZAudioFile.m in Sources */,
667558E71B3B60B80013E67E /* EZOutput.m in Sources */,
667558DA1B3B60B80013E67E /* AEFloatConverter.m in Sources */,
667558E51B3B60B80013E67E /* EZAudioUtilities.m in Sources */,
667558E81B3B60B80013E67E /* EZPlot.m in Sources */,
944D043D1860398B0076EF7A /* PlayFileViewController.m in Sources */,
@@ -14,20 +14,7 @@
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Override point for customization after application launch.
// Remember to configure your audio session
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
NSError *err = NULL;
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord error:&err];
if( err ){
NSLog(@"There was an error creating the audio session");
}
[audioSession overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:NULL];
if( err ){
NSLog(@"There was an error sending the audio to the speakers");
}
// Override point for customization after application launch.
return YES;
}
@@ -21,10 +21,30 @@
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Pcc-nf-p5k">
<rect key="frame" x="0.0" y="435" width="600" height="165"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="0.70000000000000007" colorSpace="calibratedRGB"/>
<rect key="frame" x="0.0" y="396" width="600" height="204"/>
<subviews>
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.5" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="d3R-ct-df2">
<rect key="frame" x="118" y="9" width="467" height="31"/>
<color key="tintColor" red="0.93725490199999995" green="0.93725490199999995" blue="0.95686274510000002" alpha="1" colorSpace="calibratedRGB"/>
<connections>
<action selector="changeVolume:" destination="scx-fQ-Cxi" eventType="valueChanged" id="zBC-Ej-fju"/>
</connections>
</slider>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Volume:" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="iS5-xf-09a">
<rect key="frame" x="18" y="15" width="49" height="16"/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="13"/>
<color key="textColor" red="0.93725490199999995" green="0.93725490199999995" blue="0.95686274510000002" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.70000000000000007" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="165" id="CMG-37-Ji8"/>
<constraint firstAttribute="height" constant="204" id="CMG-37-Ji8"/>
<constraint firstItem="iS5-xf-09a" firstAttribute="leading" secondItem="Pcc-nf-p5k" secondAttribute="leading" constant="18" id="MWr-Og-OGn"/>
<constraint firstItem="d3R-ct-df2" firstAttribute="top" secondItem="Pcc-nf-p5k" secondAttribute="top" constant="9" id="Ukk-HX-GtW"/>
<constraint firstItem="d3R-ct-df2" firstAttribute="leading" secondItem="iS5-xf-09a" secondAttribute="trailing" constant="53" id="cE7-h4-F6T"/>
<constraint firstItem="iS5-xf-09a" firstAttribute="top" secondItem="Pcc-nf-p5k" secondAttribute="top" constant="15" id="cFA-qs-q09"/>
<constraint firstAttribute="trailing" secondItem="d3R-ct-df2" secondAttribute="trailing" constant="17" id="rCY-Vg-KOz"/>
</constraints>
</view>
<segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="X0b-lY-wCz">
@@ -37,7 +57,7 @@
<segment title="Buffer"/>
<segment title="Rolling"/>
</segments>
<color key="tintColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
<color key="tintColor" red="0.93725490199999995" green="0.93725490199999995" blue="0.95686274510000002" alpha="1" colorSpace="calibratedRGB"/>
<connections>
<action selector="changePlotType:" destination="scx-fQ-Cxi" eventType="valueChanged" id="Xg4-nc-gcP"/>
</connections>
@@ -48,7 +68,7 @@
<constraint firstAttribute="height" constant="21" id="ZjK-w5-X90"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="13"/>
<color key="textColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
<color key="textColor" red="0.93725490199999995" green="0.93725490199999995" blue="0.95686274510000002" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" showsTouchWhenHighlighted="YES" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="MLt-5E-p8a">
@@ -58,7 +78,7 @@
<constraint firstAttribute="width" constant="59" id="mxq-4g-Aib"/>
</constraints>
<fontDescription key="fontDescription" name="HelveticaNeue-Bold" family="Helvetica Neue" pointSize="13"/>
<color key="tintColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
<color key="tintColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<state key="normal" title="Play">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
@@ -66,16 +86,16 @@
<action selector="play:" destination="scx-fQ-Cxi" eventType="touchUpInside" id="jDx-hA-MSg"/>
</connections>
</button>
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.5" minValue="0.0" maxValue="1" continuous="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Oc2-kU-kAJ">
<slider opaque="NO" multipleTouchEnabled="YES" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.5" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="Oc2-kU-kAJ">
<rect key="frame" x="115" y="483" width="469" height="31"/>
<color key="tintColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
<color key="tintColor" red="0.93725490199999995" green="0.93725490199999995" blue="0.95686274510000002" alpha="1" colorSpace="calibratedRGB"/>
<connections>
<action selector="seekToFrame:" destination="scx-fQ-Cxi" eventType="valueChanged" id="6ZA-4m-9I2"/>
</connections>
</slider>
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="128" minValue="128" maxValue="1024" translatesAutoresizingMaskIntoConstraints="NO" id="2qJ-Va-Qht">
<rect key="frame" x="117" y="444" width="467" height="31"/>
<color key="tintColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
<color key="tintColor" red="0.93725490199999995" green="0.93725490199999995" blue="0.95686274510000002" alpha="1" colorSpace="calibratedRGB"/>
<connections>
<action selector="changeRollingHistoryLength:" destination="scx-fQ-Cxi" eventType="valueChanged" id="u97-4p-2XN"/>
</connections>
@@ -83,13 +103,13 @@
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Rolling Length:" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="phI-Qy-veJ">
<rect key="frame" x="17" y="451" width="93" height="16"/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="13"/>
<color key="textColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
<color key="textColor" red="0.93725490199999995" green="0.93725490199999995" blue="0.95686274510000002" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Position:" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="DyY-r3-0uN">
<rect key="frame" x="17" y="490" width="54" height="16"/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="13"/>
<color key="textColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
<color key="textColor" red="0.93725490199999995" green="0.93725490199999995" blue="0.95686274510000002" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
@@ -130,7 +150,9 @@
<outlet property="audioPlot" destination="kt3-hw-eFY" id="9mm-d1-vJk"/>
<outlet property="filePathLabel" destination="i5r-ex-ukW" id="GRl-ld-nWi"/>
<outlet property="framePositionSlider" destination="Oc2-kU-kAJ" id="z0M-Q9-JAb"/>
<outlet property="rollingHistorySlider" destination="2qJ-Va-Qht" id="yf8-JW-5yM"/>
<outlet property="view" destination="PED-9r-Xub" id="Xxb-En-D2X"/>
<outlet property="volumeSlider" destination="d3R-ct-df2" id="pAW-BF-AFa"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="g7v-Xp-r3F" userLabel="First Responder" sceneMemberID="firstResponder"/>
@@ -20,29 +20,45 @@
Using the EZOutputDataSource to provide output data to the EZOutput component.
*/
@interface PlayFileViewController : UIViewController <EZAudioFileDelegate,
EZOutputDataSource>
EZOutputDataSource,
EZOutputDelegate>
#pragma mark - Components
/**
The EZAudioFile representing of the currently selected audio file
*/
@property (nonatomic,strong) EZAudioFile *audioFile;
@property (nonatomic, strong) EZAudioFile *audioFile;
/**
The EZOutput representing the output currently being used to play the audio file.
*/
@property (nonatomic, strong) EZOutput *output;
/**
The CoreGraphics based audio plot
*/
@property (nonatomic,weak) IBOutlet EZAudioPlot *audioPlot;
@property (nonatomic, weak) IBOutlet EZAudioPlot *audioPlot;
#pragma mark - UI Extras
/**
A label to display the current file path with the waveform shown
*/
@property (nonatomic,weak) IBOutlet UILabel *filePathLabel;
@property (nonatomic, weak) IBOutlet UILabel *filePathLabel;
/**
A slider to indicate the current frame position in the audio file
*/
@property (nonatomic,weak) IBOutlet UISlider *framePositionSlider;
@property (nonatomic, weak) IBOutlet UISlider *framePositionSlider;
/**
A slider to indicate the current rolling history length of the audio plot.
*/
@property (nonatomic, weak) IBOutlet UISlider *rollingHistorySlider;
/**
A slider to indicate the volume on the audio player
*/
@property (nonatomic, weak) IBOutlet UISlider *volumeSlider;
/**
A BOOL indicating whether or not we've reached the end of the file
@@ -60,6 +76,11 @@
*/
- (IBAction)changeRollingHistoryLength:(id)sender;
/**
Changes the volume of the audio player.
*/
- (IBAction)changeVolume:(id)sender;
/**
Begins playback if a file is loaded. Pauses if the file is already playing.
*/
@@ -9,10 +9,6 @@
#import "PlayFileViewController.h"
@implementation PlayFileViewController
@synthesize audioFile = _audioFile;
@synthesize audioPlot = _audioPlot;
@synthesize eof = _eof;
@synthesize framePositionSlider = _framePositionSlider;
#pragma mark - Status Bar Style
- (UIStatusBarStyle)preferredStatusBarStyle
@@ -21,166 +17,242 @@
}
#pragma mark - Customize the Audio Plot
-(void)viewDidLoad {
[super viewDidLoad];
/*
Customizing the audio plot's look
*/
// Background color
self.audioPlot.backgroundColor = [UIColor colorWithRed: 0.816 green: 0.349 blue: 0.255 alpha: 1];
// Waveform color
self.audioPlot.color = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0];
// Plot type
self.audioPlot.plotType = EZPlotTypeBuffer;
// Fill
self.audioPlot.shouldFill = YES;
// Mirror
self.audioPlot.shouldMirror = YES;
/*
Try opening the sample file
*/
[self openFileWithFilePathURL:[NSURL fileURLWithPath:kAudioFileDefault]];
- (void)viewDidLoad
{
[super viewDidLoad];
//
// Setup the AVAudioSession. EZMicrophone will not work properly on iOS
// if you don't do this!
//
AVAudioSession *session = [AVAudioSession sharedInstance];
NSError *error;
[session setCategory:AVAudioSessionCategoryPlayback error:&error];
if (error)
{
NSLog(@"Error setting up audio session category: %@", error.localizedDescription);
}
[session setActive:YES error:&error];
if (error)
{
NSLog(@"Error setting up audio session active: %@", error.localizedDescription);
}
//
// Customize the plot's look
//
// Background color
self.audioPlot.backgroundColor = [UIColor colorWithRed: 0.816 green: 0.349 blue: 0.255 alpha: 1];
// Waveform color
self.audioPlot.color = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0];
// Plot type
self.audioPlot.plotType = EZPlotTypeBuffer;
// Fill
self.audioPlot.shouldFill = YES;
// Mirror
self.audioPlot.shouldMirror = YES;
//
// Create an EZOutput instance
//
self.output = [EZOutput outputWithDataSource:self];
self.output.delegate = self;
//
// Customize UI controls
//
self.volumeSlider.value = [self.output volume];
self.rollingHistorySlider.value = [self.audioPlot rollingHistoryLength];
//
// Try opening the sample file
//
[self openFileWithFilePathURL:[NSURL fileURLWithPath:kAudioFileDefault]];
}
//------------------------------------------------------------------------------
#pragma mark - Actions
-(void)changePlotType:(id)sender {
NSInteger selectedSegment = [sender selectedSegmentIndex];
switch(selectedSegment){
case 0:
[self drawBufferPlot];
break;
case 1:
[self drawRollingPlot];
break;
default:
break;
}
//------------------------------------------------------------------------------
- (void)changePlotType:(id)sender
{
NSInteger selectedSegment = [sender selectedSegmentIndex];
switch(selectedSegment)
{
case 0:
[self drawBufferPlot];
break;
case 1:
[self drawRollingPlot];
break;
default:
break;
}
}
//------------------------------------------------------------------------------
- (void)changeRollingHistoryLength:(id)sender
{
float value = [(UISlider *)sender value];
[self.audioPlot setRollingHistoryLength:(int)value];
}
-(void)play:(id)sender {
if( ![[EZOutput sharedOutput] isPlaying] )
{
if( self.eof ){
[self.audioFile seekToFrame:0];
//------------------------------------------------------------------------------
- (void)changeVolume:(id)sender
{
float value = [(UISlider *)sender value];
[self.output setVolume:value];
}
//------------------------------------------------------------------------------
- (void)openFileWithFilePathURL:(NSURL *)filePathURL
{
// Stop playback
[self.output stopPlayback];
self.audioFile = [EZAudioFile audioFileWithURL:filePathURL delegate:self];
self.eof = NO;
self.filePathLabel.text = filePathURL.lastPathComponent;
self.framePositionSlider.maximumValue = (float)self.audioFile.totalFrames;
// Set the input format from the EZAudioFile on the output
[self.output setInputFormat:[self.audioFile clientFormat]];
// Plot the whole waveform
self.audioPlot.plotType = EZPlotTypeBuffer;
self.audioPlot.shouldFill = YES;
self.audioPlot.shouldMirror = YES;
__weak typeof (self) weakSelf = self;
[self.audioFile getWaveformDataWithCompletionBlock:^(float **waveformData,
int length)
{
[weakSelf.audioPlot updateBuffer:waveformData[0]
withBufferSize:length];
}];
}
//------------------------------------------------------------------------------
- (void)play:(id)sender
{
if (![self.output isPlaying])
{
if (self.eof)
{
[self.audioFile seekToFrame:0];
}
[self.output startPlayback];
}
else
{
[self.output stopPlayback];
}
[EZOutput sharedOutput].outputDataSource = self;
[[EZOutput sharedOutput] startPlayback];
}
else {
[EZOutput sharedOutput].outputDataSource = nil;
[[EZOutput sharedOutput] stopPlayback];
}
}
-(void)seekToFrame:(id)sender {
[self.audioFile seekToFrame:(SInt64)[(UISlider*)sender value]];
//------------------------------------------------------------------------------
- (void)seekToFrame:(id)sender
{
[self.audioFile seekToFrame:(SInt64)[(UISlider *)sender value]];
}
#pragma mark - Action Extensions
//------------------------------------------------------------------------------
#pragma mark - Utility
//------------------------------------------------------------------------------
/*
Give the visualization of the current buffer (this is almost exactly the openFrameworks audio input example)
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 fill
self.audioPlot.shouldFill = NO;
// Don't mirror over the x-axis
self.audioPlot.shouldMirror = NO;
- (void)drawBufferPlot
{
self.audioPlot.plotType = EZPlotTypeBuffer;
self.audioPlot.shouldMirror = NO;
self.audioPlot.shouldFill = 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)openFileWithFilePathURL:(NSURL*)filePathURL {
// Stop playback
[[EZOutput sharedOutput] stopPlayback];
self.audioFile = [EZAudioFile audioFileWithURL:filePathURL];
self.audioFile.audioFileDelegate = self;
self.eof = NO;
self.filePathLabel.text = filePathURL.lastPathComponent;
self.framePositionSlider.maximumValue = (float)self.audioFile.totalFrames;
// Set the client format from the EZAudioFile on the output
[[EZOutput sharedOutput] setAudioStreamBasicDescription:self.audioFile.clientFormat];
// Plot the whole waveform
self.audioPlot.plotType = EZPlotTypeBuffer;
self.audioPlot.shouldFill = YES;
self.audioPlot.shouldMirror = YES;
[self.audioFile getWaveformDataWithCompletionBlock:^(float *waveformData, UInt32 length) {
[self.audioPlot updateBuffer:waveformData withBufferSize:length];
}];
}
#pragma mark - EZAudioFileDelegate
-(void)audioFile:(EZAudioFile *)audioFile
readAudio:(float **)buffer
withBufferSize:(UInt32)bufferSize
withNumberOfChannels:(UInt32)numberOfChannels {
dispatch_async(dispatch_get_main_queue(), ^{
if( [EZOutput sharedOutput].isPlaying ){
if( self.audioPlot.plotType == EZPlotTypeBuffer &&
self.audioPlot.shouldFill == YES &&
self.audioPlot.shouldMirror == YES ){
self.audioPlot.shouldFill = NO;
self.audioPlot.shouldMirror = NO;
}
[self.audioPlot updateBuffer:buffer[0] withBufferSize:bufferSize];
}
});
}
-(void)audioFile:(EZAudioFile *)audioFile
updatedPosition:(SInt64)framePosition {
dispatch_async(dispatch_get_main_queue(), ^{
if( !self.framePositionSlider.touchInside ){
self.framePositionSlider.value = (float)framePosition;
}
});
}
#pragma mark - EZOutputDataSource
-(void)output:(EZOutput *)output shouldFillAudioBufferList:(AudioBufferList *)audioBufferList withNumberOfFrames:(UInt32)frames
- (void)drawRollingPlot
{
if( self.audioFile )
{
UInt32 bufferSize;
[self.audioFile readFrames:frames
audioBufferList:audioBufferList
bufferSize:&bufferSize
eof:&_eof];
if( _eof )
{
[self seekToFrame:0];
}
}
self.audioPlot.plotType = EZPlotTypeRolling;
self.audioPlot.shouldFill = YES;
self.audioPlot.shouldMirror = YES;
}
-(AudioStreamBasicDescription)outputHasAudioStreamBasicDescription:(EZOutput *)output {
return self.audioFile.clientFormat;
//------------------------------------------------------------------------------
#pragma mark - EZAudioFileDelegate
//------------------------------------------------------------------------------
- (void)audioFile:(EZAudioFile *)audioFile
updatedPosition:(SInt64)framePosition
{
__weak typeof (self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
if (!weakSelf.framePositionSlider.touchInside)
{
weakSelf.framePositionSlider.value = (float)framePosition;
}
});
}
//------------------------------------------------------------------------------
#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.audioFile seekToFrame:0];
}
}
return noErr;
}
//------------------------------------------------------------------------------
#pragma mark - EZOutputDelegate
//------------------------------------------------------------------------------
- (void) output:(EZOutput *)output
playedAudio:(float **)buffer
withBufferSize:(UInt32)bufferSize
withNumberOfChannels:(UInt32)numberOfChannels
{
__weak typeof (self) weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
if ([self.output isPlaying])
{
if (weakSelf.audioPlot.plotType == EZPlotTypeBuffer &&
weakSelf.audioPlot.shouldFill == YES &&
weakSelf.audioPlot.shouldMirror == YES)
{
weakSelf.audioPlot.shouldFill = NO;
weakSelf.audioPlot.shouldMirror = NO;
}
[weakSelf.audioPlot updateBuffer:buffer[0]
withBufferSize:bufferSize];
}
});
}
//------------------------------------------------------------------------------
@end
@@ -8,7 +8,6 @@
/* Begin PBXBuildFile section */
6628E22D1B39F7B400020E56 /* MainStoryboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6628E22C1B39F7B400020E56 /* MainStoryboard.storyboard */; };
667559BE1B3B78890013E67E /* AEFloatConverter.m in Sources */ = {isa = PBXBuildFile; fileRef = 6675599A1B3B78890013E67E /* AEFloatConverter.m */; };
667559BF1B3B78890013E67E /* EZAudio.m in Sources */ = {isa = PBXBuildFile; fileRef = 6675599C1B3B78890013E67E /* EZAudio.m */; };
667559C01B3B78890013E67E /* EZAudioDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = 6675599E1B3B78890013E67E /* EZAudioDevice.m */; };
667559C11B3B78890013E67E /* EZAudioDisplayLink.m in Sources */ = {isa = PBXBuildFile; fileRef = 667559A01B3B78890013E67E /* EZAudioDisplayLink.m */; };
@@ -57,8 +56,6 @@
/* Begin PBXFileReference section */
6628E22C1B39F7B400020E56 /* MainStoryboard.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = MainStoryboard.storyboard; sourceTree = "<group>"; };
667559991B3B78890013E67E /* AEFloatConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEFloatConverter.h; sourceTree = "<group>"; };
6675599A1B3B78890013E67E /* AEFloatConverter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AEFloatConverter.m; sourceTree = "<group>"; };
6675599B1B3B78890013E67E /* EZAudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudio.h; sourceTree = "<group>"; };
6675599C1B3B78890013E67E /* EZAudio.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudio.m; sourceTree = "<group>"; };
6675599D1B3B78890013E67E /* EZAudioDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioDevice.h; sourceTree = "<group>"; };
@@ -146,8 +143,6 @@
667559981B3B78890013E67E /* EZAudio */ = {
isa = PBXGroup;
children = (
667559991B3B78890013E67E /* AEFloatConverter.h */,
6675599A1B3B78890013E67E /* AEFloatConverter.m */,
6675599B1B3B78890013E67E /* EZAudio.h */,
6675599C1B3B78890013E67E /* EZAudio.m */,
6675599D1B3B78890013E67E /* EZAudioDevice.h */,
@@ -378,7 +373,6 @@
667559C81B3B78890013E67E /* EZAudioPlotGLKViewController.m in Sources */,
667559C21B3B78890013E67E /* EZAudioFile.m in Sources */,
667559CB1B3B78890013E67E /* EZOutput.m in Sources */,
667559BE1B3B78890013E67E /* AEFloatConverter.m in Sources */,
667559C91B3B78890013E67E /* EZAudioUtilities.m in Sources */,
667559CC1B3B78890013E67E /* EZPlot.m in Sources */,
940570DC185E7F8300EB94BA /* AppDelegate.m in Sources */,
@@ -8,7 +8,6 @@
/* Begin PBXBuildFile section */
6628E22F1B39F7BC00020E56 /* MainStoryboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6628E22E1B39F7BC00020E56 /* MainStoryboard.storyboard */; };
667559851B3B78820013E67E /* AEFloatConverter.m in Sources */ = {isa = PBXBuildFile; fileRef = 667559611B3B78820013E67E /* AEFloatConverter.m */; };
667559861B3B78820013E67E /* EZAudio.m in Sources */ = {isa = PBXBuildFile; fileRef = 667559631B3B78820013E67E /* EZAudio.m */; };
667559871B3B78820013E67E /* EZAudioDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = 667559651B3B78820013E67E /* EZAudioDevice.m */; };
667559881B3B78820013E67E /* EZAudioDisplayLink.m in Sources */ = {isa = PBXBuildFile; fileRef = 667559671B3B78820013E67E /* EZAudioDisplayLink.m */; };
@@ -58,8 +57,6 @@
/* Begin PBXFileReference section */
6628E22E1B39F7BC00020E56 /* MainStoryboard.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = MainStoryboard.storyboard; sourceTree = "<group>"; };
667559601B3B78820013E67E /* AEFloatConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AEFloatConverter.h; sourceTree = "<group>"; };
667559611B3B78820013E67E /* AEFloatConverter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AEFloatConverter.m; sourceTree = "<group>"; };
667559621B3B78820013E67E /* EZAudio.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudio.h; sourceTree = "<group>"; };
667559631B3B78820013E67E /* EZAudio.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = EZAudio.m; sourceTree = "<group>"; };
667559641B3B78820013E67E /* EZAudioDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EZAudioDevice.h; sourceTree = "<group>"; };
@@ -148,8 +145,6 @@
6675595F1B3B78820013E67E /* EZAudio */ = {
isa = PBXGroup;
children = (
667559601B3B78820013E67E /* AEFloatConverter.h */,
667559611B3B78820013E67E /* AEFloatConverter.m */,
667559621B3B78820013E67E /* EZAudio.h */,
667559631B3B78820013E67E /* EZAudio.m */,
667559641B3B78820013E67E /* EZAudioDevice.h */,
@@ -382,7 +377,6 @@
6675598F1B3B78820013E67E /* EZAudioPlotGLKViewController.m in Sources */,
667559891B3B78820013E67E /* EZAudioFile.m in Sources */,
667559921B3B78820013E67E /* EZOutput.m in Sources */,
667559851B3B78820013E67E /* AEFloatConverter.m in Sources */,
667559901B3B78820013E67E /* EZAudioUtilities.m in Sources */,
667559931B3B78820013E67E /* EZPlot.m in Sources */,
940570BE185E6A0400EB94BA /* WaveformFromFileViewController.m in Sources */,
@@ -70,10 +70,11 @@
self.audioPlot.shouldMirror = YES;
__weak typeof (self) weakSelf = self;
[self.audioFile getWaveformDataWithCompletionBlock:^(float *waveformData,
UInt32 length)
[self.audioFile getWaveformDataWithCompletionBlock:^(float **waveformData,
int length)
{
[weakSelf.audioPlot updateBuffer:waveformData withBufferSize:length];
[weakSelf.audioPlot updateBuffer:waveformData[0]
withBufferSize:length];
}];
}
+1 -12
View File
@@ -3,9 +3,6 @@
#EZAudio
A simple, intuitive audio framework for iOS and OSX.
*The Official EZAudio Page:*
http://syedharisali.com/projects/EZAudio/getting-started
##Features
![alt text](https://s3-us-west-1.amazonaws.com/ezaudio-media/EZAudioSummary.png "EZAudioFeatures")
@@ -82,9 +79,6 @@ The official documentation for EZAudio can be found here: http://cocoadocs.org/d
<br>You can also generate the docset yourself using appledocs by running the appledocs on the EZAudio source folder.
##Getting Started
*To see the full project page, interactive Getting Started guide, and Documentation go here:*
http://syedharisali.com/projects/EZAudio/getting-started
To begin using `EZAudio` you must first make sure you have the proper build requirements and frameworks. Below you'll find explanations of each component and code snippets to show how to use each to perform common tasks like getting microphone data, updating audio waveform plots, reading/seeking through audio files, and performing playback.
###Build Requirements
@@ -114,14 +108,11 @@ To begin using `EZAudio` you must first make sure you have the proper build requ
You can add EZAudio to your project in a few ways: <br><br>1.) The easiest way to use EZAudio is via <a href="http://cocoapods.org/", target="_blank">Cocoapods</a>. Simply add EZAudio to your <a href="http://guides.cocoapods.org/using/the-podfile.html", target="_blank">Podfile</a> like so:
`
pod 'EZAudio', '~> 0.1.0'
pod 'EZAudio', '~> 0.4.0'
`
2.) Alternatively, you could clone or fork this repo and just drag and drop the source into your project.
*For more information see main project page:*
http://syedharisali.com/projects/EZAudio/getting-started
##Core Components
`EZAudio` currently offers four components that encompass a wide range of audio functionality. In addition to the functional aspects of these components such as pulling audio data, reading/writing from files, and performing playback they also take special care to hook into the interface components to allow developers to display visual feedback (see the Interface Components below).
@@ -474,7 +465,6 @@ Provides an audio waveform plot that uses CoreGraphics to perform the drawing. O
####Creating An Audio Plot
You can create an audio plot in the interface builder by dragging in a UIView on iOS or an NSView on OSX onto your content area. Then change the custom class of the UIView/NSView to `EZAudioPlot`.
See full Getting Started page for how to: http://syedharisali.com/projects/EZAudio/getting-started
Alternatively, you can could create the audio plot programmatically
@@ -539,7 +529,6 @@ Provides an audio waveform plot that uses OpenGL to perform the drawing. The API
####Creating An OpenGL Audio Plot
You can create an audio plot in the interface builder by dragging in a UIView on iOS or an NSOpenGLView on OSX onto your content area. Then change the custom class of the UIView/NSView to `EZAudioPlotGL`.
See full Getting Started page for how to: http://syedharisali.com/projects/EZAudio/getting-started
Alternatively, you can could create the `EZAudioPlotGL` programmatically
```objectivec