diff --git a/ExampleApp/ExampleApp/AppDelegate.m b/ExampleApp/ExampleApp/AppDelegate.m index feeab66..219e046 100644 --- a/ExampleApp/ExampleApp/AppDelegate.m +++ b/ExampleApp/ExampleApp/AppDelegate.m @@ -31,7 +31,7 @@ self.window.backgroundColor = [UIColor whiteColor]; - audioPlayer = [[STKAudioPlayer alloc] initWithOptions:(STKAudioPlayerOptions){ .flushQueueOnSeek = YES, .enableVolumeMixer = YES}]; + audioPlayer = [[STKAudioPlayer alloc] initWithOptions:(STKAudioPlayerOptions){ .flushQueueOnSeek = YES, .enableVolumeMixer = NO, .equalizerBandFrequencies = { 0 } /*{50, 100, 200, 400, 800, 1600, 2600, 16000}*/ }]; audioPlayer.meteringEnabled = YES; audioPlayer.volume = 1.0; diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.h b/StreamingKit/StreamingKit/STKAudioPlayer.h index 1948378..138c736 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.h +++ b/StreamingKit/StreamingKit/STKAudioPlayer.h @@ -84,6 +84,8 @@ typedef struct BOOL flushQueueOnSeek; /// If YES then volume control will be enabled on iOS BOOL enableVolumeMixer; + /// A pointer to a 0 terminated array of band frequencies (iOS 5.0 and later, OSX 10.9 and later) + Float32 equalizerBandFrequencies[24]; /// The size of the internal I/O read buffer. This data in this buffer is transient and does not need to be larger. UInt32 readBufferSize; /// The size of the decompressed buffer (Default is 10 seconds which uses about 1.7MB of RAM) diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index 45f4dc6..e8604d1 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -84,7 +84,7 @@ static void PopulateOptionsWithDefault(STKAudioPlayerOptions* options) } #define CHECK_STATUS_AND_RETURN(call) \ - if ((call)) \ + if ((status = (call))) \ { \ pthread_mutex_unlock(&playerMutex); \ [self unexpectedError:STKAudioPlayerErrorAudioSystemError]; \ @@ -144,6 +144,9 @@ STKAudioPlayerInternalState; #pragma mark STKAudioPlayer +static AudioComponentDescription convertUnitDescription; +static AudioStreamBasicDescription canonicalAudioStreamBasicDescription; + @interface STKAudioPlayer() { BOOL muted; @@ -160,11 +163,15 @@ STKAudioPlayerInternalState; STKAudioPlayerOptions options; AUGraph audioGraph; + AUNode eqNode; AUNode mixerNode; AUNode outputNode; + AudioComponentInstance eqUnit; AudioComponentInstance mixerUnit; AudioComponentInstance outputUnit; + UInt32 eqBandCount; + UInt32 framesRequiredToStartPlaying; UInt32 framesRequiredToPlayAfterRebuffering; @@ -185,7 +192,6 @@ STKAudioPlayerInternalState; AudioBufferList pcmAudioBufferList; AudioConverterRef audioConverterRef; - AudioStreamBasicDescription canonicalAudioStreamBasicDescription; AudioStreamBasicDescription audioConverterAudioStreamBasicDescription; BOOL discontinuous; @@ -239,6 +245,31 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn @implementation STKAudioPlayer ++(void) initialize +{ + convertUnitDescription = (AudioComponentDescription) + { + .componentManufacturer = kAudioUnitManufacturer_Apple, + .componentType = kAudioUnitType_FormatConverter, + .componentSubType = kAudioUnitSubType_AUConverter, + .componentFlags = 0, + .componentFlagsMask = 0 + }; + + const int bytesPerSample = sizeof(AudioSampleType); + + canonicalAudioStreamBasicDescription = (AudioStreamBasicDescription) + { + .mSampleRate = 44100.00, + .mFormatID = kAudioFormatLinearPCM, + .mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked, + .mFramesPerPacket = 1, + .mChannelsPerFrame = 2, + .mBytesPerFrame = bytesPerSample * 2 /*channelsPerFrame*/, + .mBitsPerChannel = 8 * bytesPerSample, + .mBytesPerPacket = (bytesPerSample * 2) + }; +} -(STKAudioPlayerOptions) options { return options; @@ -372,17 +403,6 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn PopulateOptionsWithDefault(&options); - const int bytesPerSample = sizeof(AudioSampleType); - - canonicalAudioStreamBasicDescription.mSampleRate = 44100.00; - canonicalAudioStreamBasicDescription.mFormatID = kAudioFormatLinearPCM; - canonicalAudioStreamBasicDescription.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked; - canonicalAudioStreamBasicDescription.mFramesPerPacket = 1; - canonicalAudioStreamBasicDescription.mChannelsPerFrame = 2; - canonicalAudioStreamBasicDescription.mBytesPerFrame = bytesPerSample * canonicalAudioStreamBasicDescription.mChannelsPerFrame; - canonicalAudioStreamBasicDescription.mBitsPerChannel = 8 * bytesPerSample; - canonicalAudioStreamBasicDescription.mBytesPerPacket = canonicalAudioStreamBasicDescription.mBytesPerFrame * canonicalAudioStreamBasicDescription.mFramesPerPacket; - framesRequiredToStartPlaying = canonicalAudioStreamBasicDescription.mSampleRate * options.secondsRequiredToStartPlaying; framesRequiredToPlayAfterRebuffering = canonicalAudioStreamBasicDescription.mSampleRate * options.secondsRequiredToStartPlayingAfterBufferUnderun; @@ -1841,6 +1861,43 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl CHECK_STATUS_AND_RETURN(AudioUnitSetProperty(outputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &canonicalAudioStreamBasicDescription, sizeof(canonicalAudioStreamBasicDescription))); + if (self->options.equalizerBandFrequencies[0] != 0) + { + AudioComponentDescription eqDescription = (AudioComponentDescription) + { + .componentType = kAudioUnitType_Effect, + .componentSubType = kAudioUnitSubType_NBandEQ, + .componentManufacturer=kAudioUnitManufacturer_Apple + }; + + CHECK_STATUS_AND_RETURN(AudioUnitSetProperty(eqUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &canonicalAudioStreamBasicDescription, sizeof(canonicalAudioStreamBasicDescription))); + + CHECK_STATUS_AND_RETURN(AUGraphAddNode(audioGraph, &eqDescription, &eqNode)); + CHECK_STATUS_AND_RETURN(AUGraphNodeInfo(audioGraph, eqNode, NULL, &eqUnit)); + + while (self->options.equalizerBandFrequencies[eqBandCount] != 0) + { + eqBandCount++; + } + + CHECK_STATUS_AND_RETURN(AudioUnitSetProperty(eqUnit, kAUNBandEQProperty_NumberOfBands, kAudioUnitScope_Global, 0, &eqBandCount, sizeof(eqBandCount))); + + for (int i = 0; i < eqBandCount; i++) + { + CHECK_STATUS_AND_RETURN(AudioUnitSetParameter(eqUnit, kAUNBandEQParam_Frequency + i, kAudioUnitScope_Global, 0, (AudioUnitParameterValue)self->options.equalizerBandFrequencies[i], 0)); + } + + for (int i = 0; i < eqBandCount; i++) + { + CHECK_STATUS_AND_RETURN(AudioUnitSetParameter(eqUnit, kAUNBandEQParam_BypassBand + i, kAudioUnitScope_Global, 0, (AudioUnitParameterValue)0, 0)); + } + + for (int i = 0; i < eqBandCount; i++) + { + CHECK_STATUS_AND_RETURN(AudioUnitSetParameter(eqUnit, kAUNBandEQParam_Gain + i, kAudioUnitScope_Global, -74, 0, 0)); + } + } + AURenderCallbackStruct callbackStruct; callbackStruct.inputProc = OutputRenderCallback; @@ -1884,13 +1941,30 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl } else { + if (eqUnit) + { + CHECK_STATUS_AND_RETURN(AUGraphSetNodeInputCallback(audioGraph, eqNode, 0, &callbackStruct)); + CHECK_STATUS_AND_RETURN(AUGraphConnectNodeInput(audioGraph, eqNode, 0, mixerNode, 0)); + } + else + { + CHECK_STATUS_AND_RETURN(AUGraphSetNodeInputCallback(audioGraph, mixerNode, 0, &callbackStruct)); + } + CHECK_STATUS_AND_RETURN(AUGraphConnectNodeInput(audioGraph, mixerNode, 0, outputNode, 0)); - CHECK_STATUS_AND_RETURN(status = AUGraphSetNodeInputCallback(audioGraph, mixerNode, 0, &callbackStruct)); } } else { - CHECK_STATUS_AND_RETURN(AUGraphSetNodeInputCallback(audioGraph, outputNode, 0, &callbackStruct)); + if (eqUnit) + { + CHECK_STATUS_AND_RETURN(AUGraphSetNodeInputCallback(audioGraph, eqNode, 0, &callbackStruct)); + CHECK_STATUS_AND_RETURN(AUGraphConnectNodeInput(audioGraph, eqNode, 0, outputNode, 0)); + } + else + { + CHECK_STATUS_AND_RETURN(AUGraphSetNodeInputCallback(audioGraph, outputNode, 0, &callbackStruct)); + } } CHECK_STATUS_AND_RETURN(AUGraphInitialize(audioGraph)); @@ -2407,7 +2481,7 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* if (frameFilters) { NSUInteger count = frameFilters.count; - AudioStreamBasicDescription asbd = audioPlayer->canonicalAudioStreamBasicDescription; + AudioStreamBasicDescription asbd = canonicalAudioStreamBasicDescription; for (int i = 0; i < count; i++) {