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.
100 lines
2.5 KiB
100 lines
2.5 KiB
//
|
|
// 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
|
|
|