Skip to content

Commit

Permalink
Added methods to AudioStreamer to allow level metering, and a very ba…
Browse files Browse the repository at this point in the history
…sic metering view to demonstrate usage

Conflicts:

	Classes/AudioStreamer.h
  • Loading branch information
Grant Pannell committed Aug 24, 2010
1 parent f367704 commit e9c4938
Show file tree
Hide file tree
Showing 8 changed files with 203 additions and 5 deletions.
19 changes: 19 additions & 0 deletions Classes/AudioStreamer.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,17 @@ extern NSString * const ASPresentAlertWithTitleNotification;
// time)
double packetDuration; // sample rate times frames per packet
double lastProgress; // last calculated progress point
UInt32 numberOfChannels; // Number of audio channels in the stream (1 = mono, 2 = stereo)

#ifdef SHOUTCAST_METADATA
BOOL foundIcyStart;
BOOL foundIcyEnd;
BOOL parsedHeaders;
unsigned int metaDataInterval; // how many data bytes between meta data
unsigned int metaDataBytesRemaining; // how many bytes of metadata remain to be read
unsigned int dataBytesRead; // how many bytes of data have been read
NSMutableString *metaDataString; // the metaDataString
#endif
}

@property AudioStreamerErrorCode errorCode;
Expand All @@ -167,6 +178,9 @@ extern NSString * const ASPresentAlertWithTitleNotification;
@property (readonly) double duration;
@property (readwrite) UInt32 bitRate;
@property (readonly) NSDictionary *httpHeaders;
@property (readonly) UInt32 numberOfChannels;
@property (assign, getter=isMeteringEnabled) BOOL meteringEnabled;


- (id)initWithURL:(NSURL *)aURL;
- (void)start;
Expand All @@ -179,6 +193,11 @@ extern NSString * const ASPresentAlertWithTitleNotification;
- (void)seekToTime:(double)newSeekTime;
- (double)calculatedBitRate;

// level metering
- (float)peakPowerForChannel:(NSUInteger)channelNumber;
- (float)averagePowerForChannel:(NSUInteger)channelNumber;


@end


Expand Down
66 changes: 66 additions & 0 deletions Classes/AudioStreamer.m
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,8 @@ @implementation AudioStreamer
@synthesize state;
@synthesize bitRate;
@synthesize httpHeaders;
@synthesize numberOfChannels;


//
// initWithURL
Expand Down Expand Up @@ -1122,6 +1124,68 @@ - (double)duration
return (fileLength - dataOffset) / (calculatedBitRate * 0.125);
}


//
// isMeteringEnabled
//

- (BOOL)isMeteringEnabled {
UInt32 enabled;
UInt32 propertySize = sizeof(UInt32);
OSStatus status = AudioQueueGetProperty(audioQueue, kAudioQueueProperty_EnableLevelMetering, &enabled, &propertySize);
if(!status) {
return (enabled == 1);
}
return NO;
}


//
// setMeteringEnabled
//

- (void)setMeteringEnabled:(BOOL)enable {
if(enable == [self isMeteringEnabled])
return;
UInt32 enabled = (enable ? 1 : 0);
OSStatus status = AudioQueueSetProperty(audioQueue, kAudioQueueProperty_EnableLevelMetering, &enabled, sizeof(UInt32));
// do something if failed?
if(status)
return;
}


// level metering
- (float)peakPowerForChannel:(NSUInteger)channelNumber {
if(![self isMeteringEnabled] || channelNumber >= [self numberOfChannels])
return 0;
float peakPower = 0;
UInt32 propertySize = [self numberOfChannels] * sizeof(AudioQueueLevelMeterState);
AudioQueueLevelMeterState *audioLevels = calloc(sizeof(AudioQueueLevelMeterState), [self numberOfChannels]);
OSStatus status = AudioQueueGetProperty(audioQueue, kAudioQueueProperty_CurrentLevelMeter, audioLevels, &propertySize);
if(!status) {
peakPower = audioLevels[channelNumber].mPeakPower;
}
free(audioLevels);
return peakPower;
}


