// // 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_t clamp_to_uint8(float x){ if(x >= 1.0) return 255; if(x <= 0.0) return 0; return (uint8_t)(255.0f * x); } @interface Spectographer (){ FFTTest * FFTHandler; AudioFile * audioFile; NSUInteger sample; float * left; float * right; } - (void)threadTask:(void (^)(CGImageRef))callback; - (CGImageRef)singleStep CF_RETURNS_RETAINED; @end @implementation Spectographer - (id)init{ if(self = [super init]){ FFTHandler = [[FFTTest alloc] init]; sample = 0; left = calloc(FFTHandler.acceptedSize, sizeof(float)); right = calloc(FFTHandler.acceptedSize, sizeof(float)); } return self; } - (void)openAudioFile:(NSURL *)url{ audioFile = [AudioFile audioFileFromURL: url]; assert(audioFile); } - (void)generate:(void (^)(CGImageRef))callback{ [NSThread detachNewThreadSelector:@selector(threadTask:) toTarget:self withObject:callback]; } - (void)threadTask:(void (^)(CGImageRef))callback{ CGImageRef cgImage = nil; while((cgImage = [self singleStep])){ callback(cgImage); CGImageRelease(cgImage); } } - (CGImageRef)singleStep{ unsigned int size = FFTHandler.acceptedSize; unsigned int width = 128; unsigned int height = size/8; uint8_t * rgba = calloc(width*height*4, sizeof(uint8_t)); unsigned int x = 0; for(; x < width; ++x){ if(![audioFile fillLeft:left andRight:right forRange:NSMakeRange(sample, size)]) break; [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.0f * left[y]); rgba[4*width*yy + 4*x + 1] = clamp_to_uint8(100.0f * right[y]); 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); return nil; } 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