diff --git a/SpectogramPrototype.xcodeproj/project.pbxproj b/SpectogramPrototype.xcodeproj/project.pbxproj index 3abb045..3a84dcd 100644 --- a/SpectogramPrototype.xcodeproj/project.pbxproj +++ b/SpectogramPrototype.xcodeproj/project.pbxproj @@ -29,6 +29,7 @@ 426665BC1869EF4B005D62AC /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 426665BB1869EF4B005D62AC /* CoreAudio.framework */; }; 426665C11869F194005D62AC /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 426665C01869F194005D62AC /* AudioToolbox.framework */; }; 426665C51869F50B005D62AC /* MediaPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 426665C41869F50B005D62AC /* MediaPlayer.framework */; }; + 42ABF54B187035F900B20D82 /* Spectographer.m in Sources */ = {isa = PBXBuildFile; fileRef = 42ABF54A187035F900B20D82 /* Spectographer.m */; }; 42E12A91186F122400866CB3 /* testMusic.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 42E12A90186F122400866CB3 /* testMusic.mp3 */; }; 42E12A94186F1F4C00866CB3 /* AudioFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 42E12A93186F1F4C00866CB3 /* AudioFile.m */; }; /* End PBXBuildFile section */ @@ -73,6 +74,8 @@ 426665BB1869EF4B005D62AC /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; }; 426665C01869F194005D62AC /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; }; 426665C41869F50B005D62AC /* MediaPlayer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaPlayer.framework; path = System/Library/Frameworks/MediaPlayer.framework; sourceTree = SDKROOT; }; + 42ABF549187035F900B20D82 /* Spectographer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Spectographer.h; sourceTree = ""; }; + 42ABF54A187035F900B20D82 /* Spectographer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Spectographer.m; sourceTree = ""; }; 42E12A90186F122400866CB3 /* testMusic.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = testMusic.mp3; sourceTree = ""; }; 42E12A92186F1F4C00866CB3 /* AudioFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioFile.h; sourceTree = ""; }; 42E12A93186F1F4C00866CB3 /* AudioFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AudioFile.m; sourceTree = ""; }; @@ -150,6 +153,8 @@ 424F84BD18661F8000687D3B /* Main_iPad.storyboard */, 424F84C018661F8000687D3B /* ViewController.h */, 424F84C118661F8000687D3B /* ViewController.m */, + 42ABF549187035F900B20D82 /* Spectographer.h */, + 42ABF54A187035F900B20D82 /* Spectographer.m */, 42E12A92186F1F4C00866CB3 /* AudioFile.h */, 42E12A93186F1F4C00866CB3 /* AudioFile.m */, 426665B31869A7CC005D62AC /* RuledScrollView.h */, @@ -293,6 +298,7 @@ buildActionMask = 2147483647; files = ( 42E12A94186F1F4C00866CB3 /* AudioFile.m in Sources */, + 42ABF54B187035F900B20D82 /* Spectographer.m in Sources */, 426665B81869CEFE005D62AC /* FFTTest.m in Sources */, 424F84C218661F8000687D3B /* ViewController.m in Sources */, 424F84B918661F8000687D3B /* AppDelegate.m in Sources */, diff --git a/SpectogramPrototype/AudioFile.h b/SpectogramPrototype/AudioFile.h index b61223c..49d641f 100644 --- a/SpectogramPrototype/AudioFile.h +++ b/SpectogramPrototype/AudioFile.h @@ -16,6 +16,6 @@ + (AudioFile*) audioFileFromURL:(NSURL*) url; // Reads nSamples of data (left channel), return actual number of samples read -- (unsigned int) fillArray:(float*)array withNumberOfSamples:(unsigned int)nSamples; +- (unsigned int) fillLeft:(float*)left andRight:(float*)right withNumberOfSamples:(unsigned int)nSamples; @end diff --git a/SpectogramPrototype/AudioFile.m b/SpectogramPrototype/AudioFile.m index e764897..e0cd994 100644 --- a/SpectogramPrototype/AudioFile.m +++ b/SpectogramPrototype/AudioFile.m @@ -62,7 +62,7 @@ #pragma mark #pragma mark Usage -- (unsigned int)fillArray:(float *)array withNumberOfSamples:(unsigned int)nSamples{ +- (unsigned int)fillLeft:(float *)left andRight:(float *)right withNumberOfSamples:(unsigned int)nSamples { AudioBufferList bufferList; bufferList.mNumberBuffers = 1; bufferList.mBuffers[0].mNumberChannels = 2; @@ -74,15 +74,16 @@ assert(err == 0); short * buffer = bufferList.mBuffers[0].mData; - float * end = array + nSamples; + float * leftEnd = left + nSamples; for(unsigned int i = 0; i < nFrames; ++i){ - *array++ = *buffer++ / 32768.0; - // skip right channel - buffer++; + *left++ = *buffer++ / 32768.0; + *right++ = *buffer++ / 32768.0; } - while(array != end){ - *array++ = 0.0; + // assume left and right are of the same size + while(left != leftEnd){ + *left++ = 0.0; + *right++ = 0.0; } free(bufferList.mBuffers[0].mData); diff --git a/SpectogramPrototype/Spectographer.h b/SpectogramPrototype/Spectographer.h new file mode 100644 index 0000000..d62284d --- /dev/null +++ b/SpectogramPrototype/Spectographer.h @@ -0,0 +1,16 @@ +// +// Spectographer.h +// SpectogramPrototype +// +// Created by Joshua Moerman on 29/12/13. +// Copyright (c) 2013 Joshua Moerman. All rights reserved. +// + +#import + +@interface Spectographer : NSObject + +- (void)openAudioFile:(NSURL*)url; +- (CGImageRef)generate; + +@end diff --git a/SpectogramPrototype/Spectographer.m b/SpectogramPrototype/Spectographer.m new file mode 100644 index 0000000..72f3b65 --- /dev/null +++ b/SpectogramPrototype/Spectographer.m @@ -0,0 +1,78 @@ +// +// Spectographer.m +// SpectogramPrototype +// +// Created by Joshua Moerman on 29/12/13. +// Copyright (c) 2013 Joshua Moerman. All rights reserved. +// + +#import "Spectographer.h" +#import "FFTTest.h" +#import "AudioFile.h" + +UInt8 clamp_to_uint8(float x){ + if(x >= 1.0) return 255; + if(x <= 0.0) return 0; + return 255.0 * x; +} + +@interface Spectographer (){ + FFTTest * FFTHandler; + AudioFile * audioFile; + float * left; + float * right; +} + +@end + +@implementation Spectographer + +- (id)init{ + if(self = [super init]){ + FFTHandler = [[FFTTest alloc] init]; + left = calloc(FFTHandler.acceptedSize, sizeof(float)); + right = calloc(FFTHandler.acceptedSize, sizeof(float)); + } + return self; +} + +- (void)openAudioFile:(NSURL *)url{ + audioFile = [AudioFile audioFileFromURL: url]; + assert(audioFile); +} + +- (CGImageRef)generate{ + unsigned int size = FFTHandler.acceptedSize; + unsigned int width = size/4; + unsigned int height = size/8; + + char * rgba = calloc(width*height*4, sizeof(char)); + + for(unsigned int x = 0; x < width; ++x){ + [audioFile fillLeft:left andRight:right withNumberOfSamples:size]; + [FFTHandler inPlaceFFT:left forSize:size]; + [FFTHandler inPlaceFFT:right forSize:size]; + for(unsigned int y = 0; y < height; ++y){ + unsigned int yy = height - y - 1; + rgba[4*width*yy + 4*x + 0] = clamp_to_uint8(100.0 * left[y]); + rgba[4*width*yy + 4*x + 1] = clamp_to_uint8(100.0 * right[y]); + rgba[4*width*yy + 4*x + 2] = clamp_to_uint8(10.0 * (left[y] + right[y])); + rgba[4*width*yy + 4*x + 3] = 0; + } + } + + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); + assert(colorSpace); + CGContextRef bitmapContext = CGBitmapContextCreate(rgba, width, height, 8, 4*width, colorSpace, (CGBitmapInfo)kCGImageAlphaNoneSkipLast); + assert(bitmapContext); + CGImageRef cgImage = CGBitmapContextCreateImage(bitmapContext); + assert(cgImage); + + CGContextRelease(bitmapContext); + CGColorSpaceRelease(colorSpace); + free(rgba); + + return cgImage; +} + +@end diff --git a/SpectogramPrototype/ViewController.m b/SpectogramPrototype/ViewController.m index 0d4c9ee..dc5f6e2 100644 --- a/SpectogramPrototype/ViewController.m +++ b/SpectogramPrototype/ViewController.m @@ -11,8 +11,7 @@ #import "ViewController.h" #import "RuledScrollView.h" -#import "FFTTest.h" -#import "AudioFile.h" +#import "Spectographer.h" typedef enum { iPad, @@ -38,16 +37,9 @@ device get_device(){ return device; } -UInt8 clamp_to_uint8(float x){ - if(x >= 1.0) return 255; - if(x <= 0.0) return 0; - return 255.0 * x; -} - @interface ViewController () { + Spectographer * spectographer; UIPopoverController * pop; - FFTTest * fft_handler; - float * data; } - (void) openAudioFile:(NSURL*)filePath; @property (nonatomic, strong) UILabel* header; @@ -73,51 +65,22 @@ UInt8 clamp_to_uint8(float x){ } - (void)openAudioFile:(NSURL *)filePath{ - AudioFile * audioFile = [AudioFile audioFileFromURL:filePath]; - - if(!fft_handler) - fft_handler = [[FFTTest alloc] init]; - - unsigned int size = fft_handler.acceptedSize; - unsigned int width = size/4; - unsigned int height = size/8; + if(!spectographer) + spectographer = [[Spectographer alloc] init]; - if(!data) - data = calloc(size, sizeof(float)); - - char * rgba = calloc(width*height*4, sizeof(char)); - - for(unsigned int x = 0; x < width; ++x){ - [audioFile fillArray:data withNumberOfSamples:size]; - [fft_handler inPlaceFFT:data forSize:size]; - for(unsigned int y = 0; y < height; ++y){ - unsigned int yy = height - y - 1; - rgba[4*width*yy + 4*x + 0] = clamp_to_uint8(100.0 * data[y]); - rgba[4*width*yy + 4*x + 1] = clamp_to_uint8(50.0 * data[y]); - rgba[4*width*yy + 4*x + 2] = clamp_to_uint8(10.0 * data[y]); - rgba[4*width*yy + 4*x + 3] = 0; - } - } + [spectographer openAudioFile:filePath]; + CGImageRef image = [spectographer generate]; - CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); - assert(colorSpace); - CGContextRef bitmapContext = CGBitmapContextCreate(rgba, width, height, 8, 4*width, colorSpace, (CGBitmapInfo)kCGImageAlphaNoneSkipLast); - assert(bitmapContext); - CGImageRef cgImage = CGBitmapContextCreateImage(bitmapContext); - assert(cgImage); - UIImage * newUIImage = [UIImage imageWithCGImage:cgImage]; + UIImage * newUIImage = [UIImage imageWithCGImage:image]; assert(newUIImage); - CGColorSpaceRelease(colorSpace); - CGContextRelease(bitmapContext); - CGImageRelease(cgImage); - free(rgba); + CGImageRelease(image); UIImageView * view = [[UIImageView alloc] initWithImage:newUIImage]; - view.frame = CGRectMake(0, 0, width, height); [contentView removeFromSuperview]; contentView = view; + scrollView.zoomScale = 1.0; [scrollView.content addSubview:contentView]; contentView.autoresizingMask = UIViewAutoresizingNone;