- (float)averagePowerForChannel:(NSUInteger)channelNumber {
if(![self isMeteringEnabled] || channelNumber >= [self numberOfChannels])
return 0;
float peakPower = 0;
UInt32 propertySize = [self numberOfChannels] * sizeof(AudioQueueLevelMeterState);
AudioQueueLevelMeterState *audioLevels = calloc(sizeof(AudioQueueLevelMeterState), [self numberOfChannels]);
OSStatus status = AudioQueueGetProperty(audioQueue, kAudioQueueProperty_CurrentLevelMeter, audioLevels, &propertySize);
if(!status) {
peakPower = audioLevels[channelNumber].mAveragePower;
}
free(audioLevels);
return peakPower;
}


//
// pause
//
Expand Down Expand Up @@ -1786,6 +1850,8 @@ - (void)createQueue
sampleRate = asbd.mSampleRate;
packetDuration = asbd.mFramesPerPacket / sampleRate;

numberOfChannels = asbd.mChannelsPerFrame;

// create the audio queue
err = AudioQueueNewOutput(&asbd, MyAudioQueueOutputCallback, self, NULL, NULL, 0, &audioQueue);
if (err)
Expand Down
20 changes: 20 additions & 0 deletions Classes/LevelMeterView.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//
// LevelMeterView.h
// iPhoneStreamingPlayer
//
// Created by Carlos Oliva G. on 07-08-10.
// Copyright 2010 iDev Software. All rights reserved.
//

#import <UIKit/UIKit.h>


@interface LevelMeterView : UIView {
CGFloat leftValue;
CGFloat rightValue;
}

- (void)updateMeterWithLeftValue:(CGFloat)left rightValue:(CGFloat)right;


@end
51 changes: 51 additions & 0 deletions Classes/LevelMeterView.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//
// LevelMeterView.m
// iPhoneStreamingPlayer
//
// Created by Carlos Oliva G. on 07-08-10.
// Copyright 2010 iDev Software. All rights reserved.
//

#import "LevelMeterView.h"

#define kMeterViewFullWidth 275.0

@implementation LevelMeterView


- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
// Initialization code
self.backgroundColor = [UIColor blackColor];
}
return self;
}

// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
[[UIColor whiteColor] set];
[@"L" drawInRect:CGRectMake(0.0, 10.0, 15.0, 15.0) withFont:[UIFont boldSystemFontOfSize:[UIFont systemFontSize]] lineBreakMode:UILineBreakModeWordWrap alignment:UITextAlignmentCenter];
[@"R" drawInRect:CGRectMake(0.0, 35.0, 15.0, 15.0) withFont:[UIFont boldSystemFontOfSize:[UIFont systemFontSize]] lineBreakMode:UILineBreakModeWordWrap alignment:UITextAlignmentCenter];

CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [UIColor greenColor].CGColor);
CGContextFillRect(context, CGRectMake(15.0, 10.0, kMeterViewFullWidth * leftValue, 15.0));
CGContextFillRect(context, CGRectMake(15.0, 35.0, kMeterViewFullWidth * rightValue, 15.0));
CGContextFlush(context);
}


- (void)updateMeterWithLeftValue:(CGFloat)left rightValue:(CGFloat)right {
leftValue = left;
rightValue = right;
[self setNeedsDisplay];
}

- (void)dealloc {
[super dealloc];
}


@end
4 changes: 3 additions & 1 deletion Classes/iPhoneStreamingPlayerViewController.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

#import <UIKit/UIKit.h>

@class AudioStreamer;
@class AudioStreamer, LevelMeterView;

@interface iPhoneStreamingPlayerViewController : UIViewController
{
Expand All @@ -25,6 +25,8 @@
IBOutlet UISlider *progressSlider;
AudioStreamer *streamer;
NSTimer *progressUpdateTimer;
NSTimer *levelMeterUpdateTimer;
LevelMeterView *levelMeterView;
}

