// // AudioFile.m // SpectogramPrototype // // Created by Joshua Moerman on 28/12/13. // Copyright (c) 2013 Joshua Moerman. All rights reserved. // #import "AudioFile.h" @import AudioToolbox.ExtendedAudioFile; static const size_t buffer_size = 1 << 12; @interface Buffer : NSObject { @public float left[buffer_size]; float right[buffer_size]; NSRange range; } @end @implementation Buffer @end @interface AudioFile (){ ExtAudioFileRef audioFile; AudioStreamBasicDescription format; NSMutableArray * buffers; } - (id)initWithExtAudioFileRef:(ExtAudioFileRef) audioFile; - (void)setupFormat; - (Buffer*)getBufferForSample:(NSUInteger) sample; - (Buffer*)appendBuffer; @end @implementation AudioFile #pragma mark #pragma mark Creation - (id)initWithExtAudioFileRef:(ExtAudioFileRef) af{ if(self = [super init]){ audioFile = af; buffers = [[NSMutableArray alloc] init]; [self setupFormat]; } return self; } - (void)setupFormat{ format.mSampleRate = 44100.0f; format.mFormatID = kAudioFormatLinearPCM; format.mFormatFlags = kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsSignedInteger; format.mBytesPerPacket = 4; format.mFramesPerPacket = 1; format.mBytesPerFrame = 4; format.mChannelsPerFrame = 2; format.mBitsPerChannel = 16; format.mReserved = 0; UInt32 size = sizeof(format); OSStatus err = ExtAudioFileSetProperty(audioFile, kExtAudioFileProperty_ClientDataFormat, size, &format); assert(err == 0); } - (void)dealloc { OSStatus err = ExtAudioFileDispose(audioFile); assert(err == 0); } + (AudioFile *)audioFileFromURL:(NSURL *)url{ ExtAudioFileRef audioFile = NULL; OSStatus err = ExtAudioFileOpenURL((__bridge CFURLRef) url, &audioFile); assert(err == 0 && audioFile); return [[AudioFile alloc] initWithExtAudioFileRef:audioFile]; } - (Buffer *)getBufferForSample:(NSUInteger)sample{ for (Buffer * b in buffers) { if(NSLocationInRange(sample, b->range)){ return b; } } while(1){ Buffer * b = [self appendBuffer]; if(!b) return nil; if(NSLocationInRange(sample, b->range)) return b; } } - (Buffer *)appendBuffer{ Buffer * b = [[Buffer alloc] init]; Buffer * last = [buffers lastObject]; if(last){ b->range.location = last->range.location + last->range.length; } else { b->range.location = 0; } AudioBufferList bufferList; bufferList.mNumberBuffers = 1; bufferList.mBuffers[0].mNumberChannels = 2; bufferList.mBuffers[0].mDataByteSize = buffer_size * format.mBytesPerFrame; bufferList.mBuffers[0].mData = calloc(bufferList.mBuffers[0].mDataByteSize, 1); UInt32 nFrames = buffer_size; OSStatus err = ExtAudioFileRead(audioFile, &nFrames, &bufferList); assert(err == 0); short * buffer = bufferList.mBuffers[0].mData; float * left = b->left; float * right = b->right; float * leftEnd = left + buffer_size; for(unsigned int i = 0; i < nFrames; ++i){ *left++ = *buffer++ / 32768.0f; *right++ = *buffer++ / 32768.0f; } // assume left and right are of the same size while(left != leftEnd){ *left++ = 0; *right++ = 0; } b->range.length = nFrames; free(bufferList.mBuffers[0].mData); if(!nFrames) return nil; [buffers addObject:b]; return b; } #pragma mark #pragma mark Usage - (BOOL)fillLeft:(float *)left andRight:(float *)right forRange:(NSRange)range{ NSUInteger n = range.length; Buffer * b = nil; while ((b = [self getBufferForSample:range.location])) { while(range.length && NSLocationInRange(range.location, b->range)){ *left++ = b->left[range.location - b->range.location]; *right++ = b->right[range.location - b->range.location]; range.location++; range.length--; } if(range.length == 0) break; } return range.length < n; } @end