diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d3dcc0a --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ + +*xcworkspace +*xcuserdata +build + diff --git a/SpectogramPrototype.xcodeproj/project.pbxproj b/SpectogramPrototype.xcodeproj/project.pbxproj index 840008b..6b85503 100644 --- a/SpectogramPrototype.xcodeproj/project.pbxproj +++ b/SpectogramPrototype.xcodeproj/project.pbxproj @@ -22,6 +22,13 @@ 424F84CD18661F8000687D3B /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 424F84AC18661F8000687D3B /* UIKit.framework */; }; 424F84D518661F8000687D3B /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 424F84D318661F8000687D3B /* InfoPlist.strings */; }; 424F84D718661F8000687D3B /* SpectogramPrototypeTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 424F84D618661F8000687D3B /* SpectogramPrototypeTests.m */; }; + 4261F08818671ECD00AA0EF9 /* ContentView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4261F08718671ECD00AA0EF9 /* ContentView.xib */; }; + 426665B51869A7CC005D62AC /* RuledScrollView.m in Sources */ = {isa = PBXBuildFile; fileRef = 426665B41869A7CC005D62AC /* RuledScrollView.m */; }; + 426665B81869CEFE005D62AC /* FFTTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 426665B71869CEFE005D62AC /* FFTTest.m */; }; + 426665BA1869CF73005D62AC /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 426665B91869CF73005D62AC /* Accelerate.framework */; }; + 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 */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -55,6 +62,15 @@ 424F84D218661F8000687D3B /* SpectogramPrototypeTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "SpectogramPrototypeTests-Info.plist"; sourceTree = ""; }; 424F84D418661F8000687D3B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 424F84D618661F8000687D3B /* SpectogramPrototypeTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SpectogramPrototypeTests.m; sourceTree = ""; }; + 4261F08718671ECD00AA0EF9 /* ContentView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ContentView.xib; sourceTree = ""; }; + 426665B31869A7CC005D62AC /* RuledScrollView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RuledScrollView.h; sourceTree = ""; }; + 426665B41869A7CC005D62AC /* RuledScrollView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RuledScrollView.m; sourceTree = ""; }; + 426665B61869CEFE005D62AC /* FFTTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FFTTest.h; sourceTree = ""; }; + 426665B71869CEFE005D62AC /* FFTTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FFTTest.m; sourceTree = ""; }; + 426665B91869CF73005D62AC /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; }; + 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; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -62,6 +78,10 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 426665C51869F50B005D62AC /* MediaPlayer.framework in Frameworks */, + 426665C11869F194005D62AC /* AudioToolbox.framework in Frameworks */, + 426665BC1869EF4B005D62AC /* CoreAudio.framework in Frameworks */, + 426665BA1869CF73005D62AC /* Accelerate.framework in Frameworks */, 424F84AB18661F8000687D3B /* CoreGraphics.framework in Frameworks */, 424F84AD18661F8000687D3B /* UIKit.framework in Frameworks */, 424F84A918661F8000687D3B /* Foundation.framework in Frameworks */, @@ -103,6 +123,10 @@ 424F84A718661F8000687D3B /* Frameworks */ = { isa = PBXGroup; children = ( + 426665C41869F50B005D62AC /* MediaPlayer.framework */, + 426665C01869F194005D62AC /* AudioToolbox.framework */, + 426665BB1869EF4B005D62AC /* CoreAudio.framework */, + 426665B91869CF73005D62AC /* Accelerate.framework */, 424F84A818661F8000687D3B /* Foundation.framework */, 424F84AA18661F8000687D3B /* CoreGraphics.framework */, 424F84AC18661F8000687D3B /* UIKit.framework */, @@ -120,6 +144,11 @@ 424F84BD18661F8000687D3B /* Main_iPad.storyboard */, 424F84C018661F8000687D3B /* ViewController.h */, 424F84C118661F8000687D3B /* ViewController.m */, + 426665B31869A7CC005D62AC /* RuledScrollView.h */, + 426665B41869A7CC005D62AC /* RuledScrollView.m */, + 426665B61869CEFE005D62AC /* FFTTest.h */, + 426665B71869CEFE005D62AC /* FFTTest.m */, + 4261F08718671ECD00AA0EF9 /* ContentView.xib */, 424F84C318661F8000687D3B /* Images.xcassets */, 424F84AF18661F8000687D3B /* Supporting Files */, ); @@ -234,6 +263,7 @@ 424F84BF18661F8000687D3B /* Main_iPad.storyboard in Resources */, 424F84C418661F8000687D3B /* Images.xcassets in Resources */, 424F84BC18661F8000687D3B /* Main_iPhone.storyboard in Resources */, + 4261F08818671ECD00AA0EF9 /* ContentView.xib in Resources */, 424F84B318661F8000687D3B /* InfoPlist.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -253,8 +283,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 426665B81869CEFE005D62AC /* FFTTest.m in Sources */, 424F84C218661F8000687D3B /* ViewController.m in Sources */, 424F84B918661F8000687D3B /* AppDelegate.m in Sources */, + 426665B51869A7CC005D62AC /* RuledScrollView.m in Sources */, 424F84B518661F8000687D3B /* main.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -474,6 +506,7 @@ 424F84DC18661F8000687D3B /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; 424F84DD18661F8000687D3B /* Build configuration list for PBXNativeTarget "SpectogramPrototypeTests" */ = { isa = XCConfigurationList; @@ -482,6 +515,7 @@ 424F84DF18661F8000687D3B /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; diff --git a/SpectogramPrototype/Base.lproj/Main_iPad.storyboard b/SpectogramPrototype/Base.lproj/Main_iPad.storyboard index a185e8a..efd12b3 100644 --- a/SpectogramPrototype/Base.lproj/Main_iPad.storyboard +++ b/SpectogramPrototype/Base.lproj/Main_iPad.storyboard @@ -1,21 +1,64 @@ - + - + - - + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - + + diff --git a/SpectogramPrototype/ContentView.xib b/SpectogramPrototype/ContentView.xib new file mode 100644 index 0000000..a4c913c --- /dev/null +++ b/SpectogramPrototype/ContentView.xib @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SpectogramPrototype/FFTTest.h b/SpectogramPrototype/FFTTest.h new file mode 100644 index 0000000..4d2b360 --- /dev/null +++ b/SpectogramPrototype/FFTTest.h @@ -0,0 +1,14 @@ +// +// FFTTest.h +// SpectogramPrototype +// +// Created by Joshua Moerman on 24/12/13. +// Copyright (c) 2013 Joshua Moerman. All rights reserved. +// + +#import + +@interface FFTTest : NSObject +- (void) inPlaceFFT:(float*)data forSize:(unsigned int)n; +@property (readonly) unsigned int acceptedSize; +@end diff --git a/SpectogramPrototype/FFTTest.m b/SpectogramPrototype/FFTTest.m new file mode 100644 index 0000000..ba42483 --- /dev/null +++ b/SpectogramPrototype/FFTTest.m @@ -0,0 +1,77 @@ +// +// FFTTest.m +// SpectogramPrototype +// +// Created by Joshua Moerman on 24/12/13. +// Copyright (c) 2013 Joshua Moerman. All rights reserved. +// + +#import "FFTTest.h" +#import + +@interface FFTTest () { + FFTSetup setup; + float * window; + float * buffer; +} + +@end + +const int logN = 3; +const int N = 1 << logN; +const float scaling = 1.0 / N; + +@implementation FFTTest +@synthesize acceptedSize; + +- (id)init{ + if(self = [super init]){ + acceptedSize = N; + + setup = vDSP_create_fftsetup(logN, kFFTRadix2); + window = calloc(N, sizeof(float)); + buffer = calloc(N, sizeof(float)); + + assert(setup && window && buffer); + } + return self; +} + +- (void)dealloc{ + free(buffer); + free(window); + vDSP_destroy_fftsetup(setup); +} + +- (void)inPlaceFFT:(float *)data forSize:(unsigned int)n{ + assert(n == N); + + // Output the source data + for(int i = 0; i < N; ++i){ + printf("%f, ", data[i]); + } + printf("\n"); + + // Window our time - slice + // vDSP_blkman_window(window, N, 0); + // vmul(data, 1, window, 1, data, 1, N); + + // Rearrange data + DSPSplitComplex split_buffer = { buffer, buffer + N/2 }; + vDSP_ctoz((DSPComplex *)data, 2, &split_buffer, 1, N/2); + + // DO IT! + vDSP_fft_zrip(setup, &split_buffer, 1, logN, FFT_FORWARD); + + // Calculates magnitudes (so ignores phases) + vDSP_zvabs(&split_buffer, 1, data, 1, N/2); + vDSP_vsmul(data, 1, &scaling, data, 1, N/2); + + // Output results: + for(int i = 0; i < N/2; ++i){ + printf("%f, ", data[i]); + } + printf("\n"); + +} +@end diff --git a/SpectogramPrototype/RuledScrollView.h b/SpectogramPrototype/RuledScrollView.h new file mode 100644 index 0000000..9b40647 --- /dev/null +++ b/SpectogramPrototype/RuledScrollView.h @@ -0,0 +1,15 @@ +// +// RuledScrollView.h +// SpectogramPrototype +// +// Created by Joshua Moerman on 24/12/13. +// Copyright (c) 2013 Joshua Moerman. All rights reserved. +// + +#import + +@interface RuledScrollView : UIScrollView +@property (nonatomic, readonly) UIView * content; +@property (nonatomic, readonly) UIView * topRuler; +@property (nonatomic, readonly) UIView * leftRuler; +@end diff --git a/SpectogramPrototype/RuledScrollView.m b/SpectogramPrototype/RuledScrollView.m new file mode 100644 index 0000000..8c41fb7 --- /dev/null +++ b/SpectogramPrototype/RuledScrollView.m @@ -0,0 +1,109 @@ +// +// RuledScrollView.m +// SpectogramPrototype +// +// Created by Joshua Moerman on 24/12/13. +// Copyright (c) 2013 Joshua Moerman. All rights reserved. +// + +#import "RuledScrollView.h" + +@interface RuledScrollView () { + CGFloat rulerHeight; + CGFloat rulerWidth; +} +- (void) initView; +- (void) placeTicks; +@end + +@implementation RuledScrollView +@synthesize content, topRuler, leftRuler; + +- (id)initWithFrame:(CGRect)frame{ + if(self = [super initWithFrame:frame]){ + [self initView]; + } + return self; +} + +- (id)initWithCoder:(NSCoder *)aDecoder{ + if(self = [super initWithCoder:aDecoder]){ + [self initView]; + } + return self; +} + +- (void)initView { + rulerHeight = 44; + rulerWidth = 120; + + self.delegate = self; + self.backgroundColor = [UIColor whiteColor]; + + content = [[UIView alloc] initWithFrame:self.frame]; + content.autoresizesSubviews = NO; + [self addSubview:content]; + + CGRect frame2 = self.frame; + frame2.size.height = rulerHeight; + topRuler = [[UIView alloc] initWithFrame:frame2]; + topRuler.layer.opacity = 0.5; + topRuler.backgroundColor = self.backgroundColor; + [self addSubview:topRuler]; + + CGRect frame3 = self.frame; + frame3.size.width = rulerWidth; + leftRuler = [[UIView alloc] initWithFrame:frame3]; + leftRuler.layer.opacity = 0.5; + leftRuler.backgroundColor = self.backgroundColor; + [self addSubview:leftRuler]; +} + +/* we should draw the ticks in drawRect: + [[UIColor blackColor] setFill]; + UIRectFill((CGRect){0,200,rect.size.width,1}); + + and do tiling for the text + */ + +- (void)placeTicks { + [topRuler.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)]; + for (CGFloat x = rulerWidth + 50; x < topRuler.frame.size.width; x += 100){ + UIView * line = [[UIView alloc] initWithFrame:CGRectMake(x, 0, 1, rulerHeight)]; + line.backgroundColor = [UIColor blackColor]; + [topRuler addSubview:line]; + + CGFloat width = 100; + UILabel * text = [[UILabel alloc] initWithFrame:CGRectMake(x - width/2, 0, width, rulerHeight)]; + text.text = [NSString stringWithFormat:@"%.0f", x]; + text.textAlignment = NSTextAlignmentCenter; + [topRuler addSubview:text]; + } +} + +- (void)layoutSubviews { + { + CGRect frame = self.topRuler.frame; + frame.origin.y = self.contentOffset.y; + self.topRuler.frame = frame; + [self placeTicks]; + } + { + CGRect frame = self.leftRuler.frame; + frame.origin.x = self.contentOffset.x; + self.leftRuler.frame = frame; + } +} + +- (void)setContentSize:(CGSize)contentSize{ + [super setContentSize:CGSizeMake(contentSize.width + rulerWidth, contentSize.height + rulerHeight)]; + content.frame = CGRectMake(rulerWidth, rulerHeight, contentSize.width, contentSize.height); + topRuler.frame = CGRectMake(0, 0, contentSize.width + rulerWidth, rulerHeight); + leftRuler.frame = CGRectMake(0, 0, rulerWidth, contentSize.height + rulerHeight); +} + +- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView{ + return content; +} + +@end diff --git a/SpectogramPrototype/ViewController.h b/SpectogramPrototype/ViewController.h index f76cafb..6c24cfb 100644 --- a/SpectogramPrototype/ViewController.h +++ b/SpectogramPrototype/ViewController.h @@ -8,6 +8,14 @@ #import +@class RuledScrollView; + @interface ViewController : UIViewController +@property (nonatomic) IBOutlet RuledScrollView * scrollView; +@property (nonatomic) IBOutlet UIView * contentView; + +- (IBAction) calculateButtonPressed:(id)sender; +- (IBAction) mediaButtonPressed:(UIButton*)sender; + @end diff --git a/SpectogramPrototype/ViewController.m b/SpectogramPrototype/ViewController.m index e99198b..a4a57a3 100644 --- a/SpectogramPrototype/ViewController.m +++ b/SpectogramPrototype/ViewController.m @@ -6,24 +6,105 @@ // Copyright (c) 2013 Joshua Moerman. All rights reserved. // -#import "ViewController.h" +#import +#import -@interface ViewController () +#import "ViewController.h" +#import "RuledScrollView.h" +#import "FFTTest.h" +@interface ViewController () { + UIPopoverController * pop; + FFTTest * fft_handler; +} +- (void) openAudioFile:(NSURL*)filePath; +@property (nonatomic, strong) UILabel* header; @end @implementation ViewController -- (void)viewDidLoad -{ - [super viewDidLoad]; - // Do any additional setup after loading the view, typically from a nib. +@synthesize scrollView, contentView, header; + +- (void)viewDidLoad{ + [super viewDidLoad]; + + if(!contentView){ + [[NSBundle mainBundle] loadNibNamed:@"ContentView" owner:self options:nil]; + [scrollView.content addSubview:contentView]; + contentView.autoresizingMask = UIViewAutoresizingNone; + scrollView.contentSize = contentView.bounds.size; + } +} + +- (void)didReceiveMemoryWarning{ + [super didReceiveMemoryWarning]; +} + +- (void)calculateButtonPressed:(id)sender{ + if(!fft_handler) + fft_handler = [[FFTTest alloc] init]; + + unsigned int size = fft_handler.acceptedSize; + float * data = calloc(size, sizeof(float)); + [fft_handler inPlaceFFT:data forSize:size]; + free(data); +} + +- (void)openAudioFile:(NSURL *)filePath{ + OSStatus err; + + ExtAudioFileRef audioFile = NULL; + err = ExtAudioFileOpenURL((__bridge CFURLRef) filePath, &audioFile); + assert(err == 0); + + AudioBufferList bufferList; + bufferList.mNumberBuffers = 1; + bufferList.mBuffers[0].mNumberChannels = 2; + bufferList.mBuffers[0].mDataByteSize = 32768; + bufferList.mBuffers[0].mData = calloc(32768, 1); + + while(true){ + bufferList.mBuffers[0].mDataByteSize = 32768; + + UInt32 nFrames = 32768 / 6; + err = ExtAudioFileRead(audioFile, &nFrames, &bufferList); + assert(err == 0); + if(!nFrames) break; + } + + NSLog(@"Read some bits"); + + err = ExtAudioFileDispose(audioFile); + assert(err == 0); +} + +- (void)mediaButtonPressed:(UIButton*)sender{ + MPMediaPickerController * mppc = [[MPMediaPickerController alloc] initWithMediaTypes:MPMediaTypeAnyAudio]; + mppc.delegate = self; + mppc.allowsPickingMultipleItems = NO; + mppc.showsCloudItems = NO; + + if(pop){ + [pop dismissPopoverAnimated:YES]; + pop = nil; + } + + pop = [[UIPopoverController alloc] initWithContentViewController:mppc]; + [pop presentPopoverFromRect:sender.frame inView:self.view permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES]; +} + +- (void)mediaPicker:(MPMediaPickerController *)mediaPicker didPickMediaItems:(MPMediaItemCollection *)mediaItemCollection { + for(MPMediaItem * mediaItem in mediaItemCollection.items){ + NSURL * url = [mediaItem valueForProperty:MPMediaItemPropertyAssetURL]; + [self openAudioFile:url]; + } + [pop dismissPopoverAnimated:YES]; + pop = nil; } -- (void)didReceiveMemoryWarning -{ - [super didReceiveMemoryWarning]; - // Dispose of any resources that can be recreated. +- (void)mediaPickerDidCancel:(MPMediaPickerController *)mediaPicker { + [pop dismissPopoverAnimated:YES]; + pop = nil; } @end