- (IBAction)buttonPressed:(id)sender;
Expand Down
34 changes: 34 additions & 0 deletions Classes/iPhoneStreamingPlayerViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#import "iPhoneStreamingPlayerAppDelegate.h"
#import "iPhoneStreamingPlayerViewController.h"
#import "AudioStreamer.h"
#import "LevelMeterView.h"
#import <QuartzCore/CoreAnimation.h>
#import <MediaPlayer/MediaPlayer.h>
#import <CFNetwork/CFNetwork.h>
Expand Down Expand Up @@ -105,6 +106,11 @@ - (void)createStreamer
selector:@selector(updateProgress:)
userInfo:nil
repeats:YES];
levelMeterUpdateTimer = [NSTimer scheduledTimerWithTimeInterval:.1
target:self
selector:@selector(updateLevelMeters:)
userInfo:nil
repeats:YES];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(playbackStateChanged:)
Expand Down Expand Up @@ -135,6 +141,9 @@ - (void)viewDidLoad
[volumeView sizeToFit];

[self setButtonImage:[UIImage imageNamed:@"playbutton.png"]];

levelMeterView = [[LevelMeterView alloc] initWithFrame:CGRectMake(10.0, 310.0, 300.0, 60.0)];
[self.view addSubview:levelMeterView];
}

- (void)viewDidAppear:(BOOL)animated {
Expand Down Expand Up @@ -256,14 +265,20 @@ - (void)playbackStateChanged:(NSNotification *)aNotification
{
if ([streamer isWaiting])
{
[levelMeterView updateMeterWithLeftValue:0.0
rightValue:0.0];
[streamer setMeteringEnabled:NO];
[self setButtonImage:[UIImage imageNamed:@"loadingbutton.png"]];
}
else if ([streamer isPlaying])
{
[streamer setMeteringEnabled:YES];
[self setButtonImage:[UIImage imageNamed:@"stopbutton.png"]];
}
else if ([streamer isIdle])
{
[levelMeterView updateMeterWithLeftValue:0.0
rightValue:0.0];
[self destroyStreamer];
[self setButtonImage:[UIImage imageNamed:@"playbutton.png"]];
}
Expand Down Expand Up @@ -351,6 +366,20 @@ - (void)updateProgress:(NSTimer *)updatedTimer
}
}


//
// updateLevelMeters:
//

- (void)updateLevelMeters:(NSTimer *)timer {
iPhoneStreamingPlayerAppDelegate *appDelegate = (iPhoneStreamingPlayerAppDelegate *)[[UIApplication sharedApplication] delegate];
if([streamer isMeteringEnabled] && appDelegate.uiIsVisible) {
[levelMeterView updateMeterWithLeftValue:[streamer averagePowerForChannel:0]
rightValue:[streamer averagePowerForChannel:1]];
}
}


//
// textFieldShouldReturn:
//
Expand Down Expand Up @@ -381,6 +410,11 @@ - (void)dealloc
[progressUpdateTimer invalidate];
progressUpdateTimer = nil;
}
if(levelMeterUpdateTimer) {
[levelMeterUpdateTimer invalidate];
levelMeterUpdateTimer = nil;
}
[levelMeterView release];
[super dealloc];
}

