Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

added freq reverb plot

  • Loading branch information...
commit ca979501896edc5b2aa6047517692a0606f11e85 1 parent 5eb087a
@starzia authored
View
3  .gitignore
@@ -0,0 +1,3 @@
+*/project.xcworkspace
+*/xcuserdata
+*~
View
6 ClapIR.xcodeproj/project.pbxproj
@@ -17,6 +17,7 @@
AA049A7D15043D1700E74262 /* ClapMeasurement.m in Sources */ = {isa = PBXBuildFile; fileRef = AA049A7C15043D1700E74262 /* ClapMeasurement.m */; };
AA049A8315045BC200E74262 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AA049A8215045BC200E74262 /* AVFoundation.framework */; };
AA049A8C150464D700E74262 /* CoreMedia.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AA049A8B150464D700E74262 /* CoreMedia.framework */; };
+ AA10882F1533973900B18C83 /* PlotView.mm in Sources */ = {isa = PBXBuildFile; fileRef = AA10882E1533973900B18C83 /* PlotView.mm */; };
AAA92DE715182EA500EDCF09 /* MeasurementViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = AAA92DE615182EA500EDCF09 /* MeasurementViewController.m */; };
AACC27911514F06F00A09B15 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AACC27901514F06F00A09B15 /* Accelerate.framework */; };
AACC27951516980500A09B15 /* ClapRecorder.m in Sources */ = {isa = PBXBuildFile; fileRef = AACC27941516980500A09B15 /* ClapRecorder.m */; };
@@ -40,6 +41,8 @@
AA049A7C15043D1700E74262 /* ClapMeasurement.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ClapMeasurement.m; sourceTree = "<group>"; };
AA049A8215045BC200E74262 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
AA049A8B150464D700E74262 /* CoreMedia.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMedia.framework; path = System/Library/Frameworks/CoreMedia.framework; sourceTree = SDKROOT; };
+ AA10882D1533973900B18C83 /* PlotView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PlotView.h; sourceTree = "<group>"; };
+ AA10882E1533973900B18C83 /* PlotView.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PlotView.mm; sourceTree = "<group>"; };
AAA92DE515182EA500EDCF09 /* MeasurementViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MeasurementViewController.h; sourceTree = "<group>"; };
AAA92DE615182EA500EDCF09 /* MeasurementViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MeasurementViewController.m; sourceTree = "<group>"; };
AACC27901514F06F00A09B15 /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; };
@@ -99,6 +102,8 @@
AA049A6615043AA100E74262 /* ClapIR */ = {
isa = PBXGroup;
children = (
+ AA10882D1533973900B18C83 /* PlotView.h */,
+ AA10882E1533973900B18C83 /* PlotView.mm */,
AA049A6F15043AA100E74262 /* AppDelegate.h */,
AA049A7015043AA100E74262 /* AppDelegate.m */,
AA049A7B15043D1700E74262 /* ClapMeasurement.h */,
@@ -196,6 +201,7 @@
AA049A7D15043D1700E74262 /* ClapMeasurement.m in Sources */,
AACC27951516980500A09B15 /* ClapRecorder.m in Sources */,
AAA92DE715182EA500EDCF09 /* MeasurementViewController.m in Sources */,
+ AA10882F1533973900B18C83 /* PlotView.mm in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
View
7 ClapIR/ClapMeasurement.h
@@ -9,4 +9,11 @@
@interface ClapMeasurement : NSObject
@property float reverbTime;
+@property float* reverbTimeSpectrum;
+@property float* powerSpectrum;
+
+/** number of frequencies in spectra */
++(int)numFreqs;
+/** @return array of length [ClapMeasurement numFreqs] specifying the frequencies in spectra, reported in Hertz */
++(float*)specFrequencies;
@end
View
37 ClapIR/ClapMeasurement.m
@@ -9,4 +9,41 @@
@implementation ClapMeasurement
@synthesize reverbTime;
+@synthesize reverbTimeSpectrum;
+@synthesize powerSpectrum;
+
+float* specFreqArray = nil;
+
+-(id)init{
+ self = [super init];
+ if(self){
+ reverbTimeSpectrum = malloc( sizeof(float) * ClapMeasurement.numFreqs );
+ powerSpectrum = malloc( sizeof(float) * ClapMeasurement.numFreqs );
+ }
+ return self;
+}
+
+-(void)dealloc{
+ free( reverbTimeSpectrum );
+ free( powerSpectrum );
+}
+
++(int)numFreqs{
+ return 18;
+}
+
++(float*)specFrequencies{
+ if( specFreqArray == nil ){
+ // init array of spectrum frequencies the first time they are required
+ specFreqArray = malloc( sizeof(float) * ClapMeasurement.numFreqs );
+
+ double sqrt2 = sqrt(2);
+ double x = 44;
+ for( int i=0; i<ClapMeasurement.numFreqs; i++ ){
+ specFreqArray[i] = x;
+ x *= sqrt2;
+ }
+ }
+ return specFreqArray;
+}
@end
View
94 ClapIR/ClapRecorder.m
@@ -140,6 +140,9 @@ @implementation ClapRecorder{
int _bufferSize; // length of _buffer array
int _bufferPtr; // index where next measurement will be stored
+ // history of spectra
+ float** _specBuffer;
+
// counter for the number of spectrograms observed
long _timeStep;
}
@@ -159,10 +162,17 @@ -(id)init{
_timeStep = 0;
- // allocate a 5 second circular buffer
+ // allocate a 5 second circular buffer for energy measurements
_bufferSize = ceil( 5.0 / _spectrogramRecorder.spectrumTime );
_buffer = malloc( sizeof(float) * _bufferSize );
_bufferPtr = 0;
+
+ // allocate a 2D circular buffer for spectra:
+ // _specBuffer[freq][time]
+ _specBuffer = malloc( sizeof(float*) * ClapMeasurement.numFreqs );
+ for( int i=0; i<ClapMeasurement.numFreqs; i++ ){
+ _specBuffer[i] = malloc( sizeof(float) * _bufferSize );
+ }
}
return self;
}
@@ -181,12 +191,47 @@ -(void)stop{
_bufferPtr = 0;
}
+/** calculate rt60 from a decay curve */
+-(float)calcReverb:(float*)curve{
+
+ // calculate slope of region past direct sound (one sample offset)
+ float directSoundLength = 0.01; // seconds
+ float minPrefixLength = 0.05; // seconds
+ int directSoundSamples = ceil( directSoundLength / _spectrogramRecorder.spectrumTime );
+ int minPrefixSamples = ceil( minPrefixLength / _spectrogramRecorder.spectrumTime );
+
+ PrefixFitResult regressionResult = regressionAndKnee( curve+directSoundSamples-1,
+ _stepsInClap-directSoundSamples,
+ minPrefixSamples );
+ float slope = regressionResult.fit.slope;
+ slope /= _spectrogramRecorder.spectrumTime;
+ float rt60 = -60 / slope;
+ for( int i=0; i<_stepsInClap; i++ ){
+ printf("%.0f ", curve[i] );
+ }
+ printf( "\n" );
+ printf( "Calculated rt60 = %.3f seconds, with knee at sample %d of %d\n", rt60,
+ regressionResult.prefixLength+directSoundSamples, _stepsInClap );
+
+ return rt60;
+}
+
#pragma mark - SpectrogramRecorderDelegate
-(void)gotSpectrum:(float *)spec energy:(float)energy{
// store sample in buffer
- ///NSLog( @"got spectrum with energy: %.0f dB\n", energy );
- // TODO: deal with entire spectrum, not just energy
- _buffer[_bufferPtr] = energy;
+ {
+ // store energy
+ _buffer[_bufferPtr] = energy;
+ // store spectrum
+ {
+ // pick out each of the frequencies of interest from the spectrum
+ for( int i=0; i<ClapMeasurement.numFreqs; i++ ){
+ int specIdx = floor( ( ClapMeasurement.specFrequencies[i] / ( _spectrogramRecorder.sampleRate * 0.5 ) )
+ * _spectrogramRecorder.spectrumResolution );
+ _specBuffer[i][_bufferPtr] = spec[specIdx];
+ }
+ }
+ }
// increment clap length counter if we are in the middle of a clap
if( _isClap ) _stepsInClap++;
@@ -206,33 +251,32 @@ -(void)gotSpectrum:(float *)spec energy:(float)energy{
NSLog( @"Clap end" );
_isClap = NO;
- // copy decay curve in preparation for calculation
+ // copy decay curves in preparation for calculation
+ // buffer will store all frequencies decay curves plus the energy decay curve
float* curve = malloc( sizeof(float) * _stepsInClap );
+ float* curves = malloc( sizeof(float) * _stepsInClap * ClapMeasurement.numFreqs );
for( int i=0; i<_stepsInClap; i++ ){
- curve[i] = _buffer[(_bufferPtr-_stepsInClap+1+i)%_bufferSize];
- printf( "%.0f ", curve[i] );
+ int bufIdx = (_bufferPtr-_stepsInClap+1+i)%_bufferSize;
+ curve[i] = _buffer[ bufIdx ];
+ for( int j=0; j<ClapMeasurement.numFreqs; j++ ){
+ curves[ j*_stepsInClap + i ] = _specBuffer[ j ][ bufIdx ];
+ }
}
- printf( "\n" );
-
- // calculate slope of region past direct sound (one sample offset)
- float directSoundLength = 0.01; // seconds
- float minPrefixLength = 0.05; // seconds
- int directSoundSamples = ceil( directSoundLength / _spectrogramRecorder.spectrumTime );
- int minPrefixSamples = ceil( minPrefixLength / _spectrogramRecorder.spectrumTime );
- PrefixFitResult regressionResult = regressionAndKnee( curve+directSoundSamples-1,
- _stepsInClap-directSoundSamples,
- minPrefixSamples );
- float slope = regressionResult.fit.slope;
- slope /= _spectrogramRecorder.spectrumTime;
- float rt60 = -60 / slope;
- printf( "Calculated rt60 = %.3f seconds, with knee at sample %d of %d\n", rt60,
- regressionResult.prefixLength+directSoundSamples, _stepsInClap );
+ // calculate reverb times
ClapMeasurement* measurement = [[ClapMeasurement alloc] init];
- measurement.reverbTime = rt60;
- [delegate gotMeasurement:measurement];
-
+ measurement.reverbTime = [self calcReverb:curve];
+ for( int i=0; i<ClapMeasurement.numFreqs; i++ ){
+ measurement.reverbTimeSpectrum[i] = [self calcReverb:(curves+i*_stepsInClap)];
+ }
free( curve );
+ free( curves );
+
+ for( int i=0; i<ClapMeasurement.numFreqs; i++ ){
+ measurement.reverbTimeSpectrum[i] = [self calcReverb:_specBuffer[i]];
+ }
+
+ [delegate gotMeasurement:measurement];
}
// set background level, if this was the first time that the buffer was filled
View
20 ClapIR/MeasurementViewController.m
@@ -6,11 +6,14 @@
//
#import "MeasurementViewController.h"
+#import "PlotView.h"
@interface MeasurementViewController (){
UILabel* _rt60Label;
UILabel* _backgroundLabel;
UIButton* _resetButton;
+ PlotView* _plot;
+ float* _plotCurve;
}
-(void)reset;
@end
@@ -39,13 +42,16 @@ - (void)viewDidLoad
[self.view addSubview:_backgroundLabel];
_resetButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
- _resetButton.frame = CGRectMake( 100, 300, 320-200, 80 );
+ _resetButton.frame = CGRectMake( 100, 250, 320-200, 40 );
[_resetButton setTitle:@"reset" forState:UIControlStateNormal];
[_resetButton addTarget:self
action:@selector(reset)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:_resetButton];
+ _plot = [[PlotView alloc] initWithFrame:CGRectMake(10, 300, 320-20, 150)];
+ [self.view addSubview:_plot];
+
// start audio
recorder = [[ClapRecorder alloc] init];
recorder.delegate = self;
@@ -74,6 +80,18 @@ -(void) reset{
#pragma mark - ClapRecorderDelegate methods
-(void)gotMeasurement:(ClapMeasurement *)measurement{
_rt60Label.text = [NSString stringWithFormat:@"rt60 = %.3f seconds", measurement.reverbTime];
+ for( int i=0; i<ClapMeasurement.numFreqs; i++ ){
+ NSLog( @"%.0f Hz\t%.3f seconds", ClapMeasurement.specFrequencies[i],
+ measurement.reverbTimeSpectrum[i] );
+ }
+ // copy vector to plot
+ if( _plotCurve ) free( _plotCurve );
+ _plotCurve = malloc( sizeof(float) * ClapMeasurement.numFreqs );
+ memcpy( _plotCurve, measurement.reverbTimeSpectrum, sizeof(float) * ClapMeasurement.numFreqs );
+
+ // update plot
+ [_plot setVector:_plotCurve length:ClapMeasurement.numFreqs];
+ [_plot setYRange_min:0 max:5];
}
-(void)gotBackgroundLevel:(float)decibels{
View
34 ClapIR/PlotView.h
@@ -0,0 +1,34 @@
+//
+// plotView.h
+// simpleUI
+//
+// Created by Stephen Tarzia on 9/30/10.
+// Copyright 2010 Northwestern University. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+//#import <vector>
+
+@interface PlotView : UIView {
+ float* data; // the data to plot
+ unsigned int length; // the length of data array
+ // y axis range for plot:
+ float minY;
+ float maxY;
+ float* lineColor; // RGB array
+}
+
+@property (nonatomic) float* data;
+@property (nonatomic) unsigned int length;
+@property (nonatomic) float minY;
+@property (nonatomic) float maxY;
+@property (nonatomic) float* lineColor;
+
+// set the y axis range of the plot
+-(void)setYRange_min: (float)Ymin max:(float)Ymax;
+// automatically set the range based on the values in the vector
+-(void)autoRange;
+-(id)initWithFrame:(CGRect)frame;
+-(void)setVector: (float*)data length:(unsigned int)len;
+
+@end

0 comments on commit ca97950

Please sign in to comment.
Something went wrong with that request. Please try again.