Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 60d48a0682 | |||
| dee6322751 | |||
| 5e3048a7bf | |||
| 13fc64baa2 | |||
| 5e4b500785 |
@@ -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";
|
||||
|
||||
@@ -26,19 +26,22 @@
|
||||
NSError* error;
|
||||
|
||||
[[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 = NO, .equalizerBandFrequencies = {50, 100, 200, 400, 800, 1600, 2600, 16000} }];
|
||||
audioPlayer.meteringEnabled = YES;
|
||||
audioPlayer.volume = 0.1;
|
||||
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];
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -40,14 +40,22 @@
|
||||
[[self.window contentView] addSubview:playFromHTTPButton];
|
||||
[[self.window contentView] addSubview:meter];
|
||||
|
||||
audioPlayer = [[STKAudioPlayer alloc] initWithOptions:(STKAudioPlayerOptions){ .enableVolumeMixer = YES, .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"];
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -150,6 +150,8 @@ STKAudioPlayerInternalState;
|
||||
|
||||
#pragma mark STKAudioPlayer
|
||||
|
||||
static UInt32 maxFramesPerSlice = 4096;
|
||||
|
||||
static AudioComponentDescription mixerDescription;
|
||||
static AudioComponentDescription nbandUnitDescription;
|
||||
static AudioComponentDescription outputUnitDescription;
|
||||
@@ -169,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;
|
||||
@@ -444,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);
|
||||
|
||||
@@ -520,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);
|
||||
@@ -1863,6 +1874,10 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl
|
||||
UInt32 flag = 1;
|
||||
|
||||
CHECK_STATUS_AND_RETURN(AudioUnitSetProperty(outputUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, kOutputBus, &flag, sizeof(flag)));
|
||||
|
||||
flag = 0;
|
||||
|
||||
CHECK_STATUS_AND_RETURN(AudioUnitSetProperty(outputUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &flag, sizeof(flag)));
|
||||
#endif
|
||||
|
||||
CHECK_STATUS_AND_RETURN(AudioUnitSetProperty(outputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &canonicalAudioStreamBasicDescription, sizeof(canonicalAudioStreamBasicDescription)));
|
||||
@@ -1879,6 +1894,7 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl
|
||||
|
||||
CHECK_STATUS_AND_RETURN(AUGraphAddNode(audioGraph, &mixerDescription, &mixerNode));
|
||||
CHECK_STATUS_AND_RETURN(AUGraphNodeInfo(audioGraph, mixerNode, &mixerDescription, &mixerUnit));
|
||||
CHECK_STATUS_AND_RETURN(AudioUnitSetProperty(mixerUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &maxFramesPerSlice, sizeof(maxFramesPerSlice)));
|
||||
|
||||
UInt32 busCount = 1;
|
||||
|
||||
@@ -1901,9 +1917,10 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
CHECK_STATUS_AND_RETURN(AUGraphAddNode(audioGraph, &nbandUnitDescription, &eqNode));
|
||||
CHECK_STATUS_AND_RETURN(AUGraphNodeInfo(audioGraph, eqNode, NULL, &eqUnit));
|
||||
CHECK_STATUS_AND_RETURN(AudioUnitSetProperty(eqUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &maxFramesPerSlice, sizeof(maxFramesPerSlice)));
|
||||
|
||||
while (self->options.equalizerBandFrequencies[eqBandCount] != 0)
|
||||
{
|
||||
@@ -1946,31 +1963,43 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl
|
||||
CHECK_STATUS_AND_RETURN_VALUE(status = AUGraphNodeInfo(audioGraph, convertNode, &mixerDescription, &convertUnit), 0);
|
||||
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;
|
||||
}
|
||||
|
||||
-(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
|
||||
@@ -2005,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));
|
||||
@@ -2015,10 +2043,45 @@ 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)
|
||||
@@ -2026,13 +2089,13 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl
|
||||
[nodes addObject:@(mixerNode)];
|
||||
[units addObject:[NSValue valueWithPointer:mixerUnit]];
|
||||
}
|
||||
|
||||
|
||||
if (outputNode)
|
||||
{
|
||||
[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++)
|
||||
@@ -2044,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
|
||||
@@ -2057,6 +2116,7 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl
|
||||
[self resetPcmBuffers];
|
||||
|
||||
Boolean isRunning;
|
||||
|
||||
|
||||
status = AUGraphIsRunning(audioGraph, &isRunning);
|
||||
|
||||
@@ -2575,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)
|
||||
{
|
||||
@@ -2956,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
|
||||
|
||||
@@ -138,15 +138,22 @@
|
||||
|
||||
-(void) dataAvailable
|
||||
{
|
||||
if (stream == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.httpStatusCode == 0)
|
||||
{
|
||||
CFTypeRef response = CFReadStreamCopyProperty(stream, kCFStreamPropertyHTTPResponseHeader);
|
||||
|
||||
httpHeaders = (__bridge_transfer NSDictionary*)CFHTTPMessageCopyAllHeaderFields((CFHTTPMessageRef)response);
|
||||
|
||||
self.httpStatusCode = CFHTTPMessageGetResponseStatusCode((CFHTTPMessageRef)response);
|
||||
|
||||
CFRelease(response);
|
||||
if (response)
|
||||
{
|
||||
httpHeaders = (__bridge_transfer NSDictionary*)CFHTTPMessageCopyAllHeaderFields((CFHTTPMessageRef)response);
|
||||
|
||||
self.httpStatusCode = CFHTTPMessageGetResponseStatusCode((CFHTTPMessageRef)response);
|
||||
|
||||
CFRelease(response);
|
||||
}
|
||||
|
||||
if (self.httpStatusCode == 200)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user