Expand Down
6 changes: 3 additions & 3 deletions iPhone Resources/iPhoneStreamingPlayerViewController.xib
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@
<object class="IBUILabel" id="1000478862">
<reference key="NSNextResponder" ref="774585933"/>
<int key="NSvFlags">292</int>
<string key="NSFrame">{{20, 356}, {280, 21}}</string>
<string key="NSFrame">{{20, 376}, {280, 21}}</string>
<reference key="NSSuperview" ref="774585933"/>
<bool key="IBUIOpaque">NO</bool>
<bool key="IBUIClipsSubviews">YES</bool>
Expand All @@ -151,7 +151,7 @@
<object class="IBUIView" id="1067123130">
<reference key="NSNextResponder" ref="774585933"/>
<int key="NSvFlags">292</int>
<string key="NSFrame">{{20, 385}, {280, 55}}</string>
<string key="NSFrame">{{20, 405}, {280, 35}}</string>
<reference key="NSSuperview" ref="774585933"/>
<object class="NSColor" key="IBUIBackgroundColor">
<int key="NSColorSpace">3</int>
Expand Down Expand Up @@ -490,7 +490,7 @@
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string>{{623, 248}, {320, 480}}</string>
<string>{{740, 290}, {320, 480}}</string>
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
</object>
</object>
Expand Down
8 changes: 7 additions & 1 deletion iPhoneStreamingPlayer.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; };
288765A50DF7441C002DB57D /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 288765A40DF7441C002DB57D /* CoreGraphics.framework */; };
28D7ACF80DDB3853001CB0EB /* iPhoneStreamingPlayerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 28D7ACF70DDB3853001CB0EB /* iPhoneStreamingPlayerViewController.m */; };
762375BF120E049F00518DB5 /* LevelMeterView.m in Sources */ = {isa = PBXBuildFile; fileRef = 762375BE120E049F00518DB5 /* LevelMeterView.m */; };
7646A326120C8F3C000ED48C /* UIDevice+Hardware.m in Sources */ = {isa = PBXBuildFile; fileRef = 7646A325120C8F3C000ED48C /* UIDevice+Hardware.m */; };
C9423DF10EF8AA6B003B785B /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C9423DF00EF8AA6B003B785B /* CFNetwork.framework */; };
C9AB93E20FCF816F0047C0FA /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C9AB93E10FCF816F0047C0FA /* AudioToolbox.framework */; };
Expand All @@ -38,6 +39,8 @@
28D7ACF70DDB3853001CB0EB /* iPhoneStreamingPlayerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = iPhoneStreamingPlayerViewController.m; sourceTree = "<group>"; };
29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
32CA4F630368D1EE00C91783 /* iPhoneStreamingPlayer_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iPhoneStreamingPlayer_Prefix.pch; sourceTree = "<group>"; };
762375BD120E049F00518DB5 /* LevelMeterView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LevelMeterView.h; sourceTree = "<group>"; };
762375BE120E049F00518DB5 /* LevelMeterView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LevelMeterView.m; sourceTree = "<group>"; };
7646A324120C8F3C000ED48C /* UIDevice+Hardware.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIDevice+Hardware.h"; sourceTree = "<group>"; };
7646A325120C8F3C000ED48C /* UIDevice+Hardware.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIDevice+Hardware.m"; sourceTree = "<group>"; };
C9423DF00EF8AA6B003B785B /* CFNetwork.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CFNetwork.framework; path = System/Library/Frameworks/CFNetwork.framework; sourceTree = SDKROOT; };
Expand Down Expand Up @@ -77,12 +80,14 @@
isa = PBXGroup;
children = (
7646A321120C8F25000ED48C /* Additions */,
C9C2D8780EB6E09C00A3D071 /* AudioStreamer.m */,
C9C2D8790EB6E09C00A3D071 /* AudioStreamer.h */,
C9C2D8780EB6E09C00A3D071 /* AudioStreamer.m */,
1D3623240D0F684500981E51 /* iPhoneStreamingPlayerAppDelegate.h */,
1D3623250D0F684500981E51 /* iPhoneStreamingPlayerAppDelegate.m */,
28D7ACF60DDB3853001CB0EB /* iPhoneStreamingPlayerViewController.h */,
28D7ACF70DDB3853001CB0EB /* iPhoneStreamingPlayerViewController.m */,
762375BD120E049F00518DB5 /* LevelMeterView.h */,
762375BE120E049F00518DB5 /* LevelMeterView.m */,
);
path = Classes;
sourceTree = "<group>";
Expand Down Expand Up @@ -216,6 +221,7 @@
28D7ACF80DDB3853001CB0EB /* iPhoneStreamingPlayerViewController.m in Sources */,
C9C2D87A0EB6E09C00A3D071 /* AudioStreamer.m in Sources */,
7646A326120C8F3C000ED48C /* UIDevice+Hardware.m in Sources */,
762375BF120E049F00518DB5 /* LevelMeterView.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down

0 comments on commit e9c4938

Please sign in to comment.