Added the ability to dynamically remove or add EQ unit to save battery when EQ unit is not needed

This commit is contained in:
Thong Nguyen 2014-02-11 19:24:41 +00:00
parent dee6322751
commit 60d48a0682
7 changed files with 126 additions and 25 deletions

View File

@ -418,6 +418,7 @@
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "ExampleApp/ExampleApp-Prefix.pch";
INFOPLIST_FILE = "ExampleApp/ExampleApp-Info.plist";

View File

@ -27,19 +27,21 @@
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&error];
[[AVAudioSession sharedInstance] setActive:YES error:&error];
Float32 bufferLength = 0.1;
AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareIOBufferDuration, sizeof(bufferLength), &bufferLength);
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
audioPlayer = [[STKAudioPlayer alloc] initWithOptions:(STKAudioPlayerOptions){ .flushQueueOnSeek = YES, .enableVolumeMixer = YES, .equalizerBandFrequencies = {50, 100, 200, 400, 800, 1600, 2600, 16000} }];
audioPlayer = [[STKAudioPlayer alloc] initWithOptions:(STKAudioPlayerOptions){ .flushQueueOnSeek = YES, .enableVolumeMixer = NO, .equalizerBandFrequencies = {50, 100, 200, 400, 800, 1600, 2600, 16000} }];
audioPlayer.meteringEnabled = YES;
audioPlayer.volume = 1;
AudioPlayerView* audioPlayerView = [[AudioPlayerView alloc] initWithFrame:self.window.bounds];
AudioPlayerView* audioPlayerView = [[AudioPlayerView alloc] initWithFrame:self.window.bounds andAudioPlayer:audioPlayer];
audioPlayerView.delegate = self;
audioPlayerView.audioPlayer = audioPlayer;
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
[self becomeFirstResponder];

View File

@ -51,6 +51,7 @@
UILabel* label;
UILabel* statusLabel;
UISlider* slider;
UISwitch* enableEqSwitch;
UISwitch* repeatSwitch;
UIButton* muteButton;
UIButton* playButton;
@ -65,4 +66,6 @@
@property (readwrite, retain) STKAudioPlayer* audioPlayer;
@property (readwrite, unsafe_unretained) id<AudioPlayerViewDelegate> delegate;
- (id)initWithFrame:(CGRect)frame andAudioPlayer:(STKAudioPlayer*)audioPlayer;
@end

View File

