diff --git a/SpectogramPrototype/AudioFile.h b/SpectogramPrototype/AudioFile.h index 49d641f..718d4bb 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) fillLeft:(float*)left andRight:(float*)right withNumberOfSamples:(unsigned int)nSamples; +- (BOOL) fillLeft:(float*)left andRight:(float*)right forRange:(NSRange)range; @end diff --git a/SpectogramPrototype/AudioFile.m b/SpectogramPrototype/AudioFile.m index eb3f353..74845e7 100644 --- a/SpectogramPrototype/AudioFile.m +++ b/SpectogramPrototype/AudioFile.m @@ -9,12 +9,29 @@ #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 @@ -25,6 +42,7 @@ - (id)initWithExtAudioFileRef:(ExtAudioFileRef) af{ if(self = [super init]){ audioFile = af; + buffers = [[NSMutableArray alloc] init]; [self setupFormat]; } return self; @@ -59,22 +77,45 @@ return [[AudioFile alloc] initWithExtAudioFileRef:audioFile]; } -#pragma mark -#pragma mark Usage +- (Buffer *)getBufferForSample:(NSUInteger)sample{ + for (Buffer * b in buffers) { + if(NSLocationInRange(sample, b->range)){ + return b; + } + } + -- (unsigned int)fillLeft:(float *)left andRight:(float *)right withNumberOfSamples:(unsigned int)nSamples { - AudioBufferList bufferList; + 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 = nSamples * format.mBytesPerFrame; + bufferList.mBuffers[0].mDataByteSize = buffer_size * format.mBytesPerFrame; bufferList.mBuffers[0].mData = calloc(bufferList.mBuffers[0].mDataByteSize, 1); - UInt32 nFrames = nSamples; + UInt32 nFrames = buffer_size; OSStatus err = ExtAudioFileRead(audioFile, &nFrames, &bufferList); assert(err == 0); short * buffer = bufferList.mBuffers[0].mData; - float * leftEnd = left + nSamples; + + 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; @@ -85,10 +126,34 @@ *left++ = 0; *right++ = 0; } + + b->range.length = nFrames; free(bufferList.mBuffers[0].mData); - - return nFrames; + + 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 diff --git a/SpectogramPrototype/Base.lproj/Main_iPad.storyboard b/SpectogramPrototype/Base.lproj/Main_iPad.storyboard index f4f160c..8d6afe3 100644 --- a/SpectogramPrototype/Base.lproj/Main_iPad.storyboard +++ b/SpectogramPrototype/Base.lproj/Main_iPad.storyboard @@ -1,5 +1,5 @@ - + @@ -43,6 +43,13 @@ + + + + + + + diff --git a/SpectogramPrototype/FFTTest.m b/SpectogramPrototype/FFTTest.m index f187254..4864cb4 100644 --- a/SpectogramPrototype/FFTTest.m +++ b/SpectogramPrototype/FFTTest.m @@ -17,7 +17,7 @@ @end -const int logN = 12; +const int logN = 13; const int N = 1 << logN; const float scaling = 1.0 / N; diff --git a/SpectogramPrototype/RuledScrollView.m b/SpectogramPrototype/RuledScrollView.m index 94dbd91..eb4c428 100644 --- a/SpectogramPrototype/RuledScrollView.m +++ b/SpectogramPrototype/RuledScrollView.m @@ -54,6 +54,7 @@ frame3.size.width = rulerWidth; leftRuler = [[VerticalRuler alloc] initWithFrame:frame3]; leftRuler.layer.opacity = 0.5; + [(VerticalRuler*)leftRuler setTuning: 440.0f]; [self addSubview:leftRuler]; } diff --git a/SpectogramPrototype/Ruler.h b/SpectogramPrototype/Ruler.h index 6f53877..51cc46f 100644 --- a/SpectogramPrototype/Ruler.h +++ b/SpectogramPrototype/Ruler.h @@ -21,4 +21,5 @@ typedef struct Interval { @end @interface VerticalRuler : AbstractRuler +@property (nonatomic) float tuning; @end diff --git a/SpectogramPrototype/Ruler.m b/SpectogramPrototype/Ruler.m index 03266ac..97a8131 100644 --- a/SpectogramPrototype/Ruler.m +++ b/SpectogramPrototype/Ruler.m @@ -63,6 +63,12 @@ @implementation VerticalRuler +@synthesize tuning; + +- (void)setTuning:(float)tuning_{ + tuning = tuning_; + [self setNeedsDisplay]; +} - (UIColor*)colorForKey:(int)j{ switch (j) { @@ -90,22 +96,22 @@ const CGFloat d = visibleInterval.end - visibleInterval.begin; const CGFloat f = 44100; - const CGFloat N = 1 << 12; - const CGFloat height = (1 << 12) >> 3; + const CGFloat N = 1 << 13; + const CGFloat height = (1 << 13) >> 3; - for(int i = 220; i < 44100/8; i *= 2){ + for(int i = 110; i < 44100/8; i *= 2){ for(int j = 0; j < 12; ++j){ - const CGFloat k = i * pow(2, j/12.0); + const CGFloat k = (tuning/440.0) * i * pow(2, j/12.0); const CGFloat y = N*k/f; const CGFloat y2 = (height - y); - const CGFloat gy = 1024 * (y2 - visibleInterval.begin) / d; + const CGFloat gy = self.bounds.size.height * (y2 - visibleInterval.begin) / d; [[self colorForKey:j] setFill]; if([UIScreen isRetinaDisplay]){ - UIRectFill(CGRectMake(0, gy-0.5, self.bounds.size.width, 1.0)); + UIRectFill(CGRectMake(0, gy, self.bounds.size.width, 0.5)); } else { - UIRectFill(CGRectMake(0, gy-1, self.bounds.size.width, 2.0)); + UIRectFill(CGRectMake(0, gy, self.bounds.size.width, 1.0)); } } } diff --git a/SpectogramPrototype/Spectographer.m b/SpectogramPrototype/Spectographer.m index 9b0b9bf..0f210e4 100644 --- a/SpectogramPrototype/Spectographer.m +++ b/SpectogramPrototype/Spectographer.m @@ -19,6 +19,7 @@ uint8_t clamp_to_uint8(float x){ @interface Spectographer (){ FFTTest * FFTHandler; AudioFile * audioFile; + NSUInteger sample; float * left; float * right; } @@ -31,6 +32,7 @@ uint8_t clamp_to_uint8(float x){ - (id)init{ if(self = [super init]){ FFTHandler = [[FFTTest alloc] init]; + sample = 0; left = calloc(FFTHandler.acceptedSize, sizeof(float)); right = calloc(FFTHandler.acceptedSize, sizeof(float)); } @@ -63,7 +65,7 @@ uint8_t clamp_to_uint8(float x){ unsigned int x = 0; for(; x < width; ++x){ - if(![audioFile fillLeft:left andRight:right withNumberOfSamples:size]) + if(![audioFile fillLeft:left andRight:right forRange:NSMakeRange(sample, size)]) break; [FFTHandler inPlaceFFT:left forSize:size]; [FFTHandler inPlaceFFT:right forSize:size]; @@ -74,6 +76,7 @@ uint8_t clamp_to_uint8(float x){ rgba[4*width*yy + 4*x + 2] = clamp_to_uint8(10.0f * (left[y] + right[y])); rgba[4*width*yy + 4*x + 3] = 0; } + sample += size / 8; } if(!x){ free(rgba); diff --git a/SpectogramPrototype/ViewController.h b/SpectogramPrototype/ViewController.h index 70d4992..8437490 100644 --- a/SpectogramPrototype/ViewController.h +++ b/SpectogramPrototype/ViewController.h @@ -17,5 +17,6 @@ - (IBAction)mediaButtonPressed:(UIButton*)sender; - (IBAction)resetView:(id)sender; +- (IBAction)tuningChanged:(UISlider*)sender; @end diff --git a/SpectogramPrototype/ViewController.m b/SpectogramPrototype/ViewController.m index 03e6dad..9e48d85 100644 --- a/SpectogramPrototype/ViewController.m +++ b/SpectogramPrototype/ViewController.m @@ -11,6 +11,7 @@ #import "ViewController.h" #import "RuledScrollView.h" #import "Spectographer.h" +#import "Ruler.h" typedef enum { iPad, @@ -175,4 +176,8 @@ device get_device(){ [mediaPicker dismissViewControllerAnimated:YES completion:nil]; } +- (void)tuningChanged:(UISlider*)sender{ + [(VerticalRuler*)scrollView.leftRuler setTuning:sender.value]; +} + @end diff --git a/Untitled.md b/Untitled.md new file mode 100644 index 0000000..a47d540 --- /dev/null +++ b/Untitled.md @@ -0,0 +1,23 @@ +### Output of FFT: + +``` + {[DC,NY],C[1],C[2],...,C[n/2]} +``` + +### On a sample basis +Let ps stand for "per sample" + +``` C[1] ``` gives the ```1/N``` ps coefficient + +``` C[2] ``` gives the ```2/N``` ps coefficient + +``` C[N/2] ``` gives the ```1/2``` ps coefficient ### In seconds Let ```f``` (=44100) be the samplerate. Then: + +``` C[1] ``` gives the ```f/N``` hz coefficient + +``` C[2] ``` gives the ```2f/N``` hz coefficient + +``` C[N/2] ``` gives the ```f/2``` hz coefficient + +### From herz to position +Let ```h``` be some frequency. Then from the above we see that for ``` m := Nh/f ``` we get the ```mf/N = h``` hz coefficient in ``` C[m]```. \ No newline at end of file