(committing very old stuff) Lots of changes, no clue
This commit is contained in:
parent
276e840682
commit
61f4cc724b
11 changed files with 132 additions and 20 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="4514" systemVersion="13B42" targetRuntime="iOS.CocoaTouch.iPad" propertyAccessControl="none" useAutolayout="YES" initialViewController="DUW-zh-mwd">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="4514" systemVersion="13C64" targetRuntime="iOS.CocoaTouch.iPad" propertyAccessControl="none" useAutolayout="YES" initialViewController="DUW-zh-mwd">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="3747"/>
|
||||
</dependencies>
|
||||
|
@ -43,6 +43,13 @@
|
|||
<action selector="resetView:" destination="DUW-zh-mwd" eventType="touchUpInside" id="xv9-4L-1Ve"/>
|
||||
</connections>
|
||||
</button>
|
||||
<slider opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="440" minValue="400" maxValue="480" translatesAutoresizingMaskIntoConstraints="NO" id="ywc-Ak-o9S">
|
||||
<rect key="frame" x="20" y="100" width="200" height="34"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<connections>
|
||||
<action selector="tuningChanged:" destination="DUW-zh-mwd" eventType="valueChanged" id="ed0-8G-vOh"/>
|
||||
</connections>
|
||||
</slider>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
<constraints>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
@end
|
||||
|
||||
const int logN = 12;
|
||||
const int logN = 13;
|
||||
const int N = 1 << logN;
|
||||
const float scaling = 1.0 / N;
|
||||
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
||||
|
|
|
@ -21,4 +21,5 @@ typedef struct Interval {
|
|||
@end
|
||||
|
||||
@interface VerticalRuler : AbstractRuler
|
||||
@property (nonatomic) float tuning;
|
||||
@end
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -17,5 +17,6 @@
|
|||
|
||||
- (IBAction)mediaButtonPressed:(UIButton*)sender;
|
||||
- (IBAction)resetView:(id)sender;
|
||||
- (IBAction)tuningChanged:(UISlider*)sender;
|
||||
|
||||
@end
|
||||
|
|
|
@ -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
|
||||
|
|
23
Untitled.md
Normal file
23
Untitled.md
Normal file
|
@ -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]```.
|
Reference in a new issue