From e805e4740cfe02d365231452d014dbeb32986f34 Mon Sep 17 00:00:00 2001 From: Thong Nguyen Date: Thu, 24 Apr 2014 18:22:40 +0100 Subject: [PATCH] More Spectrum Analyzer work --- ExampleApp/ExampleApp/AudioPlayerView.m | 30 +++++++++- StreamingKit/StreamingKit/STKAudioPlayer.h | 2 + StreamingKit/StreamingKit/STKAudioPlayer.m | 68 ++++++++++++++++++++++ 3 files changed, 99 insertions(+), 1 deletion(-) diff --git a/ExampleApp/ExampleApp/AudioPlayerView.m b/ExampleApp/ExampleApp/AudioPlayerView.m index 3e65b6c..fbf894b 100644 --- a/ExampleApp/ExampleApp/AudioPlayerView.m +++ b/ExampleApp/ExampleApp/AudioPlayerView.m @@ -40,6 +40,9 @@ /// @interface AudioPlayerView() +{ + NSMutableArray* meters; +} -(void) setupTimer; -(void) updateControls; @end @@ -55,6 +58,8 @@ { self.audioPlayer = audioPlayerIn; + self.audioPlayer.spectrumAnalyzerEnabled = YES; + CGSize size = CGSizeMake(220, 50); playFromHTTPButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; @@ -114,9 +119,23 @@ statusLabel.textAlignment = NSTextAlignmentCenter; + meters = [[NSMutableArray alloc] init]; + meter = [[UIView alloc] initWithFrame:CGRectMake(0, 450, 0, 20)]; 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:playButton]; @@ -193,9 +212,18 @@ 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); + + 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 diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.h b/StreamingKit/StreamingKit/STKAudioPlayer.h index 577f298..c9d2852 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.h +++ b/StreamingKit/StreamingKit/STKAudioPlayer.h @@ -265,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) -(void) setGain:(float)gain forEqualizerBand:(int)bandIndex; +-(float) testPowerWithIndex:(int)index; + @end diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index 9f7ac6d..8ca3641 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -255,9 +255,11 @@ static AudioStreamBasicDescription canonicalAudioStreamBasicDescription; pthread_mutex_t mainThreadSyncCallMutex; pthread_cond_t mainThreadSyncCallReadyCondition; + float* window; float* obtainedReal; float* originalReal; int fftStride; + FFTSetup setupReal; DSPSplitComplex fftInput; @@ -589,6 +591,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn free(readBuffer); free(originalReal); free(obtainedReal); + free(window); } -(void) startSystemBackgroundTask @@ -2957,16 +2960,81 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* 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 { return self->meteringEnabled;