From 1c78dc58678da44118612f4c22787b758ae640f2 Mon Sep 17 00:00:00 2001 From: Thong Nguyen Date: Sun, 2 Feb 2014 14:19:12 +0000 Subject: [PATCH] Changed canonical format to fixed point for all platforms. Added metering view on ExampleAppMac --- ExampleApp/ExampleApp/AudioPlayerView.m | 2 +- .../ExampleAppMac.xcodeproj/project.pbxproj | 6 + ExampleAppMac/ExampleAppMac/AppDelegate.h | 3 +- ExampleAppMac/ExampleAppMac/AppDelegate.m | 103 +++++++++++++++++- StreamingKit/StreamingKit/STKAudioPlayer.h | 4 +- StreamingKit/StreamingKit/STKAudioPlayer.m | 64 ++++++++--- 6 files changed, 161 insertions(+), 21 deletions(-) diff --git a/ExampleApp/ExampleApp/AudioPlayerView.m b/ExampleApp/ExampleApp/AudioPlayerView.m index 28ab1a7..f2680ba 100644 --- a/ExampleApp/ExampleApp/AudioPlayerView.m +++ b/ExampleApp/ExampleApp/AudioPlayerView.m @@ -181,7 +181,7 @@ statusLabel.text = audioPlayer.state == STKAudioPlayerStateBuffering ? @"buffering" : @""; - CGFloat newWidth = 320 * (([audioPlayer peakPowerInDecibelsForChannel:1] + 60) / 60); + CGFloat newWidth = 320 * (([audioPlayer averagePowerInDecibelsForChannel:1] + 60) / 60); meter.frame = CGRectMake(0, 460, newWidth, 20); } diff --git a/ExampleAppMac/ExampleAppMac.xcodeproj/project.pbxproj b/ExampleAppMac/ExampleAppMac.xcodeproj/project.pbxproj index 05afba7..16d179c 100644 --- a/ExampleAppMac/ExampleAppMac.xcodeproj/project.pbxproj +++ b/ExampleAppMac/ExampleAppMac.xcodeproj/project.pbxproj @@ -27,6 +27,7 @@ A1A499FA189E7A5600E2A2E2 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A499F1189E799400E2A2E2 /* AudioToolbox.framework */; }; A1A499FC189E7A6D00E2A2E2 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A499FB189E7A6D00E2A2E2 /* SystemConfiguration.framework */; }; A1A499FD189E7BFC00E2A2E2 /* AudioUnit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A499ED189E793700E2A2E2 /* AudioUnit.framework */; }; + A1A49A01189E82EC00E2A2E2 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A49A00189E82EC00E2A2E2 /* QuartzCore.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -66,6 +67,8 @@ A1A499F4189E79CB00E2A2E2 /* CoreAudioKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudioKit.framework; path = System/Library/Frameworks/CoreAudioKit.framework; sourceTree = SDKROOT; }; A1A499F8189E7A3500E2A2E2 /* libStreamingKitMac.a */ = {isa = PBXFileReference; lastKnownFileType = file; name = libStreamingKitMac.a; path = ../StreamingKit/build/Debug/libStreamingKitMac.a; sourceTree = ""; }; A1A499FB189E7A6D00E2A2E2 /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; + A1A499FE189E82DD00E2A2E2 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; + A1A49A00189E82EC00E2A2E2 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -73,6 +76,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + A1A49A01189E82EC00E2A2E2 /* QuartzCore.framework in Frameworks */, A1A499FD189E7BFC00E2A2E2 /* AudioUnit.framework in Frameworks */, A1A499FC189E7A6D00E2A2E2 /* SystemConfiguration.framework in Frameworks */, A1A499FA189E7A5600E2A2E2 /* AudioToolbox.framework in Frameworks */, @@ -120,6 +124,8 @@ A1A499A3189E765800E2A2E2 /* Frameworks */ = { isa = PBXGroup; children = ( + A1A49A00189E82EC00E2A2E2 /* QuartzCore.framework */, + A1A499FE189E82DD00E2A2E2 /* CoreGraphics.framework */, A1A499FB189E7A6D00E2A2E2 /* SystemConfiguration.framework */, A1A499F8189E7A3500E2A2E2 /* libStreamingKitMac.a */, A1A499F4189E79CB00E2A2E2 /* CoreAudioKit.framework */, diff --git a/ExampleAppMac/ExampleAppMac/AppDelegate.h b/ExampleAppMac/ExampleAppMac/AppDelegate.h index 32f2314..a113dfd 100644 --- a/ExampleAppMac/ExampleAppMac/AppDelegate.h +++ b/ExampleAppMac/ExampleAppMac/AppDelegate.h @@ -7,8 +7,9 @@ // #import +#import "STKAudioPlayer.h" -@interface AppDelegate : NSObject +@interface AppDelegate : NSObject @property (assign) IBOutlet NSWindow *window; diff --git a/ExampleAppMac/ExampleAppMac/AppDelegate.m b/ExampleAppMac/ExampleAppMac/AppDelegate.m index ab2a7c5..b22990b 100644 --- a/ExampleAppMac/ExampleAppMac/AppDelegate.m +++ b/ExampleAppMac/ExampleAppMac/AppDelegate.m @@ -9,13 +9,112 @@ #import "AppDelegate.h" #import "STKAudioPlayer.h" +@interface AppDelegate() +{ + NSView* meter; + NSSlider* slider; + STKAudioPlayer* audioPlayer; +} +@end + @implementation AppDelegate -(void) applicationDidFinishLaunching:(NSNotification *)aNotification { - STKAudioPlayer* player = [[STKAudioPlayer alloc] init]; + CGRect frame = [self.window.contentView frame]; - [player play:@"http://fs.bloom.fm/oss/audiosamples/sample.mp3"]; + NSButton* playFromHTTPButton = [[NSButton alloc] initWithFrame:CGRectMake(10, 10, frame.size.width - 20, 100)]; + + [playFromHTTPButton setTitle:@"Play from HTTP"]; + [playFromHTTPButton setAction:@selector(playFromHTTP)]; + + slider = [[NSSlider alloc] initWithFrame:CGRectMake(10, 140, frame.size.width - 20, 20)]; + [slider setAction:@selector(sliderChanged:)]; + + meter = [[NSView alloc] initWithFrame:CGRectMake(10, 200, 0, 20)]; + [meter setLayer:[CALayer new]]; + [meter setWantsLayer:YES]; + meter.layer.backgroundColor = [NSColor greenColor].CGColor; + + [[self.window contentView] addSubview:slider]; + [[self.window contentView] addSubview:playFromHTTPButton]; + [[self.window contentView] addSubview:meter]; + + audioPlayer = [[STKAudioPlayer alloc] init]; + audioPlayer.delegate = self; + audioPlayer.meteringEnabled = YES; + + [NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(tick:) userInfo:nil repeats:YES]; +} + +-(void) playFromHTTP +{ + [audioPlayer play:@"http://fs.bloom.fm/oss/audiosamples/sample.mp3"]; +} + +-(void) tick:(NSTimer*)timer +{ + if (!audioPlayer) + { + slider.doubleValue = 0; + + return; + } + + CGFloat meterWidth = 0; + + if (audioPlayer.duration != 0) + { + slider.minValue = 0; + slider.maxValue = audioPlayer.duration; + slider.doubleValue = audioPlayer.progress; + + meterWidth = [self.window.contentView frame].size.width - 20; + meterWidth *= (([audioPlayer averagePowerInDecibelsForChannel:0] + 60) / 60); + } + else + { + slider.doubleValue = 0; + slider.minValue = 0; + slider.maxValue = 0; + + meterWidth = 0; + } + + CGRect frame = meter.frame; + + frame.size.width = meterWidth; + + meter.frame = frame; +} + +-(void) sliderChanged:(NSSlider*)sliderIn +{ + [audioPlayer seekToTime:sliderIn.doubleValue]; +} + +-(void) updateControls +{ +} + +-(void) audioPlayer:(STKAudioPlayer*)audioPlayer didStartPlayingQueueItemId:(NSObject*)queueItemId +{ +} + +-(void) audioPlayer:(STKAudioPlayer*)audioPlayer didFinishBufferingSourceWithQueueItemId:(NSObject*)queueItemId +{ +} + +-(void) audioPlayer:(STKAudioPlayer*)audioPlayer stateChanged:(STKAudioPlayerState)state previousState:(STKAudioPlayerState)previousState +{ +} + +-(void) audioPlayer:(STKAudioPlayer*)audioPlayer didFinishPlayingQueueItemId:(NSObject*)queueItemId withReason:(STKAudioPlayerStopReason)stopReason andProgress:(double)progress andDuration:(double)duration +{ +} + +-(void) audioPlayer:(STKAudioPlayer*)audioPlayer unexpectedError:(STKAudioPlayerErrorCode)errorCode +{ } @end diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.h b/StreamingKit/StreamingKit/STKAudioPlayer.h index a09a63f..d42e401 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.h +++ b/StreamingKit/StreamingKit/STKAudioPlayer.h @@ -202,10 +202,12 @@ typedef void(^STKFrameFilter)(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UIn /// If the given name is nil, the filter will be inserted at the beginning of the filter change -(void) addFrameFilterWithName:(NSString*)name afterFilterWithName:(NSString*)afterFilterWithName block:(STKFrameFilter)block; -/// Reads the peak power in decibals for the given channel (0 or 1) +/// Reads the peak power in decibals for the given channel (0 or 1). +/// Return values are between -60 (low) and 0 (high). -(float) peakPowerInDecibelsForChannel:(NSUInteger)channelNumber; /// Reads the average power in decibals for the given channel (0 or 1) +/// Return values are between -60 (low) and 0 (high). -(float) averagePowerInDecibelsForChannel:(NSUInteger)channelNumber; @end diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index 0cbde99..c2eedb8 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -292,14 +292,16 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn if (self = [super init]) { options = optionsIn; - + + const int bytesPerSample = sizeof(AudioSampleType); + canonicalAudioStreamBasicDescription.mSampleRate = 44100.00; canonicalAudioStreamBasicDescription.mFormatID = kAudioFormatLinearPCM; - canonicalAudioStreamBasicDescription.mFormatFlags = kAudioFormatFlagsCanonical; + canonicalAudioStreamBasicDescription.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked; canonicalAudioStreamBasicDescription.mFramesPerPacket = 1; canonicalAudioStreamBasicDescription.mChannelsPerFrame = 2; - canonicalAudioStreamBasicDescription.mBytesPerFrame = sizeof(AudioSampleType) * canonicalAudioStreamBasicDescription.mChannelsPerFrame; - canonicalAudioStreamBasicDescription.mBitsPerChannel = 8 * sizeof(AudioSampleType); + canonicalAudioStreamBasicDescription.mBytesPerFrame = bytesPerSample * canonicalAudioStreamBasicDescription.mChannelsPerFrame; + canonicalAudioStreamBasicDescription.mBitsPerChannel = 8 * bytesPerSample; canonicalAudioStreamBasicDescription.mBytesPerPacket = canonicalAudioStreamBasicDescription.mBytesPerFrame * canonicalAudioStreamBasicDescription.mFramesPerPacket; framesRequiredToStartPlaying = canonicalAudioStreamBasicDescription.mSampleRate * STK_DEFAULT_SECONDS_REQUIRED_TO_START_PLAYING; @@ -2393,10 +2395,11 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* { \ if(sampleDB##channel > peakValue##channel) \ { \ - peakValue##channel = MIN(sampleDB##channel, 0); \ + peakValue##channel = sampleDB##channel; \ } \ if (sampleDB##channel > -DBL_MAX) \ { \ + count##channel++; \ totalValue##channel += sampleDB##channel; \ } \ decibels##channel = peakValue##channel; \ @@ -2418,29 +2421,58 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* { [self appendFrameFilterWithName:@"STKMeteringFilter" block:^(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UInt32 frameCount, void* frames) { - SInt16* samples = (SInt16*)frames; + SInt16* samples16 = (SInt16*)frames; + SInt32* samples32 = (SInt32*)frames; + UInt32 countLeft = 0; + UInt32 countRight = 0; Float32 decibelsLeft = STK_DBMIN; Float32 peakValueLeft = STK_DBMIN; - Float64 totalValueLeft = STK_DBMIN; + Float64 totalValueLeft = 0; Float32 previousFilteredValueOfSampleAmplitudeLeft = 0; Float32 decibelsRight = STK_DBMIN; Float32 peakValueRight = STK_DBMIN; - Float64 totalValueRight = STK_DBMIN; + Float64 totalValueRight = 0; Float32 previousFilteredValueOfSampleAmplitudeRight = 0; - for (int i = 0; i < frameCount * 2; i++) + if (bytesPerFrame / channelsPerFrame == 2) { - Float32 absoluteValueOfSampleAmplitudeLeft = abs(samples[i]); - Float32 absoluteValueOfSampleAmplitudeRight = abs(samples[i]); - - CALCULATE_METER(Left); - CALCULATE_METER(Right); + for (int i = 0; i < frameCount * channelsPerFrame; i += channelsPerFrame) + { + Float32 absoluteValueOfSampleAmplitudeLeft = abs(samples16[i]); + Float32 absoluteValueOfSampleAmplitudeRight = abs(samples16[i + 1]); + + CALCULATE_METER(Left); + CALCULATE_METER(Right); + } + } + else if (bytesPerFrame / channelsPerFrame == 4) + { + for (int i = 0; i < frameCount * channelsPerFrame; i += channelsPerFrame) + { + Float32 absoluteValueOfSampleAmplitudeLeft = abs(samples32[i]) / 32768.0; + Float32 absoluteValueOfSampleAmplitudeRight = abs(samples32[i + 1]) / 32768.0; + + CALCULATE_METER(Left); + CALCULATE_METER(Right); + } + } + else + { + return; } peakPowerDb[0] = MIN(MAX(decibelsLeft, -60), 0); - averagePowerDb[0] = MIN(MAX(totalValueLeft / frameCount, -60), 0); peakPowerDb[1] = MIN(MAX(decibelsRight, -60), 0); - averagePowerDb[1] = MIN(MAX(totalValueRight / frameCount, -60), 0); + + if (countLeft > 0) + { + averagePowerDb[0] = MIN(MAX(totalValueLeft / frameCount, -60), 0); + } + + if (countRight != 0) + { + averagePowerDb[1] = MIN(MAX(totalValueRight / frameCount, -60), 0); + } }]; } }