Compare commits

...

2 Commits

Author SHA1 Message Date
Thong Nguyen e805e4740c More Spectrum Analyzer work 2014-04-24 18:22:40 +01:00
Thong Nguyen 484285df84 Started work on Spectrum Analyzer 2014-04-23 18:03:23 +01:00
5 changed files with 170 additions and 2 deletions

View File

@ -27,6 +27,7 @@
A17FFB6318A0028300BAA7FF /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A17FFB6218A0028300BAA7FF /* AudioToolbox.framework */; }; A17FFB6318A0028300BAA7FF /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A17FFB6218A0028300BAA7FF /* AudioToolbox.framework */; };
A17FFB6918A002E400BAA7FF /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1F5E491189EB3F20070B03F /* AVFoundation.framework */; }; A17FFB6918A002E400BAA7FF /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1F5E491189EB3F20070B03F /* AVFoundation.framework */; };
A1EBEE64188DE34500681B04 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1EBEE63188DE34500681B04 /* SystemConfiguration.framework */; }; A1EBEE64188DE34500681B04 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1EBEE63188DE34500681B04 /* SystemConfiguration.framework */; };
A1F3410A1908185900CA7755 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1F341091908185900CA7755 /* Accelerate.framework */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
@ -65,6 +66,7 @@
A142571C18907861005F0129 /* airplane.aac */ = {isa = PBXFileReference; lastKnownFileType = file; path = airplane.aac; sourceTree = "<group>"; }; A142571C18907861005F0129 /* airplane.aac */ = {isa = PBXFileReference; lastKnownFileType = file; path = airplane.aac; sourceTree = "<group>"; };
A17FFB6218A0028300BAA7FF /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; }; A17FFB6218A0028300BAA7FF /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
A1EBEE63188DE34500681B04 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; A1EBEE63188DE34500681B04 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };
A1F341091908185900CA7755 /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; };
A1F5E48F189EB3CB0070B03F /* AudioUnit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioUnit.framework; path = System/Library/Frameworks/AudioUnit.framework; sourceTree = SDKROOT; }; A1F5E48F189EB3CB0070B03F /* AudioUnit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioUnit.framework; path = System/Library/Frameworks/AudioUnit.framework; sourceTree = SDKROOT; };
A1F5E491189EB3F20070B03F /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; }; A1F5E491189EB3F20070B03F /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
/* End PBXFileReference section */ /* End PBXFileReference section */
@ -74,6 +76,7 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
A1F3410A1908185900CA7755 /* Accelerate.framework in Frameworks */,
A17FFB6918A002E400BAA7FF /* AVFoundation.framework in Frameworks */, A17FFB6918A002E400BAA7FF /* AVFoundation.framework in Frameworks */,
A17FFB6318A0028300BAA7FF /* AudioToolbox.framework in Frameworks */, A17FFB6318A0028300BAA7FF /* AudioToolbox.framework in Frameworks */,
A1EBEE64188DE34500681B04 /* SystemConfiguration.framework in Frameworks */, A1EBEE64188DE34500681B04 /* SystemConfiguration.framework in Frameworks */,
@ -119,6 +122,7 @@
A1115933188D686000641365 /* Frameworks */ = { A1115933188D686000641365 /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
A1F341091908185900CA7755 /* Accelerate.framework */,
A17FFB6218A0028300BAA7FF /* AudioToolbox.framework */, A17FFB6218A0028300BAA7FF /* AudioToolbox.framework */,
A1F5E491189EB3F20070B03F /* AVFoundation.framework */, A1F5E491189EB3F20070B03F /* AVFoundation.framework */,
A1F5E48F189EB3CB0070B03F /* AudioUnit.framework */, A1F5E48F189EB3CB0070B03F /* AudioUnit.framework */,

View File

@ -40,6 +40,9 @@
/// ///
@interface AudioPlayerView() @interface AudioPlayerView()
{
NSMutableArray* meters;
}
-(void) setupTimer; -(void) setupTimer;
-(void) updateControls; -(void) updateControls;
@end @end
@ -55,6 +58,8 @@
{ {
self.audioPlayer = audioPlayerIn; self.audioPlayer = audioPlayerIn;
self.audioPlayer.spectrumAnalyzerEnabled = YES;
CGSize size = CGSizeMake(220, 50); CGSize size = CGSizeMake(220, 50);
playFromHTTPButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; playFromHTTPButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
@ -114,10 +119,24 @@
statusLabel.textAlignment = NSTextAlignmentCenter; statusLabel.textAlignment = NSTextAlignmentCenter;
meters = [[NSMutableArray alloc] init];
meter = [[UIView alloc] initWithFrame:CGRectMake(0, 450, 0, 20)]; meter = [[UIView alloc] initWithFrame:CGRectMake(0, 450, 0, 20)];
meter.backgroundColor = [UIColor greenColor]; meter.backgroundColor = [UIColor greenColor];
for (int i = 0; i < 256; i++)
{
UIView* freqMeter = [[UIView alloc] initWithFrame:CGRectMake(i, self.bounds.size.height, 1, 0)];
freqMeter.backgroundColor = [UIColor blueColor];
[self addSubview:freqMeter];
[meters addObject:freqMeter];
[self sendSubviewToBack:freqMeter];
}
[self addSubview:slider]; [self addSubview:slider];
[self addSubview:playButton]; [self addSubview:playButton];
[self addSubview:playFromHTTPButton]; [self addSubview:playFromHTTPButton];
@ -193,9 +212,18 @@
statusLabel.text = audioPlayer.state == STKAudioPlayerStateBuffering ? @"buffering" : @""; statusLabel.text = audioPlayer.state == STKAudioPlayerStateBuffering ? @"buffering" : @"";
CGFloat newWidth = 320 * (([audioPlayer averagePowerInDecibelsForChannel:1] + 60) / 60); CGFloat newWidth = 320 * (([audioPlayer testPowerWithIndex:100] + 96) / 96);
meter.frame = CGRectMake(0, 460, newWidth, 20); meter.frame = CGRectMake(0, 460, newWidth, 20);
for (int i = 0; i < 256; i++)
{
UIView* freqMeter = [meters objectAtIndex:i];
CGFloat height = 200 * (([audioPlayer testPowerWithIndex:i] + 96) / 96);
freqMeter.frame = CGRectMake(freqMeter.frame.origin.x, self.bounds.size.height - height, 1, height);
}
} }
-(void) playFromHTTPButtonTouched -(void) playFromHTTPButtonTouched

View File

@ -37,6 +37,8 @@
A1E7C503188D5E550010896F /* STKDataSourceWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4FA188D5E550010896F /* STKDataSourceWrapper.m */; }; A1E7C503188D5E550010896F /* STKDataSourceWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4FA188D5E550010896F /* STKDataSourceWrapper.m */; };
A1E7C504188D5E550010896F /* STKHTTPDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4FC188D5E550010896F /* STKHTTPDataSource.m */; }; A1E7C504188D5E550010896F /* STKHTTPDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4FC188D5E550010896F /* STKHTTPDataSource.m */; };
A1E7C505188D5E550010896F /* STKLocalFileDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4FE188D5E550010896F /* STKLocalFileDataSource.m */; }; A1E7C505188D5E550010896F /* STKLocalFileDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4FE188D5E550010896F /* STKLocalFileDataSource.m */; };
A1F341041908183300CA7755 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1F341031908183300CA7755 /* Accelerate.framework */; };
A1F341081908183A00CA7755 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1F341071908183A00CA7755 /* Accelerate.framework */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
@ -125,6 +127,8 @@
A1E7C4FD188D5E550010896F /* STKLocalFileDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKLocalFileDataSource.h; sourceTree = "<group>"; }; A1E7C4FD188D5E550010896F /* STKLocalFileDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKLocalFileDataSource.h; sourceTree = "<group>"; };
A1E7C4FE188D5E550010896F /* STKLocalFileDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKLocalFileDataSource.m; sourceTree = "<group>"; }; A1E7C4FE188D5E550010896F /* STKLocalFileDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKLocalFileDataSource.m; sourceTree = "<group>"; };
A1E7C507188D62D20010896F /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; A1E7C507188D62D20010896F /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
A1F341031908183300CA7755 /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; };
A1F341071908183A00CA7755 /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/System/Library/Frameworks/Accelerate.framework; sourceTree = DEVELOPER_DIR; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@ -132,6 +136,7 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
A1F341081908183A00CA7755 /* Accelerate.framework in Frameworks */,
A1A4996B189E744400E2A2E2 /* Cocoa.framework in Frameworks */, A1A4996B189E744400E2A2E2 /* Cocoa.framework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
@ -150,6 +155,7 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
A1F341041908183300CA7755 /* Accelerate.framework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -248,6 +254,8 @@
A1E7C4CA188D57F50010896F /* Frameworks */ = { A1E7C4CA188D57F50010896F /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
A1F341071908183A00CA7755 /* Accelerate.framework */,
A1F341031908183300CA7755 /* Accelerate.framework */,
A1A499F6189E79EA00E2A2E2 /* AudioToolbox.framework */, A1A499F6189E79EA00E2A2E2 /* AudioToolbox.framework */,
A1C9767618981BFE0057F881 /* AudioUnit.framework */, A1C9767618981BFE0057F881 /* AudioUnit.framework */,
A1E7C507188D62D20010896F /* UIKit.framework */, A1E7C507188D62D20010896F /* UIKit.framework */,

View File

@ -146,6 +146,8 @@ typedef void(^STKFrameFilter)(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UIn
@property (readwrite) BOOL meteringEnabled; @property (readwrite) BOOL meteringEnabled;
/// Enables or disables the EQ /// Enables or disables the EQ
@property (readwrite) BOOL equalizerEnabled; @property (readwrite) BOOL equalizerEnabled;
/// Enables or disables the spectrum analyzer (fft)
@property (readwrite) BOOL spectrumAnalyzerEnabled;
/// Returns an array of STKFrameFilterEntry objects representing the filters currently in use /// Returns an array of STKFrameFilterEntry objects representing the filters currently in use
@property (readonly) NSArray* frameFilters; @property (readonly) NSArray* frameFilters;
/// Returns the items pending to be played (includes buffering and upcoming items but does not include the current item) /// Returns the items pending to be played (includes buffering and upcoming items but does not include the current item)
@ -263,4 +265,6 @@ typedef void(^STKFrameFilter)(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UIn
/// Sets the gain value (from -96 low to +24 high) for an equalizer band (0 based index) /// Sets the gain value (from -96 low to +24 high) for an equalizer band (0 based index)
-(void) setGain:(float)gain forEqualizerBand:(int)bandIndex; -(void) setGain:(float)gain forEqualizerBand:(int)bandIndex;
-(float) testPowerWithIndex:(int)index;
@end @end

View File

@ -40,6 +40,7 @@
#import "STKQueueEntry.h" #import "STKQueueEntry.h"
#import "NSMutableArray+STKAudioPlayer.h" #import "NSMutableArray+STKAudioPlayer.h"
#import "libkern/OSAtomic.h" #import "libkern/OSAtomic.h"
#include <Accelerate/Accelerate.h>
#import <float.h> #import <float.h>
#ifndef DBL_MAX #ifndef DBL_MAX
@ -189,6 +190,7 @@ static AudioStreamBasicDescription canonicalAudioStreamBasicDescription;
BOOL meteringEnabled; BOOL meteringEnabled;
BOOL equalizerOn; BOOL equalizerOn;
BOOL equalizerEnabled; BOOL equalizerEnabled;
BOOL spectrumAnalyzerEnabled;
STKAudioPlayerOptions options; STKAudioPlayerOptions options;
NSMutableArray* converterNodes; NSMutableArray* converterNodes;
@ -253,6 +255,14 @@ static AudioStreamBasicDescription canonicalAudioStreamBasicDescription;
pthread_mutex_t mainThreadSyncCallMutex; pthread_mutex_t mainThreadSyncCallMutex;
pthread_cond_t mainThreadSyncCallReadyCondition; pthread_cond_t mainThreadSyncCallReadyCondition;
float* window;
float* obtainedReal;
float* originalReal;
int fftStride;
FFTSetup setupReal;
DSPSplitComplex fftInput;
volatile BOOL waiting; volatile BOOL waiting;
volatile double requestedSeekTime; volatile double requestedSeekTime;
volatile BOOL disposeWasRequested; volatile BOOL disposeWasRequested;
@ -470,6 +480,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
self->volume = 1.0; self->volume = 1.0;
self->equalizerEnabled = optionsIn.equalizerBandFrequencies[0] != 0; self->equalizerEnabled = optionsIn.equalizerBandFrequencies[0] != 0;
self->spectrumAnalyzerEnabled = NO;
PopulateOptionsWithDefault(&options); PopulateOptionsWithDefault(&options);
@ -578,6 +589,9 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
pthread_cond_destroy(&mainThreadSyncCallReadyCondition); pthread_cond_destroy(&mainThreadSyncCallReadyCondition);
free(readBuffer); free(readBuffer);
free(originalReal);
free(obtainedReal);
free(window);
} }
-(void) startSystemBackgroundTask -(void) startSystemBackgroundTask
@ -2912,6 +2926,115 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags*
return averagePowerDb[channelNumber]; return averagePowerDb[channelNumber];
} }
-(BOOL) spectrumAnalyzerEnabled
{
return self->spectrumAnalyzerEnabled;
}
-(void) setSpectrumAnalyzerEnabled:(BOOL)value
{
if (self->spectrumAnalyzerEnabled == value)
{
return;
}
self->spectrumAnalyzerEnabled = value;
if (!value)
{
[self removeFrameFilterWithName:@"STKSpectrumAnalyzerFilter"];
}
else
{
if (!obtainedReal)
{
int maxSamples = 4096;
int log2n = log2f(maxSamples);
int n = 1 << log2n;
fftStride = 1;
int nOver2 = maxSamples / 2;
fftInput.realp = (float*)calloc(nOver2, sizeof(float));
fftInput.imagp =(float*)calloc(nOver2, sizeof(float));
obtainedReal = (float*)calloc(n, sizeof(float));
originalReal = (float*)calloc(n, sizeof(float));
window = (float*)calloc(maxSamples, sizeof(float));
vDSP_blkman_window(window, maxSamples, 0);
setupReal = vDSP_create_fftsetup(log2n, FFT_RADIX2);
}
[self appendFrameFilterWithName:@"STKSpectrumAnalyzerFilter" block:^(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UInt32 frameCount, void* frames)
{
int log2n = log2f(frameCount);
int n = 1 << log2n;
int nOver2 = frameCount / 2;
SInt16* samples16 = (SInt16*)frames;
SInt32* samples32 = (SInt32*)frames;
if (bytesPerFrame / channelsPerFrame == 2)
{
for (int i = 0, j = 0; i < frameCount * channelsPerFrame; i+= channelsPerFrame, j += 2)
{
originalReal[j] = samples16[i] / 32768.0;
}
}
else if (bytesPerFrame / channelsPerFrame == 4)
{
for (int i = 0, j = 0; i < frameCount * channelsPerFrame; i+= channelsPerFrame, j+= 2)
{
originalReal[j] = samples32[i] / 32768.0;
}
}
vDSP_hann_window(window, n, 0);
vDSP_vmul(originalReal, 2, window, 1, originalReal, 2, n);
vDSP_ctoz((COMPLEX*)originalReal, 2, &fftInput, 1, nOver2);
vDSP_fft_zrip(setupReal, &fftInput, fftStride, log2n, FFT_FORWARD);
float one = 1;
float scale = (float)1.0 / (2 * n);
vDSP_vsmul(fftInput.realp, 1, &scale, fftInput.realp, 1, nOver2);
vDSP_vsmul(fftInput.imagp, 1, &scale, fftInput.imagp, 1, nOver2);
pthread_mutex_lock(&self->playerMutex);
vDSP_zvmags(&fftInput, 1, obtainedReal, 1, nOver2);
vDSP_vdbcon(obtainedReal, 1, &one, obtainedReal, 1, nOver2, 0);
float vmin = -96;
float vmax = 0;
vDSP_vclip(obtainedReal, 1, &vmin, &vmax, obtainedReal, 1, nOver2);
pthread_mutex_unlock(&self->playerMutex);
}];
}
}
-(float) testPowerWithIndex:(int)index
{
pthread_mutex_lock(&self->playerMutex);
if (!obtainedReal)
{
pthread_mutex_unlock(&self->playerMutex);
return 0;
}
float retval = obtainedReal[index];
pthread_mutex_unlock(&self->playerMutex);
return retval;
}
-(BOOL) meteringEnabled -(BOOL) meteringEnabled
{ {
return self->meteringEnabled; return self->meteringEnabled;
@ -2942,10 +3065,11 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags*
return; return;
} }
self->meteringEnabled = value;
if (!value) if (!value)
{ {
[self removeFrameFilterWithName:@"STKMeteringFilter"]; [self removeFrameFilterWithName:@"STKMeteringFilter"];
self->meteringEnabled = NO;
} }
else else
{ {