Music to spectrogram on iOS with the accelerate framework
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.
 

159 lines
3.9 KiB

//
// 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