@ -47,12 +47,14 @@
@implementation AudioPlayerView
@synthesize audioPlayer, delegate;
- (id)initWithFrame:(CGRect)frame
- (id)initWithFrame:(CGRect)frame andAudioPlayer:(STKAudioPlayer*)audioPlayerIn
{
self = [super initWithFrame:frame];
if (self)
{
self.audioPlayer = audioPlayerIn;
CGSize size = CGSizeMake(220, 50);
playFromHTTPButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
@ -97,7 +99,12 @@
size = CGSizeMake(80, 50);
repeatSwitch = [[UISwitch alloc] initWithFrame:CGRectMake((320 - size.width) / 2, frame.size.height * 0.15 + 180, size.width, size.height)];
repeatSwitch = [[UISwitch alloc] initWithFrame:CGRectMake(30, frame.size.height * 0.15 + 180, size.width, size.height)];
enableEqSwitch = [[UISwitch alloc] initWithFrame:CGRectMake(320 - size.width - 30, frame.size.height * 0.15 + 180, size.width, size.height)];
enableEqSwitch.on = audioPlayer.equalizerEnabled;
[enableEqSwitch addTarget:self action:@selector(onEnableEqSwitch) forControlEvents:UIControlEventAllTouchEvents];
label = [[UILabel alloc] initWithFrame:CGRectMake(0, slider.frame.origin.y + slider.frame.size.height + 10, frame.size.width, 25)];
@ -123,6 +130,7 @@
[self addSubview:stopButton];
[self addSubview:meter];
[self addSubview:muteButton];
[self addSubview:enableEqSwitch];
[self setupTimer];
[self updateControls];
@ -131,6 +139,11 @@
return self;
}
-(void) onEnableEqSwitch
{
audioPlayer.equalizerEnabled = self->enableEqSwitch.on;
}
-(void) sliderChanged
{
if (!audioPlayer)

View File

@ -40,14 +40,22 @@
[[self.window contentView] addSubview:playFromHTTPButton];
[[self.window contentView] addSubview:meter];
audioPlayer = [[STKAudioPlayer alloc] initWithOptions:(STKAudioPlayerOptions){ .enableVolumeMixer = NO, .equalizerBandFrequencies = {0, 50, 100, 200, 400, 800, 1600, 2600, 16000} } ];
audioPlayer = [[STKAudioPlayer alloc] initWithOptions:(STKAudioPlayerOptions){ .enableVolumeMixer = NO, .equalizerBandFrequencies = {50, 100, 200, 400, 800, 1600, 2600, 16000} } ];
audioPlayer.delegate = self;
audioPlayer.meteringEnabled = YES;
audioPlayer.volume = 0.1;
[self performSelector:@selector(test) withObject:nil afterDelay:4];
[self performSelector:@selector(test) withObject:nil afterDelay:8];
[NSTimer scheduledTimerWithTimeInterval:0.01 target:self selector:@selector(tick:) userInfo:nil repeats:YES];
}
-(void) test
{
audioPlayer.equalizerEnabled = !audioPlayer.equalizerEnabled;
}
-(void) playFromHTTP
{
[audioPlayer play:@"http://fs.bloom.fm/oss/audiosamples/sample.mp3"];

View File

@ -140,6 +140,8 @@ typedef void(^STKFrameFilter)(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UIn
@property (readonly) double progress;
/// Enables or disables peak and average decibel meteting
@property (readwrite) BOOL meteringEnabled;
/// Enables or disables the EQ
@property (readwrite) BOOL equalizerEnabled;
/// Returns an array of STKFrameFilterEntry objects representing the filters currently in use
@property (readonly) NSArray* frameFilters;
/// Returns the items pending to be played (includes buffering and upcoming items but does not include the current item)

View File

@ -171,8 +171,12 @@ static AudioStreamBasicDescription canonicalAudioStreamBasicDescription;
Float32 averagePowerDb[2];
BOOL meteringEnabled;
BOOL equalizerOn;
BOOL equalizerEnabled;
STKAudioPlayerOptions options;
NSMutableArray* converterNodes;
AUGraph audioGraph;
AUNode eqNode;
AUNode mixerNode;
@ -446,6 +450,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
options = optionsIn;
self->volume = 1.0;
self->equalizerEnabled = optionsIn.equalizerBandFrequencies[0] != 0;
PopulateOptionsWithDefault(&options);
@ -522,7 +527,11 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn
AudioComponentInstanceDispose(outputUnit);
}
AUGraphClose(audioGraph);
if (audioGraph)
{
AUGraphUninitialize(audioGraph);
AUGraphClose(audioGraph);
}
pthread_mutex_destroy(&playerMutex);
pthread_mutex_destroy(&mainThreadSyncCallMutex);
@ -1955,6 +1964,8 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl
CHECK_STATUS_AND_RETURN_VALUE(AudioUnitSetProperty(convertUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &srcFormat, sizeof(srcFormat)), 0);
CHECK_STATUS_AND_RETURN_VALUE(AudioUnitSetProperty(convertUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &desFormat, sizeof(desFormat)), 0);
CHECK_STATUS_AND_RETURN_VALUE(AudioUnitSetProperty(convertUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &maxFramesPerSlice, sizeof(maxFramesPerSlice)), 0);
[converterNodes addObject:@(convertNode)];
return convertNode;
}
@ -1962,24 +1973,33 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl
-(void) connectNodes:(AUNode)srcNode desNode:(AUNode)desNode srcUnit:(AudioComponentInstance)srcUnit desUnit:(AudioComponentInstance)desUnit
{
OSStatus status;
BOOL addConverter = NO;
AudioStreamBasicDescription srcFormat, desFormat;
UInt32 size = sizeof(AudioStreamBasicDescription);
CHECK_STATUS_AND_RETURN(AudioUnitGetProperty(srcUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &srcFormat, &size));
CHECK_STATUS_AND_RETURN(AudioUnitGetProperty(desUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &desFormat, &size));
status = AudioUnitSetProperty(desUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &srcFormat, sizeof(srcFormat));
addConverter = memcmp(&srcFormat, &desFormat, sizeof(srcFormat)) != 0;
if (status)
if (addConverter)
{
AUNode convertNode = [self createConverterNode:srcFormat desFormat:desFormat];
CHECK_STATUS_AND_RETURN(AUGraphConnectNodeInput(audioGraph, convertNode, 0, mixerNode, 0));
status = AudioUnitSetProperty(desUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &srcFormat, sizeof(srcFormat));
addConverter = status != 0;
}
else
{
if (addConverter)
{
AUNode convertNode = [self createConverterNode:srcFormat desFormat:desFormat];
CHECK_STATUS_AND_RETURN(AUGraphConnectNodeInput(audioGraph, srcNode, 0, convertNode, 0));
CHECK_STATUS_AND_RETURN(AUGraphConnectNodeInput(audioGraph, convertNode, 0, desNode, 0));
}
else
{
CHECK_STATUS_AND_RETURN(AUGraphConnectNodeInput(audioGraph, srcNode, 0, desNode, 0));
}
}
}
-(void) setOutputCallbackForFirstNode:(AUNode)firstNode firstUnit:(AudioComponentInstance)firstUnit
@ -2014,8 +2034,7 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl
-(void) createAudioGraph
{
OSStatus status;
NSMutableArray* nodes = [[NSMutableArray alloc] init];
NSMutableArray* units = [[NSMutableArray alloc] init];
converterNodes = [[NSMutableArray alloc] init];
CHECK_STATUS_AND_RETURN(NewAUGraph(&audioGraph));
CHECK_STATUS_AND_RETURN(AUGraphOpen(audioGraph));
@ -2024,11 +2043,46 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl
[self createMixerUnit];
[self createOutputUnit];
[self connectGraph];
CHECK_STATUS_AND_RETURN(AUGraphInitialize(audioGraph));
self.volume = self->volume;
}
-(void) connectGraph
{
OSStatus status;
NSMutableArray* nodes = [[NSMutableArray alloc] init];
NSMutableArray* units = [[NSMutableArray alloc] init];
AUGraphClearConnections(audioGraph);
for (NSNumber* converterNode in converterNodes)
{
status = AUGraphRemoveNode(audioGraph, (AUNode)converterNode.intValue);
}
[converterNodes removeAllObjects];
if (eqNode)
{
[nodes addObject:@(eqNode)];
[units addObject:[NSValue valueWithPointer:eqUnit]];
if (self->equalizerEnabled)
{
[nodes addObject:@(eqNode)];
[units addObject:[NSValue valueWithPointer:eqUnit]];
self->equalizerOn = YES;
}
else
{
self->equalizerOn = NO;
}
}
else
{
self->equalizerOn = NO;
}
if (mixerNode)
{
@ -2041,7 +2095,7 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl
[nodes addObject:@(outputNode)];
[units addObject:[NSValue valueWithPointer:outputUnit]];
}
[self setOutputCallbackForFirstNode:(AUNode)[[nodes objectAtIndex:0] intValue] firstUnit:(AudioComponentInstance)[[units objectAtIndex:0] pointerValue]];
for (int i = 0; i < nodes.count - 1; i++)
@ -2053,10 +2107,6 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl
[self connectNodes:srcNode desNode:desNode srcUnit:srcUnit desUnit:desUnit];
}
CHECK_STATUS_AND_RETURN(AUGraphInitialize(audioGraph));
self.volume = self->volume;
}
-(BOOL) startAudioGraph
@ -2585,6 +2635,17 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags*
entry->filter(asbd.mChannelsPerFrame, asbd.mBytesPerFrame, inNumberFrames, ioData->mBuffers[0].mData);
}
}
if (audioPlayer->equalizerEnabled != audioPlayer->equalizerOn)
{
Boolean isUpdated;
[audioPlayer connectGraph];
AUGraphUpdate(audioPlayer->audioGraph, &isUpdated);
isUpdated = isUpdated;
}
if (entry == nil)
{
@ -2966,4 +3027,15 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags*
return self->volume;
}
-(BOOL) equalizerEnabled
{
return self->equalizerEnabled;
}
-(void) setEqualizerEnabled:(BOOL)value
{
self->equalizerEnabled = value;
}
@end