Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c7727f8586 | |||
| c8ee8a4f7d | |||
| 2e90d0e6f1 | |||
| abe23bed12 | |||
| 95d9a1e7ba | |||
| c55423759b | |||
| f2b61d340e | |||
| cc663ec82d | |||
| c96a65831d | |||
| 0073f555bc | |||
| 62055f1782 |
+3
-2
@@ -1,6 +1,6 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "EZAudio"
|
||||
s.version = "0.2.0"
|
||||
s.version = "0.3.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
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
//
|
||||
// AEFloatConverter.h
|
||||
// The Amazing Audio Engine
|
||||
//
|
||||
// Created by Michael Tyson on 25/10/2012.
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
//
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
//
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <AudioToolbox/AudioToolbox.h>
|
||||
|
||||
/*!
|
||||
* Universal converter to float format
|
||||
*
|
||||
* Use this class to easily convert arbitrary audio formats to floating point
|
||||
* for use with utilities like the Accelerate framework.
|
||||
*/
|
||||
@interface AEFloatConverter : NSObject
|
||||
|
||||
/*!
|
||||
* Initialize
|
||||
*
|
||||
* @param sourceFormat The audio format to use
|
||||
*/
|
||||
- (id)initWithSourceFormat:(AudioStreamBasicDescription)sourceFormat;
|
||||
|
||||
/*!
|
||||
* Convert audio to floating-point
|
||||
*
|
||||
* This C function, safe to use in a Core Audio realtime thread context, will take
|
||||
* an audio buffer list of audio in the format you provided at initialisation, and
|
||||
* convert it into a noninterleaved float array.
|
||||
*
|
||||
* @param converter Pointer to the converter object.
|
||||
* @param sourceBuffer An audio buffer list containing the source audio.
|
||||
* @param targetBuffers An array of floating-point arrays to store the converted float audio into.
|
||||
* Note that you must provide the correct number of arrays, to match the number of channels.
|
||||
* @param frames The number of frames to convert.
|
||||
* @return YES on success; NO on failure
|
||||
*/
|
||||
BOOL AEFloatConverterToFloat(AEFloatConverter* converter, AudioBufferList *sourceBuffer, float * const * targetBuffers, UInt32 frames);
|
||||
|
||||
/*!
|
||||
* Convert audio to floating-point, in a buffer list
|
||||
*
|
||||
* This C function, safe to use in a Core Audio realtime thread context, will take
|
||||
* an audio buffer list of audio in the format you provided at initialisation, and
|
||||
* convert it into a noninterleaved float format.
|
||||
*
|
||||
* @param converter Pointer to the converter object.
|
||||
* @param sourceBuffer An audio buffer list containing the source audio.
|
||||
* @param targetBuffer An audio buffer list to store the converted floating-point audio.
|
||||
* @param frames The number of frames to convert.
|
||||
* @return YES on success; NO on failure
|
||||
*/
|
||||
BOOL AEFloatConverterToFloatBufferList(AEFloatConverter* converter, AudioBufferList *sourceBuffer, AudioBufferList *targetBuffer, UInt32 frames);
|
||||
|
||||
/*!
|
||||
* Convert audio from floating-point
|
||||
*
|
||||
* This C function, safe to use in a Core Audio realtime thread context, will take
|
||||
* an audio buffer list of audio in the format you provided at initialisation, and
|
||||
* convert it into a float array.
|
||||
*
|
||||
* @param converter Pointer to the converter object.
|
||||
* @param sourceBuffers An array of floating-point arrays containing the floating-point audio to convert.
|
||||
* Note that you must provide the correct number of arrays, to match the number of channels.
|
||||
* @param targetBuffer An audio buffer list to store the converted audio into.
|
||||
* @param frames The number of frames to convert.
|
||||
* @return YES on success; NO on failure
|
||||
*/
|
||||
BOOL AEFloatConverterFromFloat(AEFloatConverter* converter, float * const * sourceBuffers, AudioBufferList *targetBuffer, UInt32 frames);
|
||||
|
||||
/*!
|
||||
* Convert audio from floating-point, in a buffer list
|
||||
*
|
||||
* This C function, safe to use in a Core Audio realtime thread context, will take
|
||||
* an audio buffer list of audio in the format you provided at initialisation, and
|
||||
* convert it into a float array.
|
||||
*
|
||||
* @param converter Pointer to the converter object.
|
||||
* @param sourceBuffer An audio buffer list containing the source audio.
|
||||
* @param targetBuffer An audio buffer list to store the converted audio into.
|
||||
* @param frames The number of frames to convert.
|
||||
* @return YES on success; NO on failure
|
||||
*/
|
||||
BOOL AEFloatConverterFromFloatBufferList(AEFloatConverter* converter, AudioBufferList *sourceBuffer, AudioBufferList *targetBuffer, UInt32 frames);
|
||||
|
||||
/*!
|
||||
* The AudioStreamBasicDescription representing the converted floating-point format
|
||||
*/
|
||||
@property (nonatomic, readonly) AudioStreamBasicDescription floatingPointAudioDescription;
|
||||
|
||||
/*!
|
||||
* The source audio format set at initialization
|
||||
*/
|
||||
@property (nonatomic, readonly) AudioStreamBasicDescription sourceFormat;
|
||||
|
||||
@end
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -1,211 +0,0 @@
|
||||
//
|
||||
// AEFloatConverter.m
|
||||
// The Amazing Audio Engine
|
||||
//
|
||||
// Created by Michael Tyson on 25/10/2012.
|
||||
//
|
||||
// This software is provided 'as-is', without any express or implied
|
||||
// warranty. In no event will the authors be held liable for any damages
|
||||
// arising from the use of this software.
|
||||
//
|
||||
// Permission is granted to anyone to use this software for any purpose,
|
||||
// including commercial applications, and to alter it and redistribute it
|
||||
// freely, subject to the following restrictions:
|
||||
//
|
||||
// 1. The origin of this software must not be misrepresented; you must not
|
||||
// claim that you wrote the original software. If you use this software
|
||||
// in a product, an acknowledgment in the product documentation would be
|
||||
// appreciated but is not required.
|
||||
//
|
||||
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||
// misrepresented as being the original software.
|
||||
//
|
||||
// 3. This notice may not be removed or altered from any source distribution.
|
||||
//
|
||||
|
||||
#import "AEFloatConverter.h"
|
||||
|
||||
#define checkResult(result,operation) (_checkResult((result),(operation),strrchr(__FILE__, '/')+1,__LINE__))
|
||||
static inline BOOL _checkResult(OSStatus result, const char *operation, const char* file, int line) {
|
||||
if (result != noErr) {
|
||||
NSLog(@"%s:%d: %s result %d %08X %4.4s", file, line, operation, (int)result, (int)result, (char*)&result);
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
#define kNoMoreDataErr -2222
|
||||
|
||||
struct complexInputDataProc_t {
|
||||
AudioBufferList *sourceBuffer;
|
||||
};
|
||||
|
||||
@interface AEFloatConverter () {
|
||||
AudioStreamBasicDescription _sourceAudioDescription;
|
||||
AudioStreamBasicDescription _floatAudioDescription;
|
||||
AudioConverterRef _toFloatConverter;
|
||||
AudioConverterRef _fromFloatConverter;
|
||||
AudioBufferList *_scratchFloatBufferList;
|
||||
}
|
||||
|
||||
static OSStatus complexInputDataProc(AudioConverterRef inAudioConverter,
|
||||
UInt32 *ioNumberDataPackets,
|
||||
AudioBufferList *ioData,
|
||||
AudioStreamPacketDescription **outDataPacketDescription,
|
||||
void *inUserData);
|
||||
@end
|
||||
|
||||
@implementation AEFloatConverter
|
||||
@synthesize sourceFormat = _sourceAudioDescription;
|
||||
|
||||
-(id)initWithSourceFormat:(AudioStreamBasicDescription)sourceFormat {
|
||||
if (!(self = [super init])) return nil;
|
||||
|
||||
_floatAudioDescription.mFormatID = kAudioFormatLinearPCM;
|
||||
_floatAudioDescription.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked | kAudioFormatFlagIsNonInterleaved;
|
||||
_floatAudioDescription.mChannelsPerFrame = sourceFormat.mChannelsPerFrame;
|
||||
_floatAudioDescription.mBytesPerPacket = sizeof(float);
|
||||
_floatAudioDescription.mFramesPerPacket = 1;
|
||||
_floatAudioDescription.mBytesPerFrame = sizeof(float);
|
||||
_floatAudioDescription.mBitsPerChannel = 8 * sizeof(float);
|
||||
_floatAudioDescription.mSampleRate = sourceFormat.mSampleRate;
|
||||
|
||||
_sourceAudioDescription = sourceFormat;
|
||||
|
||||
if (memcmp(&sourceFormat, &_floatAudioDescription, sizeof(AudioStreamBasicDescription)) != 0) {
|
||||
checkResult(AudioConverterNew(&sourceFormat, &_floatAudioDescription, &_toFloatConverter), "AudioConverterNew");
|
||||
checkResult(AudioConverterNew(&_floatAudioDescription, &sourceFormat, &_fromFloatConverter), "AudioConverterNew");
|
||||
_scratchFloatBufferList = (AudioBufferList*)malloc(sizeof(AudioBufferList) + (_floatAudioDescription.mChannelsPerFrame-1)*sizeof(AudioBuffer));
|
||||
_scratchFloatBufferList->mNumberBuffers = _floatAudioDescription.mChannelsPerFrame;
|
||||
for ( int i=0; i<_scratchFloatBufferList->mNumberBuffers; i++) {
|
||||
_scratchFloatBufferList->mBuffers[i].mNumberChannels = 1;
|
||||
}
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void)dealloc {
|
||||
if (_toFloatConverter) AudioConverterDispose(_toFloatConverter);
|
||||
if (_fromFloatConverter) AudioConverterDispose(_fromFloatConverter);
|
||||
if (_scratchFloatBufferList) free(_scratchFloatBufferList);
|
||||
// [super dealloc];
|
||||
}
|
||||
|
||||
|
||||
BOOL AEFloatConverterToFloat(AEFloatConverter* THIS, AudioBufferList *sourceBuffer, float * const * targetBuffers, UInt32 frames) {
|
||||
if (frames == 0) return YES;
|
||||
|
||||
if (THIS->_toFloatConverter) {
|
||||
UInt32 priorDataByteSize = sourceBuffer->mBuffers[0].mDataByteSize;
|
||||
for ( int i=0; i<sourceBuffer->mNumberBuffers; i++) {
|
||||
sourceBuffer->mBuffers[i].mDataByteSize = frames * THIS->_sourceAudioDescription.mBytesPerFrame;
|
||||
}
|
||||
|
||||
for ( int i=0; i<THIS->_scratchFloatBufferList->mNumberBuffers; i++) {
|
||||
THIS->_scratchFloatBufferList->mBuffers[i].mData = targetBuffers[i];
|
||||
THIS->_scratchFloatBufferList->mBuffers[i].mDataByteSize = frames * sizeof(float);
|
||||
}
|
||||
|
||||
OSStatus result = AudioConverterFillComplexBuffer(THIS->_toFloatConverter,
|
||||
complexInputDataProc,
|
||||
&(struct complexInputDataProc_t) { .sourceBuffer = sourceBuffer },
|
||||
&frames,
|
||||
THIS->_scratchFloatBufferList,
|
||||
NULL);
|
||||
|
||||
for ( int i=0; i<sourceBuffer->mNumberBuffers; i++) {
|
||||
sourceBuffer->mBuffers[i].mDataByteSize = priorDataByteSize;
|
||||
}
|
||||
|
||||
if (!checkResult(result, "AudioConverterConvertComplexBuffer")) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
} else {
|
||||
for ( int i=0; i<sourceBuffer->mNumberBuffers; i++) {
|
||||
memcpy(targetBuffers[i], sourceBuffer->mBuffers[i].mData, frames * sizeof(float));
|
||||
}
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
BOOL AEFloatConverterToFloatBufferList(AEFloatConverter* converter, AudioBufferList *sourceBuffer, AudioBufferList *targetBuffer, UInt32 frames) {
|
||||
assert(targetBuffer->mNumberBuffers == converter->_floatAudioDescription.mChannelsPerFrame);
|
||||
|
||||
float *targetBuffers[targetBuffer->mNumberBuffers];
|
||||
for ( int i=0; i<targetBuffer->mNumberBuffers; i++) {
|
||||
targetBuffers[i] = (float*)targetBuffer->mBuffers[i].mData;
|
||||
}
|
||||
return AEFloatConverterToFloat(converter, sourceBuffer, targetBuffers, frames);
|
||||
}
|
||||
|
||||
BOOL AEFloatConverterFromFloat(AEFloatConverter* THIS, float * const * sourceBuffers, AudioBufferList *targetBuffer, UInt32 frames) {
|
||||
if (frames == 0) return YES;
|
||||
|
||||
if (THIS->_fromFloatConverter) {
|
||||
for ( int i=0; i<THIS->_scratchFloatBufferList->mNumberBuffers; i++) {
|
||||
THIS->_scratchFloatBufferList->mBuffers[i].mData = sourceBuffers[i];
|
||||
THIS->_scratchFloatBufferList->mBuffers[i].mDataByteSize = frames * sizeof(float);
|
||||
}
|
||||
|
||||
UInt32 priorDataByteSize = targetBuffer->mBuffers[0].mDataByteSize;
|
||||
for ( int i=0; i<targetBuffer->mNumberBuffers; i++) {
|
||||
targetBuffer->mBuffers[i].mDataByteSize = frames * THIS->_sourceAudioDescription.mBytesPerFrame;
|
||||
}
|
||||
|
||||
OSStatus result = AudioConverterFillComplexBuffer(THIS->_fromFloatConverter,
|
||||
complexInputDataProc,
|
||||
&(struct complexInputDataProc_t) { .sourceBuffer = THIS->_scratchFloatBufferList },
|
||||
&frames,
|
||||
targetBuffer,
|
||||
NULL);
|
||||
|
||||
for ( int i=0; i<targetBuffer->mNumberBuffers; i++) {
|
||||
targetBuffer->mBuffers[i].mDataByteSize = priorDataByteSize;
|
||||
}
|
||||
|
||||
if (!checkResult(result, "AudioConverterConvertComplexBuffer")) {
|
||||
return NO;
|
||||
}
|
||||
} else {
|
||||
for ( int i=0; i<targetBuffer->mNumberBuffers; i++) {
|
||||
memcpy(targetBuffer->mBuffers[i].mData, sourceBuffers[i], frames * sizeof(float));
|
||||
}
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
BOOL AEFloatConverterFromFloatBufferList(AEFloatConverter* converter, AudioBufferList *sourceBuffer, AudioBufferList *targetBuffer, UInt32 frames) {
|
||||
assert(sourceBuffer->mNumberBuffers == converter->_floatAudioDescription.mChannelsPerFrame);
|
||||
|
||||
float *sourceBuffers[sourceBuffer->mNumberBuffers];
|
||||
for ( int i=0; i<sourceBuffer->mNumberBuffers; i++) {
|
||||
sourceBuffers[i] = (float*)sourceBuffer->mBuffers[i].mData;
|
||||
}
|
||||
return AEFloatConverterFromFloat(converter, sourceBuffers, targetBuffer, frames);
|
||||
}
|
||||
|
||||
static OSStatus complexInputDataProc(AudioConverterRef inAudioConverter,
|
||||
UInt32 *ioNumberDataPackets,
|
||||
AudioBufferList *ioData,
|
||||
AudioStreamPacketDescription **outDataPacketDescription,
|
||||
void *inUserData) {
|
||||
struct complexInputDataProc_t *arg = (struct complexInputDataProc_t*)inUserData;
|
||||
if (!arg->sourceBuffer) {
|
||||
return kNoMoreDataErr;
|
||||
}
|
||||
|
||||
memcpy(ioData, arg->sourceBuffer, sizeof(AudioBufferList) + (arg->sourceBuffer->mNumberBuffers-1)*sizeof(AudioBuffer));
|
||||
arg->sourceBuffer = NULL;
|
||||
|
||||
return noErr;
|
||||
}
|
||||
|
||||
-(AudioStreamBasicDescription)floatingPointAudioDescription {
|
||||
return _floatAudioDescription;
|
||||
}
|
||||
|
||||
@end
|
||||
+214
-87
@@ -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
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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,10 +188,11 @@
|
||||
#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];
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
|
||||
-6
@@ -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 */,
|
||||
|
||||
-6
@@ -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 */,
|
||||
|
||||
-6
@@ -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 */,
|
||||
|
||||
-6
@@ -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 */,
|
||||
|
||||
+7
-5
@@ -196,12 +196,12 @@
|
||||
//
|
||||
// Clear the audio plot
|
||||
//
|
||||
[self.audioPlot clear];
|
||||
// [self.audioPlot clear];
|
||||
|
||||
//
|
||||
// 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;
|
||||
@@ -223,15 +223,17 @@
|
||||
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];
|
||||
}];
|
||||
}
|
||||
|
||||
+3
-3
@@ -75,7 +75,7 @@
|
||||
</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"/>
|
||||
<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>
|
||||
@@ -139,7 +139,7 @@
|
||||
<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>
|
||||
@@ -152,7 +152,7 @@
|
||||
<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>
|
||||
|
||||
-6
@@ -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 */,
|
||||
|
||||
-6
@@ -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 */,
|
||||
|
||||
+4
-3
@@ -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];
|
||||
}];
|
||||
}
|
||||
|
||||
-6
@@ -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 */,
|
||||
|
||||
-6
@@ -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 */,
|
||||
|
||||
-6
@@ -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 */,
|
||||
|
||||
-6
@@ -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 */,
|
||||
|
||||
+9
-9
@@ -22,7 +22,7 @@
|
||||
<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"/>
|
||||
<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"/>
|
||||
</constraints>
|
||||
@@ -37,7 +37,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 +48,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 +58,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 +66,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 +83,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>
|
||||
|
||||
+156
-116
@@ -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,8 +17,8 @@
|
||||
}
|
||||
|
||||
#pragma mark - Customize the Audio Plot
|
||||
-(void)viewDidLoad {
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
/*
|
||||
@@ -46,141 +42,185 @@
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
#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];
|
||||
}
|
||||
[EZOutput sharedOutput].outputDataSource = self;
|
||||
[[EZOutput sharedOutput] startPlayback];
|
||||
}
|
||||
else {
|
||||
[EZOutput sharedOutput].outputDataSource = nil;
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
- (void)openFileWithFilePathURL:(NSURL *)filePathURL
|
||||
{
|
||||
// Stop playback
|
||||
[[EZOutput sharedOutput] 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 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;
|
||||
|
||||
__weak typeof (self) weakSelf = self;
|
||||
[self.audioFile getWaveformDataWithCompletionBlock:^(float **waveformData,
|
||||
int length)
|
||||
{
|
||||
[weakSelf.audioPlot updateBuffer:waveformData[0]
|
||||
withBufferSize:length];
|
||||
}];
|
||||
}
|
||||
|
||||
-(void)seekToFrame:(id)sender {
|
||||
[self.audioFile seekToFrame:(SInt64)[(UISlider*)sender value]];
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
- (void)play:(id)sender
|
||||
{
|
||||
if (![[EZOutput sharedOutput] isPlaying])
|
||||
{
|
||||
if (self.eof)
|
||||
{
|
||||
[self.audioFile seekToFrame:0];
|
||||
}
|
||||
[EZOutput sharedOutput].outputDataSource = self;
|
||||
[[EZOutput sharedOutput] startPlayback];
|
||||
}
|
||||
else
|
||||
{
|
||||
[EZOutput sharedOutput].outputDataSource = nil;
|
||||
[[EZOutput sharedOutput] stopPlayback];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Action Extensions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
- (void)seekToFrame:(id)sender
|
||||
{
|
||||
[self.audioFile seekToFrame:(SInt64)[(UISlider *)sender value]];
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
#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 {
|
||||
//------------------------------------------------------------------------------
|
||||
#pragma mark - EZAudioFileDelegate
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
- (void) audioFile:(EZAudioFile *)audioFile
|
||||
readAudio:(float **)buffer
|
||||
withBufferSize:(UInt32)bufferSize
|
||||
withNumberOfChannels:(UInt32)numberOfChannels
|
||||
{
|
||||
__weak typeof (self) weakSelf = self;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if ([EZOutput sharedOutput].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];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
- (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
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
- (void) output:(EZOutput *)output
|
||||
shouldFillAudioBufferList:(AudioBufferList *)audioBufferList
|
||||
withNumberOfFrames:(UInt32)frames
|
||||
{
|
||||
if ( self.audioFile )
|
||||
{
|
||||
UInt32 bufferSize;
|
||||
[self.audioFile readFrames:frames
|
||||
audioBufferList:audioBufferList
|
||||
bufferSize:&bufferSize
|
||||
eof:&_eof];
|
||||
if ( _eof )
|
||||
{
|
||||
[self seekToFrame:0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
- (AudioStreamBasicDescription)outputHasAudioStreamBasicDescription:(EZOutput *)output
|
||||
{
|
||||
return self.audioFile.clientFormat;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@end
|
||||
|
||||
-6
@@ -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 */,
|
||||
|
||||
-6
@@ -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 */,
|
||||
|
||||
+4
-3
@@ -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];
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user