Music to spectrogram on iOS with the accelerate framework
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.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.

101 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