From 5a6d93d7c1d41ef5fe7197974ad8c21ab31f4b5d Mon Sep 17 00:00:00 2001 From: Thong Nguyen Date: Tue, 28 Jan 2014 20:53:03 +0000 Subject: [PATCH 01/33] Started experimenting with using AudioUnits rather than AudioQueues --- .../StreamingKit.xcodeproj/project.pbxproj | 4 + StreamingKit/StreamingKit/STKAudioPlayer.m | 126 +++++++++++++++++- 2 files changed, 129 insertions(+), 1 deletion(-) diff --git a/StreamingKit/StreamingKit.xcodeproj/project.pbxproj b/StreamingKit/StreamingKit.xcodeproj/project.pbxproj index 5f6d53b..f468f37 100644 --- a/StreamingKit/StreamingKit.xcodeproj/project.pbxproj +++ b/StreamingKit/StreamingKit.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + A1C9767718981BFE0057F881 /* AudioUnit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1C9767618981BFE0057F881 /* AudioUnit.framework */; }; A1E7C4CC188D57F50010896F /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1E7C4CB188D57F50010896F /* Foundation.framework */; }; A1E7C4DA188D57F60010896F /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1E7C4D9188D57F60010896F /* XCTest.framework */; }; A1E7C4DB188D57F60010896F /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1E7C4CB188D57F50010896F /* Foundation.framework */; }; @@ -46,6 +47,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + A1C9767618981BFE0057F881 /* AudioUnit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioUnit.framework; path = System/Library/Frameworks/AudioUnit.framework; sourceTree = SDKROOT; }; A1E7C4C8188D57F50010896F /* libStreamingKit.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libStreamingKit.a; sourceTree = BUILT_PRODUCTS_DIR; }; A1E7C4CB188D57F50010896F /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; A1E7C4CF188D57F50010896F /* StreamingKit-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "StreamingKit-Prefix.pch"; sourceTree = ""; }; @@ -76,6 +78,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + A1C9767718981BFE0057F881 /* AudioUnit.framework in Frameworks */, A1E7C508188D62D20010896F /* UIKit.framework in Frameworks */, A1E7C4CC188D57F50010896F /* Foundation.framework in Frameworks */, ); @@ -116,6 +119,7 @@ A1E7C4CA188D57F50010896F /* Frameworks */ = { isa = PBXGroup; children = ( + A1C9767618981BFE0057F881 /* AudioUnit.framework */, A1E7C507188D62D20010896F /* UIKit.framework */, A1E7C4CB188D57F50010896F /* Foundation.framework */, A1E7C4D9188D57F60010896F /* XCTest.framework */, diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index 1c46c13..bd17acb 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -287,6 +287,8 @@ AudioQueueBufferRefLookupEntry; { UInt8* readBuffer; int readBufferSize; + + AudioComponentInstance audioUnit; STKQueueEntry* currentlyPlayingEntry; STKQueueEntry* currentlyReadingEntry; @@ -295,6 +297,7 @@ AudioQueueBufferRefLookupEntry; NSMutableArray* bufferingQueue; bool* bufferUsed; + AudioConverterRef audioConverterRef; AudioQueueBufferRef* audioQueueBuffer; AudioQueueBufferRefLookupEntry* audioQueueBufferLookup; unsigned int audioQueueBufferRefLookupCount; @@ -304,6 +307,7 @@ AudioQueueBufferRefLookupEntry; AudioQueueRef audioQueue; AudioStreamBasicDescription currentAudioStreamBasicDescription; + AudioStreamBasicDescription canonicalAudioStreamBasicDescription; NSThread* playbackThread; NSRunLoop* playbackThreadRunLoop; @@ -516,6 +520,15 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ { if (self = [super init]) { + canonicalAudioStreamBasicDescription.mSampleRate = 44100.00; + canonicalAudioStreamBasicDescription.mFormatID = kAudioFormatLinearPCM; + canonicalAudioStreamBasicDescription.mFormatFlags = kAudioFormatFlagsCanonical; + canonicalAudioStreamBasicDescription.mFramesPerPacket = 1; + canonicalAudioStreamBasicDescription.mChannelsPerFrame = 2; + canonicalAudioStreamBasicDescription.mBitsPerChannel = 8 * sizeof(AudioSampleType); + canonicalAudioStreamBasicDescription.mBytesPerPacket = sizeof(AudioSampleType) * 2; + canonicalAudioStreamBasicDescription.mBytesPerFrame = sizeof(AudioSampleType) * 2; + readBufferSize = readBufferSizeIn; readBuffer = calloc(sizeof(UInt8), readBufferSize); @@ -1169,7 +1182,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ if (upcomingQueue.count > 0) { - if ([((STKQueueEntry*)[upcomingQueue peek]) isDefinitelyCompatible:¤tAudioStreamBasicDescription]) + if ([((STKQueueEntry*)[upcomingQueue peek]) isDefinitelyCompatible:¤tAudioStreamBasicDescription]) { return YES; } @@ -1704,6 +1717,10 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ -(void) createAudioQueue { + [self createAudioUnit]; + + return; + OSStatus error; LOGINFO(@"Called"); @@ -2537,6 +2554,8 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ -(BOOL) startAudioQueue { + [self startAudioUnit]; + OSStatus error; LOGINFO(@"Called"); @@ -3147,4 +3166,109 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ return levelMeterState[channelNumber].mAveragePower; } + +/// +/// AudioUnit +/// + +#define kOutputBus 0 +#define kInputBus 1 + +static OSStatus playbackCallback(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData) +{ + return 0; +} + +BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* classDesc) +{ + UInt32 size; + + if (AudioFormatGetPropertyInfo(kAudioFormatProperty_Decoders, sizeof(formatId), &formatId, &size) != 0) + { + return NO; + } + + UInt32 decoderCount = size / sizeof(AudioClassDescription); + AudioClassDescription encoderDescriptions[decoderCount]; + + if (AudioFormatGetProperty(kAudioFormatProperty_Decoders, sizeof(formatId), &formatId, &size, encoderDescriptions) != 0) + { + return NO; + } + + for (UInt32 i=0; i < decoderCount; ++i) + { + if (encoderDescriptions[i].mManufacturer == kAppleHardwareAudioCodecManufacturer) + { + *classDesc = encoderDescriptions[i]; + + return YES; + } + } + + return NO; +} + + +-(void) createAudioUnit +{ + pthread_mutex_lock(&playerMutex); + pthread_mutex_lock(&queueBuffersMutex); + + currentAudioStreamBasicDescription = currentlyPlayingEntry->audioStreamBasicDescription; + + OSStatus status; + AudioComponentDescription desc; + + desc.componentType = kAudioUnitType_Output; + desc.componentSubType = kAudioUnitSubType_RemoteIO; + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + + AudioComponent component = AudioComponentFindNext(NULL, &desc); + + status = AudioComponentInstanceNew(component, &audioUnit); + + UInt32 flag; + + status = AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, kOutputBus, &flag, sizeof(flag)); + status = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, &canonicalAudioStreamBasicDescription, sizeof(canonicalAudioStreamBasicDescription)); + + AudioClassDescription classDesc; + + if (GetHardwareCodecClassDesc(currentAudioStreamBasicDescription.mFormatID, &classDesc)) + { + status = AudioConverterNewSpecific(¤tAudioStreamBasicDescription, &canonicalAudioStreamBasicDescription, 1, &classDesc, &audioConverterRef); + } + else + { + status = AudioConverterNew(¤tAudioStreamBasicDescription, &canonicalAudioStreamBasicDescription, &audioConverterRef); + } + + AURenderCallbackStruct callbackStruct; + + callbackStruct.inputProc = playbackCallback; + callbackStruct.inputProcRefCon = (__bridge void*)self; + status = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, kOutputBus, &callbackStruct, sizeof(callbackStruct)); + + status = AudioUnitInitialize(audioUnit); + + pthread_mutex_unlock(&queueBuffersMutex); + pthread_mutex_unlock(&playerMutex); +} + +-(BOOL) startAudioUnit +{ + AudioOutputUnitStart(audioUnit); + + return YES; +} + +-(void) stopAudioUnit +{ + AudioOutputUnitStop(audioUnit); +} + @end + From cd0dd97ebfe2b9ff5d6ac235fe74449decf33db5 Mon Sep 17 00:00:00 2001 From: Thong Nguyen Date: Tue, 28 Jan 2014 20:53:03 +0000 Subject: [PATCH 02/33] Started experimenting with using AudioUnits rather than AudioQueues --- .../StreamingKit.xcodeproj/project.pbxproj | 4 + StreamingKit/StreamingKit/STKAudioPlayer.m | 126 +++++++++++++++++- 2 files changed, 129 insertions(+), 1 deletion(-) diff --git a/StreamingKit/StreamingKit.xcodeproj/project.pbxproj b/StreamingKit/StreamingKit.xcodeproj/project.pbxproj index 5f6d53b..f468f37 100644 --- a/StreamingKit/StreamingKit.xcodeproj/project.pbxproj +++ b/StreamingKit/StreamingKit.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + A1C9767718981BFE0057F881 /* AudioUnit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1C9767618981BFE0057F881 /* AudioUnit.framework */; }; A1E7C4CC188D57F50010896F /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1E7C4CB188D57F50010896F /* Foundation.framework */; }; A1E7C4DA188D57F60010896F /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1E7C4D9188D57F60010896F /* XCTest.framework */; }; A1E7C4DB188D57F60010896F /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1E7C4CB188D57F50010896F /* Foundation.framework */; }; @@ -46,6 +47,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + A1C9767618981BFE0057F881 /* AudioUnit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioUnit.framework; path = System/Library/Frameworks/AudioUnit.framework; sourceTree = SDKROOT; }; A1E7C4C8188D57F50010896F /* libStreamingKit.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libStreamingKit.a; sourceTree = BUILT_PRODUCTS_DIR; }; A1E7C4CB188D57F50010896F /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; A1E7C4CF188D57F50010896F /* StreamingKit-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "StreamingKit-Prefix.pch"; sourceTree = ""; }; @@ -76,6 +78,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + A1C9767718981BFE0057F881 /* AudioUnit.framework in Frameworks */, A1E7C508188D62D20010896F /* UIKit.framework in Frameworks */, A1E7C4CC188D57F50010896F /* Foundation.framework in Frameworks */, ); @@ -116,6 +119,7 @@ A1E7C4CA188D57F50010896F /* Frameworks */ = { isa = PBXGroup; children = ( + A1C9767618981BFE0057F881 /* AudioUnit.framework */, A1E7C507188D62D20010896F /* UIKit.framework */, A1E7C4CB188D57F50010896F /* Foundation.framework */, A1E7C4D9188D57F60010896F /* XCTest.framework */, diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index 1c46c13..bd17acb 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -287,6 +287,8 @@ AudioQueueBufferRefLookupEntry; { UInt8* readBuffer; int readBufferSize; + + AudioComponentInstance audioUnit; STKQueueEntry* currentlyPlayingEntry; STKQueueEntry* currentlyReadingEntry; @@ -295,6 +297,7 @@ AudioQueueBufferRefLookupEntry; NSMutableArray* bufferingQueue; bool* bufferUsed; + AudioConverterRef audioConverterRef; AudioQueueBufferRef* audioQueueBuffer; AudioQueueBufferRefLookupEntry* audioQueueBufferLookup; unsigned int audioQueueBufferRefLookupCount; @@ -304,6 +307,7 @@ AudioQueueBufferRefLookupEntry; AudioQueueRef audioQueue; AudioStreamBasicDescription currentAudioStreamBasicDescription; + AudioStreamBasicDescription canonicalAudioStreamBasicDescription; NSThread* playbackThread; NSRunLoop* playbackThreadRunLoop; @@ -516,6 +520,15 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ { if (self = [super init]) { + canonicalAudioStreamBasicDescription.mSampleRate = 44100.00; + canonicalAudioStreamBasicDescription.mFormatID = kAudioFormatLinearPCM; + canonicalAudioStreamBasicDescription.mFormatFlags = kAudioFormatFlagsCanonical; + canonicalAudioStreamBasicDescription.mFramesPerPacket = 1; + canonicalAudioStreamBasicDescription.mChannelsPerFrame = 2; + canonicalAudioStreamBasicDescription.mBitsPerChannel = 8 * sizeof(AudioSampleType); + canonicalAudioStreamBasicDescription.mBytesPerPacket = sizeof(AudioSampleType) * 2; + canonicalAudioStreamBasicDescription.mBytesPerFrame = sizeof(AudioSampleType) * 2; + readBufferSize = readBufferSizeIn; readBuffer = calloc(sizeof(UInt8), readBufferSize); @@ -1169,7 +1182,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ if (upcomingQueue.count > 0) { - if ([((STKQueueEntry*)[upcomingQueue peek]) isDefinitelyCompatible:¤tAudioStreamBasicDescription]) + if ([((STKQueueEntry*)[upcomingQueue peek]) isDefinitelyCompatible:¤tAudioStreamBasicDescription]) { return YES; } @@ -1704,6 +1717,10 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ -(void) createAudioQueue { + [self createAudioUnit]; + + return; + OSStatus error; LOGINFO(@"Called"); @@ -2537,6 +2554,8 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ -(BOOL) startAudioQueue { + [self startAudioUnit]; + OSStatus error; LOGINFO(@"Called"); @@ -3147,4 +3166,109 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ return levelMeterState[channelNumber].mAveragePower; } + +/// +/// AudioUnit +/// + +#define kOutputBus 0 +#define kInputBus 1 + +static OSStatus playbackCallback(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData) +{ + return 0; +} + +BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* classDesc) +{ + UInt32 size; + + if (AudioFormatGetPropertyInfo(kAudioFormatProperty_Decoders, sizeof(formatId), &formatId, &size) != 0) + { + return NO; + } + + UInt32 decoderCount = size / sizeof(AudioClassDescription); + AudioClassDescription encoderDescriptions[decoderCount]; + + if (AudioFormatGetProperty(kAudioFormatProperty_Decoders, sizeof(formatId), &formatId, &size, encoderDescriptions) != 0) + { + return NO; + } + + for (UInt32 i=0; i < decoderCount; ++i) + { + if (encoderDescriptions[i].mManufacturer == kAppleHardwareAudioCodecManufacturer) + { + *classDesc = encoderDescriptions[i]; + + return YES; + } + } + + return NO; +} + + +-(void) createAudioUnit +{ + pthread_mutex_lock(&playerMutex); + pthread_mutex_lock(&queueBuffersMutex); + + currentAudioStreamBasicDescription = currentlyPlayingEntry->audioStreamBasicDescription; + + OSStatus status; + AudioComponentDescription desc; + + desc.componentType = kAudioUnitType_Output; + desc.componentSubType = kAudioUnitSubType_RemoteIO; + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + + AudioComponent component = AudioComponentFindNext(NULL, &desc); + + status = AudioComponentInstanceNew(component, &audioUnit); + + UInt32 flag; + + status = AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, kOutputBus, &flag, sizeof(flag)); + status = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, &canonicalAudioStreamBasicDescription, sizeof(canonicalAudioStreamBasicDescription)); + + AudioClassDescription classDesc; + + if (GetHardwareCodecClassDesc(currentAudioStreamBasicDescription.mFormatID, &classDesc)) + { + status = AudioConverterNewSpecific(¤tAudioStreamBasicDescription, &canonicalAudioStreamBasicDescription, 1, &classDesc, &audioConverterRef); + } + else + { + status = AudioConverterNew(¤tAudioStreamBasicDescription, &canonicalAudioStreamBasicDescription, &audioConverterRef); + } + + AURenderCallbackStruct callbackStruct; + + callbackStruct.inputProc = playbackCallback; + callbackStruct.inputProcRefCon = (__bridge void*)self; + status = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, kOutputBus, &callbackStruct, sizeof(callbackStruct)); + + status = AudioUnitInitialize(audioUnit); + + pthread_mutex_unlock(&queueBuffersMutex); + pthread_mutex_unlock(&playerMutex); +} + +-(BOOL) startAudioUnit +{ + AudioOutputUnitStart(audioUnit); + + return YES; +} + +-(void) stopAudioUnit +{ + AudioOutputUnitStop(audioUnit); +} + @end + From 08eaa7e85b7e53851852412be91baf1a133855ba Mon Sep 17 00:00:00 2001 From: Thong Nguyen Date: Wed, 29 Jan 2014 18:41:29 +0000 Subject: [PATCH 03/33] Some more test work with using Audio Units --- StreamingKit/StreamingKit/STKAudioPlayer.m | 290 +++++++++++++++++++-- 1 file changed, 274 insertions(+), 16 deletions(-) diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index bd17acb..da6755d 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -41,10 +41,12 @@ #import "STKLocalFileDataSource.h" #import "libkern/OSAtomic.h" +#define STK_DEFAULT_PCM_BUFFER_SIZE_IN_SECONDS (5) + #define STK_BIT_RATE_ESTIMATION_MIN_PACKETS (64) #define STK_BUFFERS_NEEDED_TO_START (32) #define STK_BUFFERS_NEEDED_WHEN_UNDERUNNING (128) -#define STK_DEFAULT_READ_BUFFER_SIZE (2 * 1024) +#define STK_DEFAULT_READ_BUFFER_SIZE (10 * 1024) #define STK_DEFAULT_PACKET_BUFFER_SIZE (2048) #define STK_FRAMES_MISSED_BEFORE_CONSIDERED_UNDERRUN (1024) #define STK_DEFAULT_NUMBER_OF_AUDIOQUEUE_BUFFERS (1024) @@ -297,6 +299,15 @@ AudioQueueBufferRefLookupEntry; NSMutableArray* bufferingQueue; bool* bufferUsed; + + UInt32 pcmBufferFrameSizeInBytes; + UInt32 pcmBufferTotalFrameCount; + UInt32 pcmBufferFrameStartIndex; + UInt32 pcmBufferUsedFrameCount; + + AudioBuffer* pcmAudioBuffer; + AudioBufferList pcmAudioBufferList; + AudioConverterRef audioConverterRef; AudioQueueBufferRef* audioQueueBuffer; AudioQueueBufferRefLookupEntry* audioQueueBufferLookup; @@ -340,6 +351,8 @@ AudioQueueBufferRefLookupEntry; OSSpinLock currentEntryReferencesLock; pthread_mutex_t playerMutex; + pthread_mutex_t pcmBuffersMutex; + pthread_cond_t pcmBuffersReadyCondition; pthread_mutex_t queueBuffersMutex; pthread_cond_t queueBufferReadyCondition; pthread_mutex_t mainThreadSyncCallMutex; @@ -523,11 +536,20 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ canonicalAudioStreamBasicDescription.mSampleRate = 44100.00; canonicalAudioStreamBasicDescription.mFormatID = kAudioFormatLinearPCM; canonicalAudioStreamBasicDescription.mFormatFlags = kAudioFormatFlagsCanonical; - canonicalAudioStreamBasicDescription.mFramesPerPacket = 1; + canonicalAudioStreamBasicDescription.mFramesPerPacket = 1; canonicalAudioStreamBasicDescription.mChannelsPerFrame = 2; + canonicalAudioStreamBasicDescription.mBytesPerFrame = sizeof(AudioSampleType) * canonicalAudioStreamBasicDescription.mChannelsPerFrame; canonicalAudioStreamBasicDescription.mBitsPerChannel = 8 * sizeof(AudioSampleType); - canonicalAudioStreamBasicDescription.mBytesPerPacket = sizeof(AudioSampleType) * 2; - canonicalAudioStreamBasicDescription.mBytesPerFrame = sizeof(AudioSampleType) * 2; + canonicalAudioStreamBasicDescription.mBytesPerPacket = canonicalAudioStreamBasicDescription.mBytesPerFrame * canonicalAudioStreamBasicDescription.mFramesPerPacket; + + pcmAudioBuffer = &pcmAudioBufferList.mBuffers[0]; + pcmAudioBufferList.mNumberBuffers = 1; + pcmAudioBufferList.mBuffers[0].mDataByteSize = (canonicalAudioStreamBasicDescription.mSampleRate * STK_DEFAULT_PCM_BUFFER_SIZE_IN_SECONDS) * canonicalAudioStreamBasicDescription.mBytesPerFrame; + pcmAudioBufferList.mBuffers[0].mData = (void*)calloc(pcmAudioBuffer->mDataByteSize, 1); + pcmAudioBufferList.mBuffers[0].mNumberChannels = 2; + + pcmBufferFrameSizeInBytes = canonicalAudioStreamBasicDescription.mBytesPerFrame; + pcmBufferTotalFrameCount = pcmAudioBuffer->mDataByteSize / pcmBufferFrameSizeInBytes; readBufferSize = readBufferSizeIn; readBuffer = calloc(sizeof(UInt8), readBufferSize); @@ -548,7 +570,9 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ pthread_mutex_init(&playerMutex, &attr); pthread_mutex_init(&queueBuffersMutex, NULL); + pthread_mutex_init(&pcmBuffersMutex, NULL); pthread_cond_init(&queueBufferReadyCondition, NULL); + pthread_cond_init(&pcmBuffersReadyCondition, NULL); pthread_mutex_init(&mainThreadSyncCallMutex, NULL); pthread_cond_init(&mainThreadSyncCallReadyCondition, NULL); @@ -581,6 +605,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ pthread_mutex_destroy(&playerMutex); pthread_mutex_destroy(&queueBuffersMutex); + pthread_mutex_destroy(&pcmBuffersMutex); pthread_cond_destroy(&queueBufferReadyCondition); pthread_mutex_destroy(&mainThreadSyncCallMutex); @@ -961,7 +986,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ } } --(void) handleAudioPackets:(const void*)inputData numberBytes:(UInt32)numberBytes numberPackets:(UInt32)numberPackets packetDescriptions:(AudioStreamPacketDescription*)packetDescriptionsIn +-(void) handleAudioPackets2:(const void*)inputData numberBytes:(UInt32)numberBytes numberPackets:(UInt32)numberPackets packetDescriptions:(AudioStreamPacketDescription*)packetDescriptionsIn { if (currentlyReadingEntry == nil) { @@ -1719,8 +1744,6 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ { [self createAudioUnit]; - return; - OSStatus error; LOGINFO(@"Called"); @@ -3174,11 +3197,6 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ #define kOutputBus 0 #define kInputBus 1 -static OSStatus playbackCallback(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData) -{ - return 0; -} - BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* classDesc) { UInt32 size; @@ -3196,7 +3214,7 @@ BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* classDesc return NO; } - for (UInt32 i=0; i < decoderCount; ++i) + for (UInt32 i = 0; i < decoderCount; ++i) { if (encoderDescriptions[i].mManufacturer == kAppleHardwareAudioCodecManufacturer) { @@ -3208,7 +3226,27 @@ BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* classDesc return NO; } - + +AudioBufferList* AllocateAudioBufferList(UInt32 bytesPerFrame, UInt32 capacityFrames, UInt32 channelsPerFrame, bool interleaved) +{ + AudioBufferList *bufferList = 0; + + UInt32 numBuffers = interleaved ? 1 : channelsPerFrame; + UInt32 channelsPerBuffer = interleaved ? channelsPerFrame : 1; + + bufferList = (AudioBufferList *)(calloc(1, offsetof(AudioBufferList, mBuffers) + (sizeof(AudioBuffer) * numBuffers))); + + bufferList->mNumberBuffers = numBuffers; + + for(UInt32 bufferIndex = 0; bufferIndex < bufferList->mNumberBuffers; ++bufferIndex) + { + bufferList->mBuffers[bufferIndex].mData = (void*)(calloc(capacityFrames, bytesPerFrame)); + bufferList->mBuffers[bufferIndex].mDataByteSize = capacityFrames * bytesPerFrame; + bufferList->mBuffers[bufferIndex].mNumberChannels = channelsPerBuffer; + } + + return bufferList; +} -(void) createAudioUnit { @@ -3241,7 +3279,8 @@ BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* classDesc { status = AudioConverterNewSpecific(¤tAudioStreamBasicDescription, &canonicalAudioStreamBasicDescription, 1, &classDesc, &audioConverterRef); } - else + + if (!audioConverterRef) { status = AudioConverterNew(¤tAudioStreamBasicDescription, &canonicalAudioStreamBasicDescription, &audioConverterRef); } @@ -3260,7 +3299,9 @@ BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* classDesc -(BOOL) startAudioUnit { - AudioOutputUnitStart(audioUnit); + OSStatus status; + + status = AudioOutputUnitStart(audioUnit); return YES; } @@ -3270,5 +3311,222 @@ BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* classDesc AudioOutputUnitStop(audioUnit); } +typedef struct +{ + BOOL done; + UInt32 numberOfPackets; + AudioBuffer audioBuffer; + AudioStreamPacketDescription* packetDescriptions; +} +AudioConvertInfo; + +OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNumberDataPackets, AudioBufferList* ioData, AudioStreamPacketDescription **outDataPacketDescription, void* inUserData) +{ + AudioConvertInfo* convertInfo = (AudioConvertInfo*)inUserData; + + if (convertInfo->done) + { + ioNumberDataPackets = 0; + + return 100; + } + + ioData->mNumberBuffers = 1; + ioData->mBuffers[0] = convertInfo->audioBuffer; + *outDataPacketDescription = convertInfo->packetDescriptions; + *ioNumberDataPackets = convertInfo->numberOfPackets; + convertInfo->done = YES; + + return 0; +} + +-(void) handleAudioPackets:(const void*)inputData numberBytes:(UInt32)numberBytes numberPackets:(UInt32)numberPackets packetDescriptions:(AudioStreamPacketDescription*)packetDescriptionsIn +{ + if (currentlyReadingEntry == nil) + { + return; + } + + if (seekToTimeWasRequested || disposeWasRequested) + { + return; + } + + if (audioQueue == nil) + { + if (currentlyPlayingEntry == nil) + { + return; + } + + [self createAudioQueue]; + + if (audioQueue == nil) + { + return; + } + + if (self.internalState == STKAudioPlayerInternalStateStopped) + { + if (stopReason == AudioPlayerStopReasonEof) + { + stopReason = AudioPlayerStopReasonNoStop; + self.internalState = STKAudioPlayerInternalStateWaitingForData; + } + else + { + return; + } + } + } + else if (memcmp(¤tAudioStreamBasicDescription, ¤tlyReadingEntry->audioStreamBasicDescription, sizeof(currentAudioStreamBasicDescription)) != 0) + { + if (currentlyReadingEntry == currentlyPlayingEntry && currentlyReadingEntry != nil) + { + [self createAudioQueue]; + + if (audioQueue == nil) + { + return; + } + } + else + { + return; + } + } + + if (discontinuous) + { + discontinuous = NO; + } + + [self startAudioQueue]; + + OSStatus status; + + AudioConvertInfo convertInfo; + + convertInfo.done = NO; + convertInfo.numberOfPackets = numberPackets; + convertInfo.packetDescriptions = packetDescriptionsIn; + convertInfo.audioBuffer.mData = (void *)inputData; + convertInfo.audioBuffer.mDataByteSize = numberBytes; + convertInfo.audioBuffer.mNumberChannels = currentAudioStreamBasicDescription.mChannelsPerFrame; + + while (true) + { + UInt32 used; + UInt32 start; + UInt32 end; + UInt32 framesLeftInsideBuffer; + + pthread_mutex_lock(&pcmBuffersMutex); + + while (true) + { + used = pcmBufferUsedFrameCount; + start = pcmBufferFrameStartIndex; + end = pcmBufferFrameStartIndex + pcmBufferUsedFrameCount; + framesLeftInsideBuffer = pcmBufferTotalFrameCount - used; + + if (framesLeftInsideBuffer > 0 || (disposeWasRequested || seekToTimeWasRequested || self.internalState == STKAudioPlayerInternalStateStopped || self.internalState == STKAudioPlayerInternalStateStopping || self.internalState == STKAudioPlayerInternalStateDisposed)) + { + break; + } + + pthread_cond_wait(&pcmBuffersReadyCondition, &pcmBuffersMutex); + } + + pthread_mutex_unlock(&pcmBuffersMutex); + + AudioBuffer* localPcmAudioBuffer; + AudioBufferList localPcmBufferList; + + localPcmBufferList.mNumberBuffers = 1; + localPcmAudioBuffer = &localPcmBufferList.mBuffers[0]; + + if (end >= start) + { + UInt32 framesAdded = 0; + UInt32 framesToDecode = pcmBufferTotalFrameCount - end; + + localPcmAudioBuffer->mData = pcmAudioBuffer->mData + (end * pcmBufferFrameSizeInBytes); + localPcmAudioBuffer->mDataByteSize = framesToDecode * pcmBufferFrameSizeInBytes; + localPcmAudioBuffer->mNumberChannels = pcmAudioBuffer->mNumberChannels; + + status = AudioConverterFillComplexBuffer(audioConverterRef, AudioConverterCallback, (void*)&convertInfo, &framesToDecode, &localPcmBufferList, NULL); + + framesAdded = framesToDecode; + + if (status == 100) + { + pthread_mutex_lock(&pcmBuffersMutex); + + pcmBufferUsedFrameCount += framesAdded; + + pthread_mutex_unlock(&pcmBuffersMutex); + + return; + } + else if (status != 0) + { + NSLog(@""); + } + + framesToDecode = start; + + if (framesToDecode == 0) + { + pthread_mutex_lock(&pcmBuffersMutex); + + pcmBufferUsedFrameCount += framesAdded; + + pthread_mutex_unlock(&pcmBuffersMutex); + + continue; + } + + localPcmAudioBuffer->mData = pcmAudioBuffer->mData; + localPcmAudioBuffer->mDataByteSize = framesToDecode * pcmBufferFrameSizeInBytes; + localPcmAudioBuffer->mNumberChannels = pcmAudioBuffer->mNumberChannels; + + status = AudioConverterFillComplexBuffer(audioConverterRef, AudioConverterCallback, (void*)&convertInfo, &framesToDecode, &localPcmBufferList, NULL); + + if (status == 100) + { + framesAdded += framesToDecode; + + pthread_mutex_lock(&queueBuffersMutex); + + pcmBufferUsedFrameCount += framesAdded; + + pthread_mutex_unlock(&queueBuffersMutex); + + return; + } + else if (status != 0) + { + NSLog(@""); + } + } + else + { + NSLog(@""); + } + } +} + +static OSStatus playbackCallback(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData) +{ + STKAudioPlayer* audioPlayer = (__bridge STKAudioPlayer*)inRefCon; + + pthread_mutex_lock(&audioPlayer->pcmBuffersMutex); + + pthread_mutex_unlock(&audioPlayer->pcmBuffersMutex); + + return 0; +} + @end From 10d799a35f149e7b672ced987efdcdd2263f6eae Mon Sep 17 00:00:00 2001 From: Thong Nguyen Date: Wed, 29 Jan 2014 22:09:38 +0000 Subject: [PATCH 04/33] Got basic playback mostly working using AudioUnits --- StreamingKit/StreamingKit/STKAudioPlayer.m | 226 +++++++++++++++++---- 1 file changed, 181 insertions(+), 45 deletions(-) diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index da6755d..215b38a 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -41,12 +41,12 @@ #import "STKLocalFileDataSource.h" #import "libkern/OSAtomic.h" -#define STK_DEFAULT_PCM_BUFFER_SIZE_IN_SECONDS (5) +#define STK_DEFAULT_PCM_BUFFER_SIZE_IN_SECONDS (2) #define STK_BIT_RATE_ESTIMATION_MIN_PACKETS (64) #define STK_BUFFERS_NEEDED_TO_START (32) #define STK_BUFFERS_NEEDED_WHEN_UNDERUNNING (128) -#define STK_DEFAULT_READ_BUFFER_SIZE (10 * 1024) +#define STK_DEFAULT_READ_BUFFER_SIZE (64 * 1024) #define STK_DEFAULT_PACKET_BUFFER_SIZE (2048) #define STK_FRAMES_MISSED_BEFORE_CONSIDERED_UNDERRUN (1024) #define STK_DEFAULT_NUMBER_OF_AUDIOQUEUE_BUFFERS (1024) @@ -300,10 +300,11 @@ AudioQueueBufferRefLookupEntry; bool* bufferUsed; - UInt32 pcmBufferFrameSizeInBytes; - UInt32 pcmBufferTotalFrameCount; - UInt32 pcmBufferFrameStartIndex; - UInt32 pcmBufferUsedFrameCount; + OSSpinLock pcmBufferSpinLock; + volatile UInt32 pcmBufferFrameSizeInBytes; + volatile UInt32 pcmBufferTotalFrameCount; + volatile UInt32 pcmBufferFrameStartIndex; + volatile UInt32 pcmBufferUsedFrameCount; AudioBuffer* pcmAudioBuffer; AudioBufferList pcmAudioBufferList; @@ -3268,9 +3269,11 @@ AudioBufferList* AllocateAudioBufferList(UInt32 bytesPerFrame, UInt32 capacityFr status = AudioComponentInstanceNew(component, &audioUnit); - UInt32 flag; + UInt32 flag = 1; - status = AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, kOutputBus, &flag, sizeof(flag)); + status = AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &flag, sizeof(flag)); + status = AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, kOutputBus, &flag, sizeof(flag)); + status = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &canonicalAudioStreamBasicDescription, sizeof(canonicalAudioStreamBasicDescription)); status = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, &canonicalAudioStreamBasicDescription, sizeof(canonicalAudioStreamBasicDescription)); AudioClassDescription classDesc; @@ -3289,12 +3292,15 @@ AudioBufferList* AllocateAudioBufferList(UInt32 bytesPerFrame, UInt32 capacityFr callbackStruct.inputProc = playbackCallback; callbackStruct.inputProcRefCon = (__bridge void*)self; - status = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, kOutputBus, &callbackStruct, sizeof(callbackStruct)); + + status = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, kOutputBus, &callbackStruct, sizeof(callbackStruct)); status = AudioUnitInitialize(audioUnit); pthread_mutex_unlock(&queueBuffersMutex); pthread_mutex_unlock(&playerMutex); + + [self startAudioUnit]; } -(BOOL) startAudioUnit @@ -3333,7 +3339,12 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu ioData->mNumberBuffers = 1; ioData->mBuffers[0] = convertInfo->audioBuffer; - *outDataPacketDescription = convertInfo->packetDescriptions; + + if (outDataPacketDescription) + { + *outDataPacketDescription = convertInfo->packetDescriptions; + } + *ioNumberDataPackets = convertInfo->numberOfPackets; convertInfo->done = YES; @@ -3400,8 +3411,8 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu { discontinuous = NO; } - - [self startAudioQueue]; + + [self startAudioUnit]; OSStatus status; @@ -3416,36 +3427,59 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu while (true) { - UInt32 used; - UInt32 start; - UInt32 end; - UInt32 framesLeftInsideBuffer; + OSSpinLockLock(&pcmBufferSpinLock); + UInt32 used = pcmBufferUsedFrameCount; + UInt32 start = pcmBufferFrameStartIndex; + UInt32 end = (pcmBufferFrameStartIndex + pcmBufferUsedFrameCount) % pcmBufferTotalFrameCount; + UInt32 framesLeftInsideBuffer = pcmBufferTotalFrameCount - used; + OSSpinLockUnlock(&pcmBufferSpinLock); - pthread_mutex_lock(&pcmBuffersMutex); - - while (true) + if (framesLeftInsideBuffer == 0) { - used = pcmBufferUsedFrameCount; - start = pcmBufferFrameStartIndex; - end = pcmBufferFrameStartIndex + pcmBufferUsedFrameCount; - framesLeftInsideBuffer = pcmBufferTotalFrameCount - used; - - if (framesLeftInsideBuffer > 0 || (disposeWasRequested || seekToTimeWasRequested || self.internalState == STKAudioPlayerInternalStateStopped || self.internalState == STKAudioPlayerInternalStateStopping || self.internalState == STKAudioPlayerInternalStateDisposed)) + pthread_mutex_lock(&pcmBuffersMutex); + + while (true) { - break; - } + OSSpinLockLock(&pcmBufferSpinLock); + used = pcmBufferUsedFrameCount; + start = pcmBufferFrameStartIndex; + end = (pcmBufferFrameStartIndex + pcmBufferUsedFrameCount) % pcmBufferTotalFrameCount; + framesLeftInsideBuffer = pcmBufferTotalFrameCount - used; + OSSpinLockUnlock(&pcmBufferSpinLock); - pthread_cond_wait(&pcmBuffersReadyCondition, &pcmBuffersMutex); + if (framesLeftInsideBuffer > 0) + { + break; + } + + if (disposeWasRequested || seekToTimeWasRequested || self.internalState == STKAudioPlayerInternalStateStopped || self.internalState == STKAudioPlayerInternalStateStopping || self.internalState == STKAudioPlayerInternalStateDisposed) + { + pthread_mutex_unlock(&pcmBuffersMutex); + + return; + } + + waiting = YES; + + pthread_cond_wait(&pcmBuffersReadyCondition, &pcmBuffersMutex); + + waiting = NO; + } + + pthread_mutex_unlock(&pcmBuffersMutex); } - pthread_mutex_unlock(&pcmBuffersMutex); - AudioBuffer* localPcmAudioBuffer; AudioBufferList localPcmBufferList; localPcmBufferList.mNumberBuffers = 1; localPcmAudioBuffer = &localPcmBufferList.mBuffers[0]; + if (start == end) + { + NSLog(@""); + } + if (end >= start) { UInt32 framesAdded = 0; @@ -3462,9 +3496,7 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu if (status == 100) { pthread_mutex_lock(&pcmBuffersMutex); - pcmBufferUsedFrameCount += framesAdded; - pthread_mutex_unlock(&pcmBuffersMutex); return; @@ -3478,11 +3510,9 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu if (framesToDecode == 0) { - pthread_mutex_lock(&pcmBuffersMutex); - + OSSpinLockLock(&pcmBufferSpinLock); pcmBufferUsedFrameCount += framesAdded; - - pthread_mutex_unlock(&pcmBuffersMutex); + OSSpinLockUnlock(&pcmBufferSpinLock); continue; } @@ -3493,18 +3523,24 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu status = AudioConverterFillComplexBuffer(audioConverterRef, AudioConverterCallback, (void*)&convertInfo, &framesToDecode, &localPcmBufferList, NULL); + framesAdded += framesToDecode; + if (status == 100) { - framesAdded += framesToDecode; - - pthread_mutex_lock(&queueBuffersMutex); - + OSSpinLockLock(&pcmBufferSpinLock); pcmBufferUsedFrameCount += framesAdded; - - pthread_mutex_unlock(&queueBuffersMutex); + OSSpinLockUnlock(&pcmBufferSpinLock); return; } + else if (status == 0) + { + OSSpinLockLock(&pcmBufferSpinLock); + pcmBufferUsedFrameCount += framesAdded; + OSSpinLockUnlock(&pcmBufferSpinLock); + + continue; + } else if (status != 0) { NSLog(@""); @@ -3512,7 +3548,37 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu } else { - NSLog(@""); + UInt32 framesAdded = 0; + UInt32 framesToDecode = start - end; + + localPcmAudioBuffer->mData = pcmAudioBuffer->mData + (end * pcmBufferFrameSizeInBytes); + localPcmAudioBuffer->mDataByteSize = framesToDecode * pcmBufferFrameSizeInBytes; + localPcmAudioBuffer->mNumberChannels = pcmAudioBuffer->mNumberChannels; + + status = AudioConverterFillComplexBuffer(audioConverterRef, AudioConverterCallback, (void*)&convertInfo, &framesToDecode, &localPcmBufferList, NULL); + + framesAdded = framesToDecode; + + if (status == 100) + { + OSSpinLockLock(&pcmBufferSpinLock); + pcmBufferUsedFrameCount += framesAdded; + OSSpinLockUnlock(&pcmBufferSpinLock); + + return; + } + else if (status == 0) + { + OSSpinLockLock(&pcmBufferSpinLock); + pcmBufferUsedFrameCount += framesAdded; + OSSpinLockUnlock(&pcmBufferSpinLock); + + continue; + } + else if (status != 0) + { + NSLog(@""); + } } } } @@ -3521,9 +3587,79 @@ static OSStatus playbackCallback(void* inRefCon, AudioUnitRenderActionFlags* ioA { STKAudioPlayer* audioPlayer = (__bridge STKAudioPlayer*)inRefCon; - pthread_mutex_lock(&audioPlayer->pcmBuffersMutex); + OSSpinLockLock(&audioPlayer->pcmBufferSpinLock); + AudioBuffer* audioBuffer = audioPlayer->pcmAudioBuffer; + UInt32 frameSizeInBytes = audioPlayer->pcmBufferFrameSizeInBytes; + UInt32 used = audioPlayer->pcmBufferUsedFrameCount; + UInt32 start = audioPlayer->pcmBufferFrameStartIndex; + UInt32 end = (audioPlayer->pcmBufferFrameStartIndex + audioPlayer->pcmBufferUsedFrameCount) % audioPlayer->pcmBufferTotalFrameCount; + BOOL signal = audioPlayer->waiting && used < audioPlayer->pcmBufferTotalFrameCount / 2; - pthread_mutex_unlock(&audioPlayer->pcmBuffersMutex); + OSSpinLockUnlock(&audioPlayer->pcmBufferSpinLock); + + assert(audioPlayer->pcmBufferUsedFrameCount <= audioPlayer->pcmBufferTotalFrameCount); + + if (used > 0) + { + UInt32 totalFramesCopied = 0; + + if (end > start) + { + UInt32 framesToCopy = MIN(inNumberFrames, used); + + ioData->mBuffers[0].mNumberChannels = 2; + ioData->mBuffers[0].mDataByteSize = frameSizeInBytes * framesToCopy; + memcpy(ioData->mBuffers[0].mData, audioBuffer->mData + (start * frameSizeInBytes), ioData->mBuffers[0].mDataByteSize); + + totalFramesCopied = framesToCopy; + + OSSpinLockLock(&audioPlayer->pcmBufferSpinLock); + audioPlayer->pcmBufferFrameStartIndex = (audioPlayer->pcmBufferFrameStartIndex + totalFramesCopied) % audioPlayer->pcmBufferTotalFrameCount; + audioPlayer->pcmBufferUsedFrameCount -= totalFramesCopied; + OSSpinLockUnlock(&audioPlayer->pcmBufferSpinLock); + } + else + { + UInt32 framesToCopy = MIN(inNumberFrames, audioPlayer->pcmBufferTotalFrameCount - start); + + ioData->mBuffers[0].mNumberChannels = 2; + ioData->mBuffers[0].mDataByteSize = frameSizeInBytes * framesToCopy; + memcpy(ioData->mBuffers[0].mData, audioBuffer->mData + (start * frameSizeInBytes), ioData->mBuffers[0].mDataByteSize); + + UInt32 moreFramesToCopy = 0; + UInt32 delta = inNumberFrames - framesToCopy; + + if (delta > 0) + { + moreFramesToCopy = MIN(delta, end); + + ioData->mBuffers[0].mNumberChannels = 2; + ioData->mBuffers[0].mDataByteSize += frameSizeInBytes * moreFramesToCopy; + memcpy(ioData->mBuffers[0].mData + (framesToCopy * frameSizeInBytes), audioBuffer->mData, frameSizeInBytes * moreFramesToCopy); + } + + totalFramesCopied = framesToCopy + moreFramesToCopy; + + OSSpinLockLock(&audioPlayer->pcmBufferSpinLock); + audioPlayer->pcmBufferFrameStartIndex = (audioPlayer->pcmBufferFrameStartIndex + totalFramesCopied) % audioPlayer->pcmBufferTotalFrameCount; + audioPlayer->pcmBufferUsedFrameCount -= totalFramesCopied; + OSSpinLockUnlock(&audioPlayer->pcmBufferSpinLock); + } + + if (totalFramesCopied < inNumberFrames) + { + UInt32 delta = inNumberFrames - totalFramesCopied; + + memset(ioData->mBuffers[0].mData + (totalFramesCopied * frameSizeInBytes), 1, delta * frameSizeInBytes); + } + } + + if (signal) + { + pthread_mutex_lock(&audioPlayer->pcmBuffersMutex); + pthread_cond_signal(&audioPlayer->pcmBuffersReadyCondition); + pthread_mutex_unlock(&audioPlayer->pcmBuffersMutex); + } return 0; } From b0466171f6f72627864ed1749e114f16a1eb7b65 Mon Sep 17 00:00:00 2001 From: Thong Nguyen Date: Wed, 29 Jan 2014 22:22:23 +0000 Subject: [PATCH 05/33] Removed AudioUnit kAudioUnitScope_Output init code --- StreamingKit/StreamingKit/STKAudioPlayer.m | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index 215b38a..29ca6b9 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -3270,11 +3270,9 @@ AudioBufferList* AllocateAudioBufferList(UInt32 bytesPerFrame, UInt32 capacityFr status = AudioComponentInstanceNew(component, &audioUnit); UInt32 flag = 1; - + status = AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &flag, sizeof(flag)); - status = AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, kOutputBus, &flag, sizeof(flag)); status = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &canonicalAudioStreamBasicDescription, sizeof(canonicalAudioStreamBasicDescription)); - status = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, kInputBus, &canonicalAudioStreamBasicDescription, sizeof(canonicalAudioStreamBasicDescription)); AudioClassDescription classDesc; From 8d61f3a3e3691959882723f8f7f3328aac3122ea Mon Sep 17 00:00:00 2001 From: Thong Nguyen Date: Wed, 29 Jan 2014 22:25:25 +0000 Subject: [PATCH 06/33] Removed AudioUnit kAudioUnitScope_Output init code --- StreamingKit/StreamingKit/STKAudioPlayer.m | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index 29ca6b9..b9ddefd 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -3228,27 +3228,6 @@ BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* classDesc return NO; } -AudioBufferList* AllocateAudioBufferList(UInt32 bytesPerFrame, UInt32 capacityFrames, UInt32 channelsPerFrame, bool interleaved) -{ - AudioBufferList *bufferList = 0; - - UInt32 numBuffers = interleaved ? 1 : channelsPerFrame; - UInt32 channelsPerBuffer = interleaved ? channelsPerFrame : 1; - - bufferList = (AudioBufferList *)(calloc(1, offsetof(AudioBufferList, mBuffers) + (sizeof(AudioBuffer) * numBuffers))); - - bufferList->mNumberBuffers = numBuffers; - - for(UInt32 bufferIndex = 0; bufferIndex < bufferList->mNumberBuffers; ++bufferIndex) - { - bufferList->mBuffers[bufferIndex].mData = (void*)(calloc(capacityFrames, bytesPerFrame)); - bufferList->mBuffers[bufferIndex].mDataByteSize = capacityFrames * bytesPerFrame; - bufferList->mBuffers[bufferIndex].mNumberChannels = channelsPerBuffer; - } - - return bufferList; -} - -(void) createAudioUnit { pthread_mutex_lock(&playerMutex); From b8528e30c5d50083b63369ad092c5b7df7128b8c Mon Sep 17 00:00:00 2001 From: Thong Nguyen Date: Wed, 29 Jan 2014 22:30:52 +0000 Subject: [PATCH 07/33] Added remote control event handling to AppDelegate so that example app will show play icon in status bar --- ExampleApp/ExampleApp/AppDelegate.m | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ExampleApp/ExampleApp/AppDelegate.m b/ExampleApp/ExampleApp/AppDelegate.m index 4fe456a..bbc64bf 100644 --- a/ExampleApp/ExampleApp/AppDelegate.m +++ b/ExampleApp/ExampleApp/AppDelegate.m @@ -37,6 +37,9 @@ audioPlayerView.delegate = self; audioPlayerView.audioPlayer = audioPlayer; + + [[UIApplication sharedApplication] beginReceivingRemoteControlEvents]; + [self becomeFirstResponder]; [self.window addSubview:audioPlayerView]; @@ -45,6 +48,11 @@ return YES; } +-(BOOL) canBecomeFirstResponder +{ + return YES; +} + -(void) audioPlayerViewPlayFromHTTPSelected:(AudioPlayerView*)audioPlayerView { NSURL* url = [NSURL URLWithString:@"http://fs.bloom.fm/oss/audiosamples/sample.mp3"]; From cdb6f27a534e98c0e5fbf7fd55ad41c1fa64769c Mon Sep 17 00:00:00 2001 From: Thong Nguyen Date: Wed, 29 Jan 2014 22:40:34 +0000 Subject: [PATCH 08/33] Disabled input IO when creating Audio Unit --- StreamingKit/StreamingKit/STKAudioPlayer.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index b9ddefd..e5ec394 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -3250,7 +3250,7 @@ BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* classDesc UInt32 flag = 1; - status = AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, kInputBus, &flag, sizeof(flag)); + status = AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, kOutputBus, &flag, sizeof(flag)); status = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &canonicalAudioStreamBasicDescription, sizeof(canonicalAudioStreamBasicDescription)); AudioClassDescription classDesc; From 6df11418f70ddeccf0e74610d00cf8736893039d Mon Sep 17 00:00:00 2001 From: Thong Nguyen Date: Thu, 30 Jan 2014 12:46:26 +0000 Subject: [PATCH 09/33] Started removing legacy AudioQueue code --- .../StreamingKit.xcodeproj/project.pbxproj | 6 + .../NSMutableArray+STKAudioPlayer.h | 13 ++ .../NSMutableArray+STKAudioPlayer.m | 13 ++ StreamingKit/StreamingKit/STKAudioPlayer.h | 5 +- StreamingKit/StreamingKit/STKAudioPlayer.m | 194 ++++++------------ StreamingKit/StreamingKit/STKQueueEntry.h | 13 ++ StreamingKit/StreamingKit/STKQueueEntry.m | 13 ++ 7 files changed, 125 insertions(+), 132 deletions(-) create mode 100644 StreamingKit/StreamingKit/NSMutableArray+STKAudioPlayer.h create mode 100644 StreamingKit/StreamingKit/NSMutableArray+STKAudioPlayer.m create mode 100644 StreamingKit/StreamingKit/STKQueueEntry.h create mode 100644 StreamingKit/StreamingKit/STKQueueEntry.m diff --git a/StreamingKit/StreamingKit.xcodeproj/project.pbxproj b/StreamingKit/StreamingKit.xcodeproj/project.pbxproj index f468f37..a22c99b 100644 --- a/StreamingKit/StreamingKit.xcodeproj/project.pbxproj +++ b/StreamingKit/StreamingKit.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + A1BF65D2189A6582004DD08C /* STKQueueEntry.m in Sources */ = {isa = PBXBuildFile; fileRef = A1BF65D1189A6582004DD08C /* STKQueueEntry.m */; }; A1C9767718981BFE0057F881 /* AudioUnit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1C9767618981BFE0057F881 /* AudioUnit.framework */; }; A1E7C4CC188D57F50010896F /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1E7C4CB188D57F50010896F /* Foundation.framework */; }; A1E7C4DA188D57F60010896F /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1E7C4D9188D57F60010896F /* XCTest.framework */; }; @@ -47,6 +48,8 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + A1BF65D0189A6582004DD08C /* STKQueueEntry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKQueueEntry.h; sourceTree = ""; }; + A1BF65D1189A6582004DD08C /* STKQueueEntry.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKQueueEntry.m; sourceTree = ""; }; A1C9767618981BFE0057F881 /* AudioUnit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioUnit.framework; path = System/Library/Frameworks/AudioUnit.framework; sourceTree = SDKROOT; }; A1E7C4C8188D57F50010896F /* libStreamingKit.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libStreamingKit.a; sourceTree = BUILT_PRODUCTS_DIR; }; A1E7C4CB188D57F50010896F /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; @@ -145,6 +148,8 @@ A1E7C4FD188D5E550010896F /* STKLocalFileDataSource.h */, A1E7C4FE188D5E550010896F /* STKLocalFileDataSource.m */, A1E7C4CE188D57F50010896F /* Supporting Files */, + A1BF65D0189A6582004DD08C /* STKQueueEntry.h */, + A1BF65D1189A6582004DD08C /* STKQueueEntry.m */, ); path = StreamingKit; sourceTree = ""; @@ -260,6 +265,7 @@ A1E7C501188D5E550010896F /* STKCoreFoundationDataSource.m in Sources */, A1E7C4FF188D5E550010896F /* STKAudioPlayer.m in Sources */, A1E7C505188D5E550010896F /* STKLocalFileDataSource.m in Sources */, + A1BF65D2189A6582004DD08C /* STKQueueEntry.m in Sources */, A1E7C504188D5E550010896F /* STKHTTPDataSource.m in Sources */, A1E7C503188D5E550010896F /* STKDataSourceWrapper.m in Sources */, A1E7C502188D5E550010896F /* STKDataSource.m in Sources */, diff --git a/StreamingKit/StreamingKit/NSMutableArray+STKAudioPlayer.h b/StreamingKit/StreamingKit/NSMutableArray+STKAudioPlayer.h new file mode 100644 index 0000000..25cfac2 --- /dev/null +++ b/StreamingKit/StreamingKit/NSMutableArray+STKAudioPlayer.h @@ -0,0 +1,13 @@ +// +// NSMutableArray+STKAudioPlayer.h +// StreamingKit +// +// Created by Thong Nguyen on 30/01/2014. +// Copyright (c) 2014 Thong Nguyen. All rights reserved. +// + +#import + +@interface NSMutableArray (STKAudioPlayer) + +@end diff --git a/StreamingKit/StreamingKit/NSMutableArray+STKAudioPlayer.m b/StreamingKit/StreamingKit/NSMutableArray+STKAudioPlayer.m new file mode 100644 index 0000000..3339669 --- /dev/null +++ b/StreamingKit/StreamingKit/NSMutableArray+STKAudioPlayer.m @@ -0,0 +1,13 @@ +// +// NSMutableArray+STKAudioPlayer.m +// StreamingKit +// +// Created by Thong Nguyen on 30/01/2014. +// Copyright (c) 2014 Thong Nguyen. All rights reserved. +// + +#import "NSMutableArray+STKAudioPlayer.h" + +@implementation NSMutableArray (STKAudioPlayer) + +@end diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.h b/StreamingKit/StreamingKit/STKAudioPlayer.h index 43761b5..1032541 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.h +++ b/StreamingKit/StreamingKit/STKAudioPlayer.h @@ -2,7 +2,7 @@ AudioPlayer.m Created by Thong Nguyen on 14/05/2012. - https://github.com/tumtumtum/audjustable + https://github.com/tumtumtum/StreamingKit Inspired by Matt Gallagher's AudioStreamer: https://github.com/mattgallagher/AudioStreamer @@ -143,8 +143,5 @@ STKAudioPlayerErrorCode; -(void) unmute; -(void) dispose; -(NSObject*) currentlyPlayingQueueItemId; --(void) updateMeters; --(float) peakPowerInDecibelsForChannel:(NSUInteger)channelNumber; --(float) averagePowerInDecibelsForChannel:(NSUInteger)channelNumber; @end diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index e5ec394..d2006d9 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -2,12 +2,9 @@ AudioPlayer.m Created by Thong Nguyen on 14/05/2012. - https://github.com/tumtumtum/audjustable + https://github.com/tumtumtum/StreamingKit - Inspired by Matt Gallagher's AudioStreamer: - https://github.com/mattgallagher/AudioStreamer - - Copyright (c) 2012 Thong Nguyen (tumtumtum@gmail.com). All rights reserved. + Copyright (c) 2014 Thong Nguyen (tumtumtum@gmail.com). All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -41,7 +38,8 @@ #import "STKLocalFileDataSource.h" #import "libkern/OSAtomic.h" -#define STK_DEFAULT_PCM_BUFFER_SIZE_IN_SECONDS (2) +#define STK_DEFAULT_PCM_BUFFER_SIZE_IN_SECONDS (5) +#define STK_DEFAULT_SECONDS_REQUIRED_TO_START_PLAYING (0.75) #define STK_BIT_RATE_ESTIMATION_MIN_PACKETS (64) #define STK_BUFFERS_NEEDED_TO_START (32) @@ -51,8 +49,6 @@ #define STK_FRAMES_MISSED_BEFORE_CONSIDERED_UNDERRUN (1024) #define STK_DEFAULT_NUMBER_OF_AUDIOQUEUE_BUFFERS (1024) -#define OSSTATUS_PARAM_ERROR (-50) - #define LOGINFO(x) [self logInfo:[NSString stringWithFormat:@"%s %@", sel_getName(_cmd), x]]; typedef struct @@ -301,6 +297,7 @@ AudioQueueBufferRefLookupEntry; bool* bufferUsed; OSSpinLock pcmBufferSpinLock; + volatile BOOL buffering; volatile UInt32 pcmBufferFrameSizeInBytes; volatile UInt32 pcmBufferTotalFrameCount; volatile UInt32 pcmBufferFrameStartIndex; @@ -3117,79 +3114,6 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ return retval; } -#pragma mark Metering - --(void) setMeteringEnabled:(BOOL)value -{ - if (!audioQueue) - { - meteringEnabled = value; - - return; - } - - UInt32 on = value ? 1 : 0; - OSStatus error = AudioQueueSetProperty(audioQueue, kAudioQueueProperty_EnableLevelMetering, &on, sizeof(on)); - - if (error) - { - meteringEnabled = NO; - } - else - { - meteringEnabled = YES; - } -} - --(BOOL) meteringEnabled -{ - return meteringEnabled; -} - --(void) updateMeters -{ - if (!meteringEnabled) - { - NSAssert(NO, @"Metering is not enabled. Make sure to set meteringEnabled = YES."); - } - - UInt32 channels = currentAudioStreamBasicDescription.mChannelsPerFrame; - - if (numberOfChannels != channels) - { - numberOfChannels = channels; - - if (levelMeterState) free(levelMeterState); - { - levelMeterState = malloc(sizeof(AudioQueueLevelMeterState) * numberOfChannels); - } - } - - UInt32 sizeofMeters = (UInt32)(sizeof(AudioQueueLevelMeterState) * numberOfChannels); - - AudioQueueGetProperty(audioQueue, kAudioQueueProperty_CurrentLevelMeterDB, levelMeterState, &sizeofMeters); -} - --(float) peakPowerInDecibelsForChannel:(NSUInteger)channelNumber -{ - if (!meteringEnabled || !levelMeterState || (channelNumber > numberOfChannels)) - { - return 0; - } - - return levelMeterState[channelNumber].mPeakPower; -} - --(float) averagePowerInDecibelsForChannel:(NSUInteger)channelNumber -{ - if (!meteringEnabled || !levelMeterState || (channelNumber > numberOfChannels)) - { - return 0; - } - - return levelMeterState[channelNumber].mAveragePower; -} - /// /// AudioUnit @@ -3571,64 +3495,78 @@ static OSStatus playbackCallback(void* inRefCon, AudioUnitRenderActionFlags* ioA UInt32 start = audioPlayer->pcmBufferFrameStartIndex; UInt32 end = (audioPlayer->pcmBufferFrameStartIndex + audioPlayer->pcmBufferUsedFrameCount) % audioPlayer->pcmBufferTotalFrameCount; BOOL signal = audioPlayer->waiting && used < audioPlayer->pcmBufferTotalFrameCount / 2; - OSSpinLockUnlock(&audioPlayer->pcmBufferSpinLock); - - assert(audioPlayer->pcmBufferUsedFrameCount <= audioPlayer->pcmBufferTotalFrameCount); + + UInt32 totalFramesCopied = 0; if (used > 0) { - UInt32 totalFramesCopied = 0; - - if (end > start) + if (!(audioPlayer->buffering && used < audioPlayer->pcmBufferTotalFrameCount)) { - UInt32 framesToCopy = MIN(inNumberFrames, used); - - ioData->mBuffers[0].mNumberChannels = 2; - ioData->mBuffers[0].mDataByteSize = frameSizeInBytes * framesToCopy; - memcpy(ioData->mBuffers[0].mData, audioBuffer->mData + (start * frameSizeInBytes), ioData->mBuffers[0].mDataByteSize); - - totalFramesCopied = framesToCopy; - - OSSpinLockLock(&audioPlayer->pcmBufferSpinLock); - audioPlayer->pcmBufferFrameStartIndex = (audioPlayer->pcmBufferFrameStartIndex + totalFramesCopied) % audioPlayer->pcmBufferTotalFrameCount; - audioPlayer->pcmBufferUsedFrameCount -= totalFramesCopied; - OSSpinLockUnlock(&audioPlayer->pcmBufferSpinLock); - } - else - { - UInt32 framesToCopy = MIN(inNumberFrames, audioPlayer->pcmBufferTotalFrameCount - start); - - ioData->mBuffers[0].mNumberChannels = 2; - ioData->mBuffers[0].mDataByteSize = frameSizeInBytes * framesToCopy; - memcpy(ioData->mBuffers[0].mData, audioBuffer->mData + (start * frameSizeInBytes), ioData->mBuffers[0].mDataByteSize); - - UInt32 moreFramesToCopy = 0; - UInt32 delta = inNumberFrames - framesToCopy; - - if (delta > 0) + if (audioPlayer->buffering) { - moreFramesToCopy = MIN(delta, end); + NSLog(@"Buffering resuming"); - ioData->mBuffers[0].mNumberChannels = 2; - ioData->mBuffers[0].mDataByteSize += frameSizeInBytes * moreFramesToCopy; - memcpy(ioData->mBuffers[0].mData + (framesToCopy * frameSizeInBytes), audioBuffer->mData, frameSizeInBytes * moreFramesToCopy); + audioPlayer->buffering = NO; } - totalFramesCopied = framesToCopy + moreFramesToCopy; - - OSSpinLockLock(&audioPlayer->pcmBufferSpinLock); - audioPlayer->pcmBufferFrameStartIndex = (audioPlayer->pcmBufferFrameStartIndex + totalFramesCopied) % audioPlayer->pcmBufferTotalFrameCount; - audioPlayer->pcmBufferUsedFrameCount -= totalFramesCopied; - OSSpinLockUnlock(&audioPlayer->pcmBufferSpinLock); + if (end > start) + { + UInt32 framesToCopy = MIN(inNumberFrames, used); + + ioData->mBuffers[0].mNumberChannels = 2; + ioData->mBuffers[0].mDataByteSize = frameSizeInBytes * framesToCopy; + memcpy(ioData->mBuffers[0].mData, audioBuffer->mData + (start * frameSizeInBytes), ioData->mBuffers[0].mDataByteSize); + + totalFramesCopied = framesToCopy; + + OSSpinLockLock(&audioPlayer->pcmBufferSpinLock); + audioPlayer->pcmBufferFrameStartIndex = (audioPlayer->pcmBufferFrameStartIndex + totalFramesCopied) % audioPlayer->pcmBufferTotalFrameCount; + audioPlayer->pcmBufferUsedFrameCount -= totalFramesCopied; + OSSpinLockUnlock(&audioPlayer->pcmBufferSpinLock); + } + else + { + UInt32 framesToCopy = MIN(inNumberFrames, audioPlayer->pcmBufferTotalFrameCount - start); + + ioData->mBuffers[0].mNumberChannels = 2; + ioData->mBuffers[0].mDataByteSize = frameSizeInBytes * framesToCopy; + memcpy(ioData->mBuffers[0].mData, audioBuffer->mData + (start * frameSizeInBytes), ioData->mBuffers[0].mDataByteSize); + + UInt32 moreFramesToCopy = 0; + UInt32 delta = inNumberFrames - framesToCopy; + + if (delta > 0) + { + moreFramesToCopy = MIN(delta, end); + + ioData->mBuffers[0].mNumberChannels = 2; + ioData->mBuffers[0].mDataByteSize += frameSizeInBytes * moreFramesToCopy; + memcpy(ioData->mBuffers[0].mData + (framesToCopy * frameSizeInBytes), audioBuffer->mData, frameSizeInBytes * moreFramesToCopy); + } + + totalFramesCopied = framesToCopy + moreFramesToCopy; + + OSSpinLockLock(&audioPlayer->pcmBufferSpinLock); + audioPlayer->pcmBufferFrameStartIndex = (audioPlayer->pcmBufferFrameStartIndex + totalFramesCopied) % audioPlayer->pcmBufferTotalFrameCount; + audioPlayer->pcmBufferUsedFrameCount -= totalFramesCopied; + OSSpinLockUnlock(&audioPlayer->pcmBufferSpinLock); + } + } + } + + if (totalFramesCopied < inNumberFrames) + { + UInt32 delta = inNumberFrames - totalFramesCopied; + + memset(ioData->mBuffers[0].mData + (totalFramesCopied * frameSizeInBytes), 0, delta * frameSizeInBytes); + + if (!audioPlayer->buffering) + { + NSLog(@"Buffering"); } - if (totalFramesCopied < inNumberFrames) - { - UInt32 delta = inNumberFrames - totalFramesCopied; - - memset(ioData->mBuffers[0].mData + (totalFramesCopied * frameSizeInBytes), 1, delta * frameSizeInBytes); - } + audioPlayer->buffering = YES; } if (signal) diff --git a/StreamingKit/StreamingKit/STKQueueEntry.h b/StreamingKit/StreamingKit/STKQueueEntry.h new file mode 100644 index 0000000..8fd8313 --- /dev/null +++ b/StreamingKit/StreamingKit/STKQueueEntry.h @@ -0,0 +1,13 @@ +// +// STKQueueEntry.h +// StreamingKit +// +// Created by Thong Nguyen on 30/01/2014. +// Copyright (c) 2014 Thong Nguyen. All rights reserved. +// + +#import + +@interface STKQueueEntry : NSobject + +@end diff --git a/StreamingKit/StreamingKit/STKQueueEntry.m b/StreamingKit/StreamingKit/STKQueueEntry.m new file mode 100644 index 0000000..842fb1c --- /dev/null +++ b/StreamingKit/StreamingKit/STKQueueEntry.m @@ -0,0 +1,13 @@ +// +// STKQueueEntry.m +// StreamingKit +// +// Created by Thong Nguyen on 30/01/2014. +// Copyright (c) 2014 Thong Nguyen. All rights reserved. +// + +#import "STKQueueEntry.h" + +@implementation STKQueueEntry + +@end From 15b18069e3c995f5dbedabb5e0804b6bbe49f3e7 Mon Sep 17 00:00:00 2001 From: Thong Nguyen Date: Thu, 30 Jan 2014 12:46:41 +0000 Subject: [PATCH 10/33] Started removing legacy AudioQueue code --- .../StreamingKit.xcodeproj/project.pbxproj | 8 +- .../NSMutableArray+STKAudioPlayer.h | 7 +- .../NSMutableArray+STKAudioPlayer.m | 41 +- StreamingKit/StreamingKit/STKAudioPlayer.h | 1 - StreamingKit/StreamingKit/STKAudioPlayer.m | 1541 ++--------------- StreamingKit/StreamingKit/STKQueueEntry.h | 41 +- StreamingKit/StreamingKit/STKQueueEntry.m | 143 +- 7 files changed, 338 insertions(+), 1444 deletions(-) diff --git a/StreamingKit/StreamingKit.xcodeproj/project.pbxproj b/StreamingKit/StreamingKit.xcodeproj/project.pbxproj index a22c99b..0fabf5d 100644 --- a/StreamingKit/StreamingKit.xcodeproj/project.pbxproj +++ b/StreamingKit/StreamingKit.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ A1BF65D2189A6582004DD08C /* STKQueueEntry.m in Sources */ = {isa = PBXBuildFile; fileRef = A1BF65D1189A6582004DD08C /* STKQueueEntry.m */; }; + A1BF65D5189A65C6004DD08C /* NSMutableArray+STKAudioPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = A1BF65D4189A65C6004DD08C /* NSMutableArray+STKAudioPlayer.m */; }; A1C9767718981BFE0057F881 /* AudioUnit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1C9767618981BFE0057F881 /* AudioUnit.framework */; }; A1E7C4CC188D57F50010896F /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1E7C4CB188D57F50010896F /* Foundation.framework */; }; A1E7C4DA188D57F60010896F /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1E7C4D9188D57F60010896F /* XCTest.framework */; }; @@ -50,6 +51,8 @@ /* Begin PBXFileReference section */ A1BF65D0189A6582004DD08C /* STKQueueEntry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKQueueEntry.h; sourceTree = ""; }; A1BF65D1189A6582004DD08C /* STKQueueEntry.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKQueueEntry.m; sourceTree = ""; }; + A1BF65D3189A65C6004DD08C /* NSMutableArray+STKAudioPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSMutableArray+STKAudioPlayer.h"; sourceTree = ""; }; + A1BF65D4189A65C6004DD08C /* NSMutableArray+STKAudioPlayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSMutableArray+STKAudioPlayer.m"; sourceTree = ""; }; A1C9767618981BFE0057F881 /* AudioUnit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioUnit.framework; path = System/Library/Frameworks/AudioUnit.framework; sourceTree = SDKROOT; }; A1E7C4C8188D57F50010896F /* libStreamingKit.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libStreamingKit.a; sourceTree = BUILT_PRODUCTS_DIR; }; A1E7C4CB188D57F50010896F /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; @@ -147,9 +150,11 @@ A1E7C4FC188D5E550010896F /* STKHTTPDataSource.m */, A1E7C4FD188D5E550010896F /* STKLocalFileDataSource.h */, A1E7C4FE188D5E550010896F /* STKLocalFileDataSource.m */, - A1E7C4CE188D57F50010896F /* Supporting Files */, A1BF65D0189A6582004DD08C /* STKQueueEntry.h */, A1BF65D1189A6582004DD08C /* STKQueueEntry.m */, + A1BF65D3189A65C6004DD08C /* NSMutableArray+STKAudioPlayer.h */, + A1BF65D4189A65C6004DD08C /* NSMutableArray+STKAudioPlayer.m */, + A1E7C4CE188D57F50010896F /* Supporting Files */, ); path = StreamingKit; sourceTree = ""; @@ -269,6 +274,7 @@ A1E7C504188D5E550010896F /* STKHTTPDataSource.m in Sources */, A1E7C503188D5E550010896F /* STKDataSourceWrapper.m in Sources */, A1E7C502188D5E550010896F /* STKDataSource.m in Sources */, + A1BF65D5189A65C6004DD08C /* NSMutableArray+STKAudioPlayer.m in Sources */, A1E7C500188D5E550010896F /* STKAutoRecoveringHTTPDataSource.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/StreamingKit/StreamingKit/NSMutableArray+STKAudioPlayer.h b/StreamingKit/StreamingKit/NSMutableArray+STKAudioPlayer.h index 25cfac2..4d6bd51 100644 --- a/StreamingKit/StreamingKit/NSMutableArray+STKAudioPlayer.h +++ b/StreamingKit/StreamingKit/NSMutableArray+STKAudioPlayer.h @@ -8,6 +8,9 @@ #import -@interface NSMutableArray (STKAudioPlayer) - +@interface NSMutableArray(STKAudioPlayer) +-(void) enqueue:(id)obj; +-(void) skipQueue:(id)obj; +-(id) dequeue; +-(id) peek; @end diff --git a/StreamingKit/StreamingKit/NSMutableArray+STKAudioPlayer.m b/StreamingKit/StreamingKit/NSMutableArray+STKAudioPlayer.m index 3339669..687f91f 100644 --- a/StreamingKit/StreamingKit/NSMutableArray+STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/NSMutableArray+STKAudioPlayer.m @@ -8,6 +8,45 @@ #import "NSMutableArray+STKAudioPlayer.h" -@implementation NSMutableArray (STKAudioPlayer) +@implementation NSMutableArray(STKAudioPlayer) + +-(void) enqueue:(id)obj +{ + [self insertObject:obj atIndex:0]; +} + +-(void) skipQueue:(id)obj +{ + [self addObject:obj]; +} + +-(id) dequeue +{ + if ([self count] == 0) + { + return nil; + } + + id retval = [self lastObject]; + + [self removeLastObject]; + + return retval; +} + +-(id) peek +{ + return [self lastObject]; +} + +-(id) peekRecent +{ + if (self.count == 0) + { + return nil; + } + + return [self objectAtIndex:0]; +} @end diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.h b/StreamingKit/StreamingKit/STKAudioPlayer.h index 1032541..c5919c0 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.h +++ b/StreamingKit/StreamingKit/STKAudioPlayer.h @@ -123,7 +123,6 @@ STKAudioPlayerErrorCode; @property (readwrite) STKAudioPlayerState state; @property (readonly) STKAudioPlayerStopReason stopReason; @property (readwrite, unsafe_unretained) id delegate; -@property (readwrite) BOOL meteringEnabled; -(id) init; -(id) initWithNumberOfAudioQueueBuffers:(int)numberOfAudioQueueBuffers andReadBufferSize:(int)readBufferSizeIn; diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index d2006d9..f1cbb8b 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -36,251 +36,22 @@ #import "AudioToolbox/AudioToolbox.h" #import "STKHTTPDataSource.h" #import "STKLocalFileDataSource.h" +#import "STKQueueEntry.h" +#import "NSMutableArray+STKAudioPlayer.h" #import "libkern/OSAtomic.h" #define STK_DEFAULT_PCM_BUFFER_SIZE_IN_SECONDS (5) #define STK_DEFAULT_SECONDS_REQUIRED_TO_START_PLAYING (0.75) -#define STK_BIT_RATE_ESTIMATION_MIN_PACKETS (64) #define STK_BUFFERS_NEEDED_TO_START (32) #define STK_BUFFERS_NEEDED_WHEN_UNDERUNNING (128) #define STK_DEFAULT_READ_BUFFER_SIZE (64 * 1024) #define STK_DEFAULT_PACKET_BUFFER_SIZE (2048) #define STK_FRAMES_MISSED_BEFORE_CONSIDERED_UNDERRUN (1024) -#define STK_DEFAULT_NUMBER_OF_AUDIOQUEUE_BUFFERS (1024) + #define LOGINFO(x) [self logInfo:[NSString stringWithFormat:@"%s %@", sel_getName(_cmd), x]]; -typedef struct -{ - AudioQueueBufferRef ref; - int bufferIndex; -} -AudioQueueBufferRefLookupEntry; - -@interface NSMutableArray(AudioPlayerExtensions) --(void) enqueue:(id)obj; --(id) dequeue; --(id) peek; -@end - -@implementation NSMutableArray(AudioPlayerExtensions) - --(void) enqueue:(id)obj -{ - [self insertObject:obj atIndex:0]; -} - --(void) skipQueue:(id)obj -{ - [self addObject:obj]; -} - --(id) dequeue -{ - if ([self count] == 0) - { - return nil; - } - - id retval = [self lastObject]; - - [self removeLastObject]; - - return retval; -} - --(id) peek -{ - return [self lastObject]; -} - --(id) peekRecent -{ - if (self.count == 0) - { - return nil; - } - - return [self objectAtIndex:0]; -} - -@end - -@interface STKQueueEntry : NSObject -{ -@public - BOOL parsedHeader; - double sampleRate; - double lastProgress; - double packetDuration; - UInt64 audioDataOffset; - UInt64 audioDataByteCount; - UInt32 packetBufferSize; - volatile BOOL cancel; - volatile int processedPacketsCount; - volatile int processedPacketsSizeTotal; - AudioStreamBasicDescription audioStreamBasicDescription; -} -@property (readwrite, retain) NSObject* queueItemId; -@property (readwrite, retain) STKDataSource* dataSource; -@property (readwrite) Float64 seekTime; -@property (readwrite) int bytesBuffered; -@property (readwrite) int lastByteIndex; -@property (readwrite) Float64 lastFrameIndex; -@property (readwrite) Float64 timeWhenLastBufferReturned; -@property (readwrite) Float64 firstFrameIndex; -@property (readonly) UInt64 audioDataLengthInBytes; - --(double) duration; --(double) calculatedBitRate; - --(id) initWithDataSource:(STKDataSource*)dataSource andQueueItemId:(NSObject*)queueItemId; - -@end - -@implementation STKQueueEntry - --(id) initWithDataSource:(STKDataSource*)dataSourceIn andQueueItemId:(NSObject*)queueItemIdIn -{ - if (self = [super init]) - { - self.dataSource = dataSourceIn; - self.queueItemId = queueItemIdIn; - self.lastFrameIndex = -1; - self.lastByteIndex = -1; - } - - return self; -} - --(double) calculatedBitRate -{ - double retval; - - if (packetDuration && processedPacketsCount > STK_BIT_RATE_ESTIMATION_MIN_PACKETS) - { - double averagePacketByteSize = processedPacketsSizeTotal / processedPacketsCount; - - retval = averagePacketByteSize / packetDuration * 8; - - return retval; - } - - retval = (audioStreamBasicDescription.mBytesPerFrame * audioStreamBasicDescription.mSampleRate) * 8; - - return retval; -} - --(void) updateAudioDataSource -{ - if ([self.dataSource conformsToProtocol:@protocol(AudioDataSource)]) - { - double calculatedBitrate = [self calculatedBitRate]; - - id audioDataSource = (id)self.dataSource; - - audioDataSource.averageBitRate = calculatedBitrate; - audioDataSource.audioDataOffset = audioDataOffset; - } -} - --(Float64) calculateProgressWithTotalFramesPlayed:(Float64)framesPlayed -{ - return (Float64)self.seekTime + ((framesPlayed - self.firstFrameIndex) / (Float64)self->audioStreamBasicDescription.mSampleRate); -} - --(double) calculateProgressWithBytesPlayed:(Float64)bytesPlayed -{ - double retval = lastProgress; - - if (self->sampleRate > 0) - { - double calculatedBitrate = [self calculatedBitRate]; - - retval = bytesPlayed / calculatedBitrate * 8; - - retval = self.seekTime + retval; - - [self updateAudioDataSource]; - } - - return retval; -} - --(double) duration -{ - if (self->sampleRate <= 0) - { - return 0; - } - - UInt64 audioDataLengthInBytes = [self audioDataLengthInBytes]; - - double calculatedBitRate = [self calculatedBitRate]; - - if (calculatedBitRate < 1.0 || self.dataSource.length == 0) - { - return 0; - } - - return audioDataLengthInBytes / (calculatedBitRate / 8); -} - --(UInt64) audioDataLengthInBytes -{ - if (audioDataByteCount) - { - return audioDataByteCount; - } - else - { - if (!self.dataSource.length) - { - return 0; - } - - return self.dataSource.length - audioDataOffset; - } -} - --(BOOL) isDefinitelyCompatible:(AudioStreamBasicDescription*)basicDescription -{ - if (self->audioStreamBasicDescription.mSampleRate == 0) - { - return NO; - } - - return (memcmp(&(self->audioStreamBasicDescription), basicDescription, sizeof(*basicDescription)) == 0); -} - --(BOOL) isKnownToBeIncompatible:(AudioStreamBasicDescription*)basicDescription -{ - if (self->audioStreamBasicDescription.mSampleRate == 0) - { - return NO; - } - - return (memcmp(&(self->audioStreamBasicDescription), basicDescription, sizeof(*basicDescription)) != 0); -} - --(BOOL) couldBeIncompatible:(AudioStreamBasicDescription*)basicDescription -{ - if (self->audioStreamBasicDescription.mSampleRate == 0) - { - return YES; - } - - return memcmp(&(self->audioStreamBasicDescription), basicDescription, sizeof(*basicDescription)) != 0; -} - --(NSString*) description -{ - return [[self queueItemId] description]; -} - -@end - @interface STKAudioPlayer() { UInt8* readBuffer; @@ -294,27 +65,19 @@ AudioQueueBufferRefLookupEntry; NSMutableArray* upcomingQueue; NSMutableArray* bufferingQueue; - bool* bufferUsed; - - OSSpinLock pcmBufferSpinLock; volatile BOOL buffering; - volatile UInt32 pcmBufferFrameSizeInBytes; + OSSpinLock pcmBufferSpinLock; + int32_t rebufferingStartFrames; volatile UInt32 pcmBufferTotalFrameCount; volatile UInt32 pcmBufferFrameStartIndex; volatile UInt32 pcmBufferUsedFrameCount; + volatile UInt32 pcmBufferFrameSizeInBytes; AudioBuffer* pcmAudioBuffer; AudioBufferList pcmAudioBufferList; AudioConverterRef audioConverterRef; - AudioQueueBufferRef* audioQueueBuffer; - AudioQueueBufferRefLookupEntry* audioQueueBufferLookup; - unsigned int audioQueueBufferRefLookupCount; - unsigned int audioQueueBufferCount; - AudioStreamPacketDescription* packetDescs; - UInt32 numberOfBuffersUsed; - - AudioQueueRef audioQueue; + AudioStreamBasicDescription currentAudioStreamBasicDescription; AudioStreamBasicDescription canonicalAudioStreamBasicDescription; @@ -328,15 +91,6 @@ AudioQueueBufferRefLookupEntry; BOOL discontinuous; - int32_t bytesFilled; - int32_t packetsFilled; - int32_t framesFilled; - int32_t fillBufferIndex; - - volatile Float64 framesQueued; - volatile Float64 timelineAdjust; - volatile Float64 rebufferingStartFrames; - #if TARGET_OS_IPHONE UIBackgroundTaskIdentifier backgroundTaskId; #endif @@ -349,10 +103,7 @@ AudioQueueBufferRefLookupEntry; OSSpinLock currentEntryReferencesLock; pthread_mutex_t playerMutex; - pthread_mutex_t pcmBuffersMutex; - pthread_cond_t pcmBuffersReadyCondition; - pthread_mutex_t queueBuffersMutex; - pthread_cond_t queueBufferReadyCondition; + pthread_cond_t playerThreadReadyCondition; pthread_mutex_t mainThreadSyncCallMutex; pthread_cond_t mainThreadSyncCallReadyCondition; @@ -361,13 +112,6 @@ AudioQueueBufferRefLookupEntry; volatile BOOL seekToTimeWasRequested; volatile BOOL newFileToPlay; volatile double requestedSeekTime; - volatile BOOL audioQueueFlushing; - volatile SInt64 audioPacketsReadCount; - volatile SInt64 audioPacketsPlayedCount; - - BOOL meteringEnabled; - UInt32 numberOfChannels; - AudioQueueLevelMeterState* levelMeterState; } @property (readwrite) STKAudioPlayerInternalState internalState; @@ -388,8 +132,6 @@ AudioQueueBufferRefLookupEntry; -(void) processFinishPlayingIfAnyAndPlayingNext:(STKQueueEntry*)entry withNext:(STKQueueEntry*)next; -(void) handlePropertyChangeForFileStream:(AudioFileStreamID)audioFileStreamIn fileStreamPropertyID:(AudioFileStreamPropertyID)propertyID ioFlags:(UInt32*)ioFlags; -(void) handleAudioPackets:(const void*)inputData numberBytes:(UInt32)numberBytes numberPackets:(UInt32)numberPackets packetDescriptions:(AudioStreamPacketDescription*)packetDescriptions; --(void) handleAudioQueueOutput:(AudioQueueRef)audioQueue buffer:(AudioQueueBufferRef)buffer; --(void) handlePropertyChangeForQueue:(AudioQueueRef)audioQueue propertyID:(AudioQueuePropertyID)propertyID; @end static void AudioFileStreamPropertyListenerProc(void* clientData, AudioFileStreamID audioFileStream, AudioFileStreamPropertyID propertyId, UInt32* flags) @@ -406,20 +148,6 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn [player handleAudioPackets:inputData numberBytes:numberBytes numberPackets:numberPackets packetDescriptions:packetDescriptions]; } -static void AudioQueueOutputCallbackProc(void* clientData, AudioQueueRef audioQueue, AudioQueueBufferRef buffer) -{ - STKAudioPlayer* player = (__bridge STKAudioPlayer*)clientData; - - [player handleAudioQueueOutput:audioQueue buffer:buffer]; -} - -static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQueue, AudioQueuePropertyID propertyId) -{ - STKAudioPlayer* player = (__bridge STKAudioPlayer*)userData; - - [player handlePropertyChangeForQueue:audioQueue propertyID:propertyId]; -} - @implementation STKAudioPlayer @synthesize delegate, internalState, state; @@ -494,16 +222,6 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ return stopReason; } --(BOOL) audioQueueIsRunning -{ - UInt32 isRunning; - UInt32 isRunningSize = sizeof(isRunning); - - AudioQueueGetProperty(audioQueue, kAudioQueueProperty_IsRunning, &isRunning, &isRunningSize); - - return isRunning ? YES : NO; -} - -(void) logInfo:(NSString*)line { if ([NSThread currentThread].isMainThread) @@ -524,10 +242,10 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ -(id) init { - return [self initWithNumberOfAudioQueueBuffers:STK_DEFAULT_NUMBER_OF_AUDIOQUEUE_BUFFERS andReadBufferSize:STK_DEFAULT_READ_BUFFER_SIZE]; + return [self initWithReadBufferSize:STK_DEFAULT_READ_BUFFER_SIZE]; } --(id) initWithNumberOfAudioQueueBuffers:(int)numberOfAudioQueueBuffers andReadBufferSize:(int)readBufferSizeIn +-(id) initWithReadBufferSize:(int)readBufferSizeIn { if (self = [super init]) { @@ -552,29 +270,16 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ readBufferSize = readBufferSizeIn; readBuffer = calloc(sizeof(UInt8), readBufferSize); - audioQueueBufferCount = numberOfAudioQueueBuffers; - audioQueueBuffer = calloc(sizeof(AudioQueueBufferRef), audioQueueBufferCount); - - audioQueueBufferRefLookupCount = audioQueueBufferCount * 2; - audioQueueBufferLookup = calloc(sizeof(AudioQueueBufferRefLookupEntry), audioQueueBufferRefLookupCount); - - packetDescs = calloc(sizeof(AudioStreamPacketDescription), audioQueueBufferCount); - bufferUsed = calloc(sizeof(bool), audioQueueBufferCount); - pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&playerMutex, &attr); - pthread_mutex_init(&queueBuffersMutex, NULL); - pthread_mutex_init(&pcmBuffersMutex, NULL); - pthread_cond_init(&queueBufferReadyCondition, NULL); - pthread_cond_init(&pcmBuffersReadyCondition, NULL); - pthread_mutex_init(&mainThreadSyncCallMutex, NULL); + pthread_cond_init(&playerThreadReadyCondition, NULL); pthread_cond_init(&mainThreadSyncCallReadyCondition, NULL); - + threadStartedLock = [[NSConditionLock alloc] initWithCondition:0]; threadFinishedCondLock = [[NSConditionLock alloc] initWithCondition:0]; @@ -602,11 +307,8 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ } pthread_mutex_destroy(&playerMutex); - pthread_mutex_destroy(&queueBuffersMutex); - pthread_mutex_destroy(&pcmBuffersMutex); - pthread_cond_destroy(&queueBufferReadyCondition); - pthread_mutex_destroy(&mainThreadSyncCallMutex); + pthread_cond_destroy(&playerThreadReadyCondition); pthread_cond_destroy(&mainThreadSyncCallReadyCondition); @@ -615,17 +317,12 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ AudioFileStreamClose(audioFileStream); } - if (audioQueue) + if (audioUnit) { - AudioQueueDispose(audioQueue, true); + AudioComponentInstanceDispose(audioUnit); } - free(bufferUsed); free(readBuffer); - free(packetDescs); - free(audioQueueBuffer); - free(audioQueueBufferLookup); - free(levelMeterState); } -(void) startSystemBackgroundTask @@ -690,8 +387,6 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ { pthread_mutex_lock(&playerMutex); { - pthread_mutex_lock(&queueBuffersMutex); - NSMutableArray* array = [[NSMutableArray alloc] initWithCapacity:bufferingQueue.count + (includeUpcoming ? upcomingQueue.count : 0)]; STKQueueEntry* entry = [bufferingQueue dequeue]; @@ -731,8 +426,6 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ } }]; } - - pthread_mutex_unlock(&queueBuffersMutex); } pthread_mutex_unlock(&playerMutex); } @@ -767,12 +460,8 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ [self resetAudioQueueWithReason:@"from skipCurrent"]; [self clearQueue]; - pthread_mutex_lock(&queueBuffersMutex); - [upcomingQueue enqueue:[[STKQueueEntry alloc] initWithDataSource:dataSourceIn andQueueItemId:queueItemId]]; - pthread_mutex_unlock(&queueBuffersMutex); - self.internalState = STKAudioPlayerInternalStateRunning; newFileToPlay = YES; @@ -843,46 +532,6 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ AudioFileStreamGetProperty(inAudioFileStream, kAudioFileStreamProperty_DataFormat, &size, &newBasicDescription); pthread_mutex_lock(&playerMutex); - pthread_mutex_lock(&queueBuffersMutex); - - BOOL cancel = NO; - - AudioQueueSetParameter(audioQueue, kAudioQueueParam_Volume, 1); - - if (currentlyReadingEntry->cancel) - { - cancel = YES; - } - else - { - if (currentlyReadingEntry != currentlyPlayingEntry && audioQueue && currentAudioStreamBasicDescription.mSampleRate != 0) - { - if (memcmp(¤tAudioStreamBasicDescription, &newBasicDescription, sizeof(currentAudioStreamBasicDescription)) != 0) - { - cancel = YES; - } - } - } - - if (cancel) - { - [currentlyReadingEntry.dataSource unregisterForEvents]; - - if ([bufferingQueue objectAtIndex:0] == currentlyReadingEntry) - { - [bufferingQueue removeObjectAtIndex:0]; - } - - STKQueueEntry* newEntry = [[STKQueueEntry alloc] initWithDataSource:currentlyReadingEntry.dataSource andQueueItemId:currentlyReadingEntry.queueItemId]; - - entryToUpdate = newEntry; - - [upcomingQueue skipQueue:newEntry]; - - OSSpinLockLock(¤tEntryReferencesLock); - currentlyReadingEntry = nil; - OSSpinLockUnlock(¤tEntryReferencesLock); - } entryToUpdate->audioStreamBasicDescription = newBasicDescription; entryToUpdate->sampleRate = entryToUpdate->audioStreamBasicDescription.mSampleRate; @@ -913,7 +562,6 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ [entryToUpdate updateAudioDataSource]; - pthread_mutex_unlock(&queueBuffersMutex); pthread_mutex_unlock(&playerMutex); } @@ -984,473 +632,6 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ } } --(void) handleAudioPackets2:(const void*)inputData numberBytes:(UInt32)numberBytes numberPackets:(UInt32)numberPackets packetDescriptions:(AudioStreamPacketDescription*)packetDescriptionsIn -{ - if (currentlyReadingEntry == nil) - { - return; - } - - if (seekToTimeWasRequested || disposeWasRequested) - { - return; - } - - if (audioQueue == nil) - { - if (currentlyPlayingEntry == nil) - { - return; - } - - [self createAudioQueue]; - - if (audioQueue == nil) - { - return; - } - - if (self.internalState == STKAudioPlayerInternalStateStopped) - { - if (stopReason == AudioPlayerStopReasonEof) - { - stopReason = AudioPlayerStopReasonNoStop; - self.internalState = STKAudioPlayerInternalStateWaitingForData; - } - else - { - return; - } - } - } - else if (memcmp(¤tAudioStreamBasicDescription, ¤tlyReadingEntry->audioStreamBasicDescription, sizeof(currentAudioStreamBasicDescription)) != 0) - { - if (currentlyReadingEntry == currentlyPlayingEntry && currentlyReadingEntry != nil) - { - [self createAudioQueue]; - - if (audioQueue == nil) - { - return; - } - } - else - { - return; - } - } - - if (discontinuous) - { - discontinuous = NO; - } - - if (packetDescriptionsIn) - { - // VBR - - for (int i = 0; i < numberPackets; i++) - { - SInt64 packetOffset = packetDescriptionsIn[i].mStartOffset; - SInt64 packetSize = packetDescriptionsIn[i].mDataByteSize; - int bufSpaceRemaining; - - int framesPerPacket; - - if (packetDescriptionsIn[i].mVariableFramesInPacket > 0) - { - framesPerPacket = packetDescriptionsIn[i].mVariableFramesInPacket; - } - else - { - framesPerPacket = currentlyReadingEntry->audioStreamBasicDescription.mFramesPerPacket; - } - - if (currentlyReadingEntry->processedPacketsCount * framesPerPacket < currentlyReadingEntry->audioStreamBasicDescription.mSampleRate * 15 * currentlyReadingEntry->audioStreamBasicDescription.mChannelsPerFrame) - { - OSAtomicAdd32((int32_t)packetSize, ¤tlyReadingEntry->processedPacketsSizeTotal); - OSAtomicIncrement32(¤tlyReadingEntry->processedPacketsCount); - } - - if (packetSize > currentlyReadingEntry->packetBufferSize) - { - return; - } - - bufSpaceRemaining = currentlyReadingEntry->packetBufferSize - bytesFilled; - - if (bufSpaceRemaining < packetSize) - { - [self enqueueBuffer]; - - if (audioQueue == nil || disposeWasRequested || seekToTimeWasRequested || self.internalState == STKAudioPlayerInternalStateStopped || self.internalState == STKAudioPlayerInternalStateStopping || self.internalState == STKAudioPlayerInternalStateDisposed) - { - return; - } - } - - if (bytesFilled + packetSize > currentlyReadingEntry->packetBufferSize) - { - return; - } - - AudioQueueBufferRef bufferToFill = audioQueueBuffer[fillBufferIndex]; - memcpy((char*)bufferToFill->mAudioData + bytesFilled, (const char*)inputData + packetOffset, (unsigned long)packetSize); - - framesFilled += framesPerPacket; - - packetDescs[packetsFilled] = packetDescriptionsIn[i]; - packetDescs[packetsFilled].mStartOffset = bytesFilled; - - bytesFilled += packetSize; - packetsFilled++; - - int packetsDescRemaining = audioQueueBufferCount - packetsFilled; - - if (packetsDescRemaining <= 0) - { - [self enqueueBuffer]; - - if (audioQueue == nil || disposeWasRequested || seekToTimeWasRequested || self.internalState == STKAudioPlayerInternalStateStopped || self.internalState == STKAudioPlayerInternalStateStopping || self.internalState == STKAudioPlayerInternalStateDisposed) - { - return; - } - } - } - } - else - { - // CBR - - int offset = 0; - - while (numberBytes) - { - int bytesLeft = currentlyReadingEntry->packetBufferSize - bytesFilled; - - if (bytesLeft < numberBytes) - { - [self enqueueBuffer]; - - if (audioQueue == nil || disposeWasRequested || seekToTimeWasRequested || self.internalState == STKAudioPlayerInternalStateStopped || self.internalState == STKAudioPlayerInternalStateStopping || self.internalState == STKAudioPlayerInternalStateDisposed) - { - return; - } - } - - int copySize; - bytesLeft = currentlyReadingEntry->packetBufferSize - bytesFilled; - - if (bytesLeft < numberBytes) - { - copySize = bytesLeft; - } - else - { - copySize = numberBytes; - } - - if (bytesFilled > currentlyPlayingEntry->packetBufferSize) - { - pthread_mutex_unlock(&playerMutex); - - return; - } - - AudioQueueBufferRef fillBuf = audioQueueBuffer[fillBufferIndex]; - memcpy((char*)fillBuf->mAudioData + bytesFilled, (const char*)(inputData + offset), copySize); - - bytesFilled += copySize; - packetsFilled = 0; - numberBytes -= copySize; - offset += copySize; - } - } -} - - --(BOOL) moreFramesAreDefinitelyAvailableToPlay -{ - return [self moreFramesAreDefinitelyAvailableToPlayIgnoreExisting:NO]; -} - --(BOOL) moreFramesAreDefinitelyAvailableToPlayIgnoreExisting:(BOOL)ignoreExisting -{ - if (numberOfBuffersUsed > 0 && !ignoreExisting) - { - return YES; - } - - if (currentlyReadingEntry == currentlyPlayingEntry) - { - if (currentlyReadingEntry.dataSource.position == currentlyReadingEntry.dataSource.length) - { - return NO; - } - else - { - return YES; - } - } - - if (bufferingQueue.count > 1) - { - return YES; - } - - if (bufferingQueue.count == 1 && [((STKQueueEntry*)[bufferingQueue peek]) couldBeIncompatible:¤tAudioStreamBasicDescription]) - { - return NO; - } - - if (upcomingQueue.count > 0) - { - if ([((STKQueueEntry*)[upcomingQueue peek]) isDefinitelyCompatible:¤tAudioStreamBasicDescription]) - { - return YES; - } - } - - return NO; -} - --(void) makeSureIncompatibleNextBufferingIsCancelled -{ - if (bufferingQueue.count == 1) - { - STKQueueEntry* nextBuffering = [bufferingQueue peek]; - - if ([nextBuffering couldBeIncompatible:¤tAudioStreamBasicDescription]) - { - nextBuffering->cancel = YES; - } - } -} - --(void) handleAudioQueueOutput:(AudioQueueRef)audioQueueIn buffer:(AudioQueueBufferRef)bufferIn -{ - int bufferIndex = -1; - - if (audioQueueIn != audioQueue) - { - return; - } - - STKQueueEntry* entry = nil; - - if (currentlyPlayingEntry) - { - OSSpinLockLock(¤tEntryReferencesLock); - { - if (currentlyPlayingEntry) - { - entry = currentlyPlayingEntry; - - if (!audioQueueFlushing) - { - entry.bytesBuffered += bufferIn->mAudioDataByteSize; - } - } - } - OSSpinLockUnlock(¤tEntryReferencesLock); - } - - int index = (int)bufferIn % audioQueueBufferRefLookupCount; - - for (int i = 0; i < audioQueueBufferCount; i++) - { - if (audioQueueBufferLookup[index].ref == bufferIn) - { - bufferIndex = audioQueueBufferLookup[index].bufferIndex; - - break; - } - - index = (index + 1) % audioQueueBufferRefLookupCount; - } - - audioPacketsPlayedCount++; - - if (bufferIndex == -1) - { - [self playbackThreadQueueMainThreadSyncBlock:^ - { - [self didEncounterError:STKAudioPlayerErrorUnknownBuffer]; - }]; - - pthread_mutex_lock(&queueBuffersMutex); - pthread_cond_signal(&queueBufferReadyCondition); - pthread_mutex_unlock(&queueBuffersMutex); - - return; - } - - pthread_mutex_lock(&queueBuffersMutex); - - BOOL signal = NO; - - if (bufferUsed[bufferIndex]) - { - bufferUsed[bufferIndex] = false; - numberOfBuffersUsed--; - } - else - { - // This should never happen - - signal = YES; - } - - Float64 currentTime = [self currentTimeInFrames]; - - if (entry != nil && !seekToTimeWasRequested) - { - if (entry.lastByteIndex == audioPacketsPlayedCount || ![self moreFramesAreDefinitelyAvailableToPlay]) - { - LOGINFO(@"Final AudioBuffer returned"); - - entry.timeWhenLastBufferReturned = currentTime; - - if (entry.lastFrameIndex < currentTime && entry.lastByteIndex == audioPacketsPlayedCount) - { - LOGINFO(@"Timeline drift"); - - entry.lastFrameIndex = currentTime + 1; - } - - if (![self moreFramesAreDefinitelyAvailableToPlay]) - { - [self makeSureIncompatibleNextBufferingIsCancelled]; - - if (self.internalState != STKAudioPlayerInternalStateFlushingAndStoppingButStillPlaying) - { - if (audioQueue && [self audioQueueIsRunning]) - { - self.internalState = STKAudioPlayerInternalStateFlushingAndStoppingButStillPlaying; - - LOGINFO(@"AudioQueueStop from handleAudioQueueOutput"); - - [self invokeOnPlaybackThread:^ - { - AudioQueueStop(audioQueue, NO); - }]; - } - } - } - } - - if (self.internalState != STKAudioPlayerInternalStateFlushingAndStoppingButStillPlaying) - { - if ((entry.lastFrameIndex != -1 && currentTime >= entry.lastFrameIndex && audioPacketsPlayedCount >= entry.lastByteIndex) || ![self moreFramesAreDefinitelyAvailableToPlay]) - { - LOGINFO(@"Final frame played"); - - Float64 hardwareDelay = currentTime - entry.timeWhenLastBufferReturned; - - if (averageHardwareDelay == 0) - { - averageHardwareDelay = hardwareDelay; - } - else - { - averageHardwareDelay = (averageHardwareDelay + hardwareDelay) / 2; - } - - LOGINFO(([NSString stringWithFormat:@"Current Hardware Delay: %f", hardwareDelay])); - LOGINFO(([NSString stringWithFormat:@"Average Hardware Delay: %f", averageHardwareDelay])); - - entry.lastFrameIndex = -1; - - [self invokeOnPlaybackThread:^ - { - [self audioQueueFinishedPlaying:entry]; - }]; - - signal = YES; - } - } - } - - if (!audioQueueFlushing) - { - if (numberOfBuffersUsed == 0 - && !seekToTimeWasRequested - && !disposeWasRequested - && self.internalState != STKAudioPlayerInternalStateFlushingAndStoppingButStillPlaying) - { - if (self->rebufferingStartFrames == 0) - { - if (self->numberOfBuffersUsed == 0 - && !disposeWasRequested - && !seekToTimeWasRequested - && self->rebufferingStartFrames == 0 - && self.internalState != STKAudioPlayerInternalStateWaitingForData - && self.internalState != STKAudioPlayerInternalStateFlushingAndStoppingButStillPlaying - && [self moreFramesAreDefinitelyAvailableToPlay]) - { - [self invokeOnPlaybackThread:^ - { - self->rebufferingStartFrames = currentTime; - AudioQueuePause(audioQueue); - }]; - - LOGINFO(([NSString stringWithFormat:@"Buffer underrun with time: %f", [self currentTimeInFrames]])); - } - } - - if (self.internalState != STKAudioPlayerInternalStateRebuffering && self.internalState != STKAudioPlayerInternalStatePaused) - { - Float64 interval = STK_FRAMES_MISSED_BEFORE_CONSIDERED_UNDERRUN / currentAudioStreamBasicDescription.mSampleRate; - - [self invokeOnPlaybackThreadAtInterval:interval withBlock:^ - { - [self setRebufferingStateIfApplicable]; - }]; - } - - signal = YES; - } - else - { - if (self.internalState == STKAudioPlayerInternalStateRebuffering && ([self readyToEndRebufferingState] || [self readyToEndWaitingForDataState])) - { - [self invokeOnPlaybackThread:^ - { - self->rebufferingStartFrames = 0; - [self startAudioQueue]; - }]; - - signal = YES; - } - } - } - - signal = signal || disposeWasRequested; - - if (self.internalState == STKAudioPlayerInternalStateStopped - || self.internalState == STKAudioPlayerInternalStateStopping - || self.internalState == STKAudioPlayerInternalStateDisposed - || self.internalState == STKAudioPlayerInternalStateError) - { - signal = signal || waiting || numberOfBuffersUsed < (STK_BUFFERS_NEEDED_TO_START * 2); - } - else if (audioQueueFlushing) - { - signal = signal || (audioQueueFlushing && numberOfBuffersUsed < (STK_BUFFERS_NEEDED_TO_START * 2)); - } - else - { - signal = signal || seekToTimeWasRequested || (numberOfBuffersUsed < self->audioQueueBufferCount / 2); - } - - if (signal) - { - pthread_cond_signal(&queueBufferReadyCondition); - } - - pthread_mutex_unlock(&queueBuffersMutex); -} - -(void) setRebufferingStateIfApplicable { pthread_mutex_lock(&playerMutex); @@ -1479,252 +660,12 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ -(Float64) currentTimeInFrames { - if (audioQueue == nil) + if (audioUnit == nil) { return 0; } - AudioTimeStamp timeStamp; - Boolean outTimelineDiscontinuity; - - AudioQueueGetCurrentTime(audioQueue, NULL, &timeStamp, &outTimelineDiscontinuity); - - return timeStamp.mSampleTime - timelineAdjust; -} - --(void) processFinishedPlayingViaAudioQueueStop -{ - if (currentlyPlayingEntry) - { - pthread_mutex_lock(&playerMutex); - - STKQueueEntry* entry = currentlyPlayingEntry; - - entry.lastFrameIndex = -1; - - pthread_mutex_unlock(&playerMutex); - - [self invokeOnPlaybackThread:^ - { - self->stopReason = AudioPlayerStopReasonEof; - self.internalState = STKAudioPlayerInternalStateStopped; - - if (audioQueue) - { - [self stopAudioQueueWithReason:@"processFinishedPlayingViaAudioQueueStop"]; - } - - [self audioQueueFinishedPlaying:entry]; - }]; - } - else - { - pthread_mutex_lock(&playerMutex); - - STKQueueEntry* entry = currentlyPlayingEntry; - - entry.lastFrameIndex = -1; - - pthread_mutex_unlock(&playerMutex); - - [self invokeOnPlaybackThread:^ - { - if (audioQueue) - { - [self stopAudioQueueWithReason:@"processFinishedPlayingViaAudioQueueStop"]; - } - - [self audioQueueFinishedPlaying:entry]; - }]; - } -} - --(void) handlePropertyChangeForQueue:(AudioQueueRef)audioQueueIn propertyID:(AudioQueuePropertyID)propertyId -{ - if (audioQueueIn != audioQueue) - { - return; - } - - if (propertyId == kAudioQueueProperty_IsRunning) - { - if (![self audioQueueIsRunning] && self.internalState == STKAudioPlayerInternalStateStopping) - { - self.internalState = STKAudioPlayerInternalStateStopped; - } - else if (![self audioQueueIsRunning] && self.internalState == STKAudioPlayerInternalStateFlushingAndStoppingButStillPlaying) - { - LOGINFO(@"AudioQueue not IsRunning") - - [self invokeOnPlaybackThread:^ - { - [self processFinishedPlayingViaAudioQueueStop]; - }]; - - pthread_mutex_lock(&queueBuffersMutex); - - if (signal) - { - pthread_cond_signal(&queueBufferReadyCondition); - } - - pthread_mutex_unlock(&queueBuffersMutex); - } - } -} - --(BOOL) readyToEndRebufferingState -{ - BOOL tailEndOfBuffer = ![self moreFramesAreDefinitelyAvailableToPlayIgnoreExisting:YES]; - - return self->rebufferingStartFrames > 0 && (numberOfBuffersUsed > STK_BUFFERS_NEEDED_WHEN_UNDERUNNING || tailEndOfBuffer); -} - --(BOOL) readyToEndWaitingForDataState -{ - BOOL tailEndOfBuffer = ![self moreFramesAreDefinitelyAvailableToPlayIgnoreExisting:YES]; - - return (self.internalState == STKAudioPlayerInternalStateWaitingForData || self.internalState == STKAudioPlayerInternalStateWaitingForDataAfterSeek) && (numberOfBuffersUsed >= STK_BUFFERS_NEEDED_TO_START || tailEndOfBuffer); -} - --(void) enqueueBuffer -{ - BOOL queueBuffersMutexUnlocked = NO; - - pthread_mutex_lock(&playerMutex); - { - OSStatus error; - - if (audioFileStream == 0) - { - pthread_mutex_unlock(&playerMutex); - - return; - } - - if (self.internalState == STKAudioPlayerInternalStateStopped) - { - pthread_mutex_unlock(&playerMutex); - - return; - } - - if (audioQueueFlushing || newFileToPlay) - { - pthread_mutex_unlock(&playerMutex); - - return; - } - - pthread_mutex_lock(&queueBuffersMutex); - - if (audioQueue == nil) - { - pthread_mutex_unlock(&queueBuffersMutex); - - return; - } - - bufferUsed[fillBufferIndex] = true; - numberOfBuffersUsed++; - - AudioQueueBufferRef buffer = audioQueueBuffer[fillBufferIndex]; - - buffer->mAudioDataByteSize = bytesFilled; - - if (packetsFilled) - { - error = AudioQueueEnqueueBuffer(audioQueue, buffer, packetsFilled, packetDescs); - } - else - { - error = AudioQueueEnqueueBuffer(audioQueue, buffer, 0, NULL); - } - - audioPacketsReadCount++; - framesQueued += framesFilled; - - if (error) - { - pthread_mutex_unlock(&queueBuffersMutex); - pthread_mutex_unlock(&playerMutex); - - [self stopAudioQueueWithReason:@"enqueueBuffer critical error"]; - - return; - } - - if ([self readyToEndRebufferingState]) - { - if (self.internalState != STKAudioPlayerInternalStatePaused) - { - self->rebufferingStartFrames = 0; - - pthread_mutex_unlock(&queueBuffersMutex); - - queueBuffersMutexUnlocked = YES; - - if (![self startAudioQueue] || audioQueue == nil) - { - pthread_mutex_unlock(&playerMutex); - - return; - } - } - } - - if ([self readyToEndWaitingForDataState]) - { - if (self.internalState != STKAudioPlayerInternalStatePaused) - { - pthread_mutex_unlock(&queueBuffersMutex); - - queueBuffersMutexUnlocked = YES; - - if (![self startAudioQueue] || audioQueue == nil) - { - pthread_mutex_unlock(&playerMutex); - - return; - } - } - } - - if (++fillBufferIndex >= audioQueueBufferCount) - { - fillBufferIndex = 0; - } - - bytesFilled = 0; - framesFilled = 0; - packetsFilled = 0; - - if (!queueBuffersMutexUnlocked) - { - pthread_mutex_unlock(&queueBuffersMutex); - } - } - pthread_mutex_unlock(&playerMutex); - - pthread_mutex_lock(&queueBuffersMutex); - - waiting = YES; - - while (bufferUsed[fillBufferIndex] && !(disposeWasRequested || seekToTimeWasRequested || self.internalState == STKAudioPlayerInternalStateStopped || self.internalState == STKAudioPlayerInternalStateStopping || self.internalState == STKAudioPlayerInternalStateDisposed)) - { - if (numberOfBuffersUsed == 0) - { - memset(&bufferUsed[0], 0, sizeof(bool) * audioQueueBufferCount); - - break; - } - - pthread_cond_wait(&queueBufferReadyCondition, &queueBuffersMutex); - } - - waiting = NO; - - pthread_mutex_unlock(&queueBuffersMutex); + return 0; } -(void) didEncounterError:(STKAudioPlayerErrorCode)errorCodeIn @@ -1738,184 +679,6 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ }]; } --(void) createAudioQueue -{ - [self createAudioUnit]; - - OSStatus error; - - LOGINFO(@"Called"); - - [self startSystemBackgroundTask]; - - pthread_mutex_lock(&playerMutex); - pthread_mutex_lock(&queueBuffersMutex); - - if (audioQueue) - { - LOGINFO(@"AudioQueueStop/1"); - - pthread_mutex_unlock(&queueBuffersMutex); - AudioQueueStop(audioQueue, YES); - AudioQueueDispose(audioQueue, YES); - - memset(¤tAudioStreamBasicDescription, 0, sizeof(currentAudioStreamBasicDescription)); - - audioQueue = nil; - } - - if (currentlyPlayingEntry == nil) - { - LOGINFO(@"currentlyPlayingEntry == nil"); - - pthread_mutex_unlock(&queueBuffersMutex); - pthread_mutex_unlock(&playerMutex); - - return; - } - - currentAudioStreamBasicDescription = currentlyPlayingEntry->audioStreamBasicDescription; - - error = AudioQueueNewOutput(¤tAudioStreamBasicDescription, AudioQueueOutputCallbackProc, (__bridge void*)self, NULL, NULL, 0, &audioQueue); - - if (error) - { - [self playbackThreadQueueMainThreadSyncBlock:^ - { - [self.delegate audioPlayer:self didEncounterError:STKAudioPlayerErrorQueueCreationFailed]; - }]; - - pthread_mutex_unlock(&queueBuffersMutex); - pthread_mutex_unlock(&playerMutex); - - return; - } - - AudioQueuePause(audioQueue); - - error = AudioQueueAddPropertyListener(audioQueue, kAudioQueueProperty_IsRunning, AudioQueueIsRunningCallbackProc, (__bridge void*)self); - - if (error) - { - [self playbackThreadQueueMainThreadSyncBlock:^ - { - [self.delegate audioPlayer:self didEncounterError:STKAudioPlayerErrorQueueCreationFailed]; - }]; - - pthread_mutex_unlock(&queueBuffersMutex); - pthread_mutex_unlock(&playerMutex); - - return; - } - -#if TARGET_OS_IPHONE - UInt32 val = kAudioQueueHardwareCodecPolicy_PreferHardware; - - AudioQueueSetProperty(audioQueue, kAudioQueueProperty_HardwareCodecPolicy, &val, sizeof(UInt32)); - - AudioQueueSetParameter(audioQueue, kAudioQueueParam_Volume, 1); -#endif - - memset(audioQueueBufferLookup, 0, sizeof(AudioQueueBufferRefLookupEntry) * audioQueueBufferRefLookupCount); - - // Allocate AudioQueue buffers - - for (int i = 0; i < audioQueueBufferCount; i++) - { - error = AudioQueueAllocateBuffer(audioQueue, currentlyPlayingEntry->packetBufferSize, &audioQueueBuffer[i]); - - unsigned int hash = (unsigned int)audioQueueBuffer[i] % audioQueueBufferRefLookupCount; - - while (true) - { - if (audioQueueBufferLookup[hash].ref == 0) - { - audioQueueBufferLookup[hash].ref = audioQueueBuffer[i]; - audioQueueBufferLookup[hash].bufferIndex = i; - - break; - } - else - { - hash++; - hash %= audioQueueBufferRefLookupCount; - } - } - - bufferUsed[i] = false; - - if (error) - { - [self playbackThreadQueueMainThreadSyncBlock:^ - { - [self.delegate audioPlayer:self didEncounterError:STKAudioPlayerErrorQueueCreationFailed]; - }]; - - pthread_mutex_unlock(&playerMutex); - - return; - } - } - - audioPacketsReadCount = 0; - audioPacketsPlayedCount = 0; - framesQueued = 0; - timelineAdjust = 0; - rebufferingStartFrames = 0; - - // Get file cookie/magic bytes information - - UInt32 cookieSize; - Boolean writable; - - error = AudioFileStreamGetPropertyInfo(audioFileStream, kAudioFileStreamProperty_MagicCookieData, &cookieSize, &writable); - - if (!error) - { - void* cookieData = calloc(1, cookieSize); - - error = AudioFileStreamGetProperty(audioFileStream, kAudioFileStreamProperty_MagicCookieData, &cookieSize, cookieData); - - if (error) - { - free(cookieData); - - pthread_mutex_unlock(&queueBuffersMutex); - pthread_mutex_unlock(&playerMutex); - - return; - } - - error = AudioQueueSetProperty(audioQueue, kAudioQueueProperty_MagicCookie, cookieData, cookieSize); - - if (error) - { - free(cookieData); - - [self playbackThreadQueueMainThreadSyncBlock:^ - { - [self.delegate audioPlayer:self didEncounterError:STKAudioPlayerErrorQueueCreationFailed]; - }]; - - pthread_mutex_unlock(&queueBuffersMutex); - pthread_mutex_unlock(&playerMutex); - - return; - } - - free(cookieData); - } - - AudioQueueSetParameter(audioQueue, kAudioQueueParam_Volume, 1); - - // Reset metering enabled in case the user set it before the queue was created - - [self setMeteringEnabled:meteringEnabled]; - - pthread_mutex_unlock(&queueBuffersMutex); - pthread_mutex_unlock(&playerMutex); -} - -(double) duration { if (newFileToPlay) @@ -2026,14 +789,14 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ [self processRunloop]; }]; - pthread_mutex_lock(&queueBuffersMutex); + pthread_mutex_lock(&playerMutex); if (waiting) { - pthread_cond_signal(&queueBufferReadyCondition); + pthread_cond_signal(&playerThreadReadyCondition); } - pthread_mutex_unlock(&queueBuffersMutex); + pthread_mutex_unlock(&playerMutex); } -(void) seekToTime:(double)value @@ -2084,10 +847,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ if (startPlaying) { - if (audioQueue) - { - [self resetAudioQueueWithReason:@"from setCurrentlyReadingEntry" andPause:YES]; - } + // TODO: RESET AUDIOUNIT } if (audioFileStream) @@ -2121,36 +881,23 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ } else { - pthread_mutex_lock(&queueBuffersMutex); [bufferingQueue enqueue:entry]; - pthread_mutex_unlock(&queueBuffersMutex); } } -(void) audioQueueFinishedPlaying:(STKQueueEntry*)entry { - pthread_mutex_lock(&playerMutex); + STKQueueEntry* next = [bufferingQueue peek]; + + if (next == nil) { - pthread_mutex_lock(&queueBuffersMutex); - - STKQueueEntry* next = [bufferingQueue peek]; - - pthread_mutex_unlock(&queueBuffersMutex); - - if (next == nil) - { - [self processRunloop]; - } - - pthread_mutex_lock(&queueBuffersMutex); - next = [bufferingQueue dequeue]; - pthread_mutex_unlock(&queueBuffersMutex); - - [self processFinishPlayingIfAnyAndPlayingNext:entry withNext:next]; - [self processRunloop]; } - pthread_mutex_unlock(&playerMutex); + + next = [bufferingQueue dequeue]; + + [self processFinishPlayingIfAnyAndPlayingNext:entry withNext:next]; + [self processRunloop]; } -(void) processFinishPlayingIfAnyAndPlayingNext:(STKQueueEntry*)entry withNext:(STKQueueEntry*)next @@ -2281,12 +1028,8 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ -(BOOL) processRunloop { - BOOL dontPlayNew = NO; - pthread_mutex_lock(&playerMutex); { - dontPlayNew = self.internalState == STKAudioPlayerInternalStateFlushingAndStoppingButStillPlaying; - if (self.internalState == STKAudioPlayerInternalStatePaused) { pthread_mutex_unlock(&playerMutex); @@ -2328,8 +1071,6 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ [self processFinishPlayingIfAnyAndPlayingNext:currentlyPlayingEntry withNext:nil]; } - pthread_mutex_lock(&queueBuffersMutex); - if ([bufferingQueue peek] == currentlyPlayingEntry) { [bufferingQueue dequeue]; @@ -2341,24 +1082,18 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ seekToTimeWasRequested = NO; OSSpinLockUnlock(¤tEntryReferencesLock); - pthread_mutex_unlock(&queueBuffersMutex); - if (stopReason == AudioPlayerStopReasonUserActionFlushStop) { [self resetAudioQueueWithReason:@"from processRunLoop"]; } } - else if (currentlyReadingEntry == nil && !dontPlayNew) + else if (currentlyReadingEntry == nil) { - pthread_mutex_lock(&queueBuffersMutex); - BOOL processNextToRead = YES; STKQueueEntry* next = [bufferingQueue peek]; if (next != nil && next->audioStreamBasicDescription.mSampleRate == 0) { - pthread_mutex_unlock(&queueBuffersMutex); - processNextToRead = NO; } @@ -2368,8 +1103,6 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ if ([next isKnownToBeIncompatible:¤tAudioStreamBasicDescription] && currentlyPlayingEntry != nil) { - pthread_mutex_unlock(&queueBuffersMutex); - processNextToRead = NO; } } @@ -2383,8 +1116,6 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ BOOL startPlaying = currentlyPlayingEntry == nil; BOOL wasCurrentlyPlayingNothing = currentlyPlayingEntry == nil; - pthread_mutex_unlock(&queueBuffersMutex); - [self setCurrentlyReadingEntry:entry andStartPlaying:startPlaying]; if (wasCurrentlyPlayingNothing) @@ -2396,22 +1127,12 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ } else if (currentlyPlayingEntry == nil) { - pthread_mutex_unlock(&queueBuffersMutex); - if (self.internalState != STKAudioPlayerInternalStateStopped) { [self stopAudioQueueWithReason:@"from processRunLoop/2"]; stopReason = AudioPlayerStopReasonEof; } } - else - { - pthread_mutex_unlock(&queueBuffersMutex); - } - } - else - { - pthread_mutex_unlock(&queueBuffersMutex); } } @@ -2465,9 +1186,6 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ [threadStartedLock lockWhenCondition:0]; [threadStartedLock unlockWithCondition:1]; - bytesFilled = 0; - packetsFilled = 0; - [playbackThreadRunLoop addPort:[NSPort port] forMode:NSDefaultRunLoopMode]; while (true) @@ -2562,9 +1280,9 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ discontinuous = YES; } - if (audioQueue) + if (audioUnit) { - [self resetAudioQueueWithReason:@"from seekToTime"]; + // TODO: RESET AUDIO UNIT } currentEntry.bytesBuffered = 0; @@ -2577,99 +1295,11 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ { [self startAudioUnit]; - OSStatus error; - - LOGINFO(@"Called"); - - AudioQueueSetParameter(audioQueue, kAudioQueueParam_Volume, 1); - - error = AudioQueueStart(audioQueue, NULL); - - if (error) - { -#if TARGET_OS_IPHONE - if (backgroundTaskId == UIBackgroundTaskInvalid) - { - [self startSystemBackgroundTask]; - } -#endif - - [self stopAudioQueueWithReason:@"from startAudioQueue"]; - [self createAudioQueue]; - - if (audioQueue != nil) - { - AudioQueueStart(audioQueue, NULL); - } - } - - [self stopSystemBackgroundTask]; - self.internalState = STKAudioPlayerInternalStatePlaying; return YES; } --(void) stopAudioQueueWithReason:(NSString*)reason -{ - OSStatus error; - - LOGINFO(([NSString stringWithFormat:@"With Reason: %@", reason])); - - if (!audioQueue) - { - LOGINFO(@"Already No AudioQueue"); - - self.internalState = STKAudioPlayerInternalStateStopped; - - return; - } - else - { - LOGINFO(@"Stopping AudioQueue"); - - audioQueueFlushing = YES; - - error = AudioQueueStop(audioQueue, YES); - error = error | AudioQueueDispose(audioQueue, YES); - - memset(¤tAudioStreamBasicDescription, 0, sizeof(currentAudioStreamBasicDescription)); - - audioQueue = nil; - } - - if (error) - { - [self didEncounterError:STKAudioPlayerErrorQueueStopFailed]; - } - - pthread_mutex_lock(&queueBuffersMutex); - - if (numberOfBuffersUsed != 0) - { - numberOfBuffersUsed = 0; - - memset(&bufferUsed[0], 0, sizeof(bool) * audioQueueBufferCount); - } - - pthread_cond_signal(&queueBufferReadyCondition); - pthread_mutex_unlock(&queueBuffersMutex); - - bytesFilled = 0; - fillBufferIndex = 0; - packetsFilled = 0; - framesQueued = 0; - timelineAdjust = 0; - rebufferingStartFrames = 0; - - audioPacketsReadCount = 0; - audioPacketsPlayedCount = 0; - audioQueueFlushing = NO; - - self.internalState = STKAudioPlayerInternalStateStopped; -} - - -(void) resetAudioQueueWithReason:(NSString*)reason { [self resetAudioQueueWithReason:reason andPause:NO]; @@ -2746,12 +1376,6 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ { currentlyPlayingEntry->lastProgress = 0; } - - audioPacketsReadCount = 0; - audioPacketsPlayedCount = 0; - audioQueueFlushing = NO; - - pthread_mutex_unlock(&queueBuffersMutex); } -(void) dataSourceDataAvailable:(STKDataSource*)dataSourceIn @@ -3114,11 +1738,6 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ return retval; } - -/// -/// AudioUnit -/// - #define kOutputBus 0 #define kInputBus 1 @@ -3152,6 +1771,45 @@ BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* classDesc return NO; } +-(void) createAudioConverter +{ + AudioClassDescription classDesc; + + if (GetHardwareCodecClassDesc(currentAudioStreamBasicDescription.mFormatID, &classDesc)) + { + status = AudioConverterNewSpecific(¤tAudioStreamBasicDescription, &canonicalAudioStreamBasicDescription, 1, &classDesc, &audioConverterRef); + } + + if (!audioConverterRef) + { + status = AudioConverterNew(¤tAudioStreamBasicDescription, &canonicalAudioStreamBasicDescription, &audioConverterRef); + } + + Boolean writable; + UInt32 cookieSize; + + error = AudioFileStreamGetPropertyInfo(audioFileStream, kAudioFileStreamProperty_MagicCookieData, &cookieSize, &writable); + + if (!error) + { + void cookieData = alloca(cookieSize); + + error = AudioFileStreamGetProperty(audioFileStream, kAudioFileStreamProperty_MagicCookieData, &cookieSize, cookieData); + + if (error) + { + return; + } + + error = AudioConverterSetProperty(audioConverterRef, kAudioConverterDecompressionMagicCookie, cookieSize, &cookieData); + + if (error) + { + return; + } + } +} + -(void) createAudioUnit { pthread_mutex_lock(&playerMutex); @@ -3177,18 +1835,6 @@ BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* classDesc status = AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, kOutputBus, &flag, sizeof(flag)); status = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &canonicalAudioStreamBasicDescription, sizeof(canonicalAudioStreamBasicDescription)); - AudioClassDescription classDesc; - - if (GetHardwareCodecClassDesc(currentAudioStreamBasicDescription.mFormatID, &classDesc)) - { - status = AudioConverterNewSpecific(¤tAudioStreamBasicDescription, &canonicalAudioStreamBasicDescription, 1, &classDesc, &audioConverterRef); - } - - if (!audioConverterRef) - { - status = AudioConverterNew(¤tAudioStreamBasicDescription, &canonicalAudioStreamBasicDescription, &audioConverterRef); - } - AURenderCallbackStruct callbackStruct; callbackStruct.inputProc = playbackCallback; @@ -3213,9 +1859,34 @@ BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* classDesc return YES; } --(void) stopAudioUnit +-(void) stopAudioUnitWithReason:(NSString*)reason { + OSStatus error; + + LOGINFO(([NSString stringWithFormat:@"With Reason: %@", reason])); + + if (!audioUnit) + { + LOGINFO(@"No AudioUnit"); + + self.internalState = STKAudioPlayerInternalStateStopped; + + return; + } + else + { + LOGINFO(@"Stopping AudioUnit"); + } + AudioOutputUnitStop(audioUnit); + + timelineAdjust = 0; + rebufferingStartFrames = 0; + + audioPacketsReadCount = 0; + audioPacketsPlayedCount = 0; + + self.internalState = STKAudioPlayerInternalStateStopped; } typedef struct @@ -3337,7 +2008,7 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu if (framesLeftInsideBuffer == 0) { - pthread_mutex_lock(&pcmBuffersMutex); + pthread_mutex_lock(&readerThreadMutex); while (true) { @@ -3355,19 +2026,19 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu if (disposeWasRequested || seekToTimeWasRequested || self.internalState == STKAudioPlayerInternalStateStopped || self.internalState == STKAudioPlayerInternalStateStopping || self.internalState == STKAudioPlayerInternalStateDisposed) { - pthread_mutex_unlock(&pcmBuffersMutex); + pthread_mutex_unlock(&readerThreadMutex); return; } waiting = YES; - pthread_cond_wait(&pcmBuffersReadyCondition, &pcmBuffersMutex); + pthread_cond_wait(&readerThreadReadyCondition, &readerThreadMutex); waiting = NO; } - pthread_mutex_unlock(&pcmBuffersMutex); + pthread_mutex_unlock(&readerThreadMutex); } AudioBuffer* localPcmAudioBuffer; @@ -3396,9 +2067,9 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu if (status == 100) { - pthread_mutex_lock(&pcmBuffersMutex); + pthread_mutex_lock(&readerThreadMutex); pcmBufferUsedFrameCount += framesAdded; - pthread_mutex_unlock(&pcmBuffersMutex); + pthread_mutex_unlock(&readerThreadMutex); return; } @@ -3571,9 +2242,9 @@ static OSStatus playbackCallback(void* inRefCon, AudioUnitRenderActionFlags* ioA if (signal) { - pthread_mutex_lock(&audioPlayer->pcmBuffersMutex); - pthread_cond_signal(&audioPlayer->pcmBuffersReadyCondition); - pthread_mutex_unlock(&audioPlayer->pcmBuffersMutex); + pthread_mutex_lock(&audioPlayer->readerThreadMutex); + pthread_cond_signal(&audioPlayer->readerThreadReadyCondition); + pthread_mutex_unlock(&audioPlayer->readerThreadMutex); } return 0; diff --git a/StreamingKit/StreamingKit/STKQueueEntry.h b/StreamingKit/StreamingKit/STKQueueEntry.h index 8fd8313..6cb9c05 100644 --- a/StreamingKit/StreamingKit/STKQueueEntry.h +++ b/StreamingKit/StreamingKit/STKQueueEntry.h @@ -6,8 +6,43 @@ // Copyright (c) 2014 Thong Nguyen. All rights reserved. // -#import +#import "STKDataSource.h" +#import "AudioToolbox/AudioToolbox.h" -@interface STKQueueEntry : NSobject +@interface STKQueueEntry : NSObject +{ +@public + BOOL parsedHeader; + double sampleRate; + double lastProgress; + double packetDuration; + UInt64 audioDataOffset; + UInt64 audioDataByteCount; + UInt32 packetBufferSize; + volatile BOOL cancel; + volatile int processedPacketsCount; + volatile int processedPacketsSizeTotal; + AudioStreamBasicDescription audioStreamBasicDescription; +} -@end +@property (readwrite, retain) NSObject* queueItemId; +@property (readwrite, retain) STKDataSource* dataSource; +@property (readwrite) Float64 seekTime; +@property (readwrite) int bytesBuffered; +@property (readwrite) int lastByteIndex; +@property (readwrite) Float64 lastFrameIndex; +@property (readwrite) Float64 timeWhenLastBufferReturned; +@property (readwrite) Float64 firstFrameIndex; +@property (readonly) UInt64 audioDataLengthInBytes; + +-(double) duration; +-(double) calculatedBitRate; +-(void) updateAudioDataSource; +-(BOOL) isDefinitelyCompatible:(AudioStreamBasicDescription*)basicDescription; +-(BOOL) isKnownToBeIncompatible:(AudioStreamBasicDescription*)basicDescription; +-(BOOL) couldBeIncompatible:(AudioStreamBasicDescription*)basicDescription; +-(Float64) calculateProgressWithTotalFramesPlayed:(Float64)framesPlayed; + +-(id) initWithDataSource:(STKDataSource*)dataSource andQueueItemId:(NSObject*)queueItemId; + +@end \ No newline at end of file diff --git a/StreamingKit/StreamingKit/STKQueueEntry.m b/StreamingKit/StreamingKit/STKQueueEntry.m index 842fb1c..fa23f05 100644 --- a/StreamingKit/StreamingKit/STKQueueEntry.m +++ b/StreamingKit/StreamingKit/STKQueueEntry.m @@ -7,7 +7,148 @@ // #import "STKQueueEntry.h" +#import "STKDataSource.h" + +#define STK_BIT_RATE_ESTIMATION_MIN_PACKETS (64) @implementation STKQueueEntry -@end +-(id) initWithDataSource:(STKDataSource*)dataSourceIn andQueueItemId:(NSObject*)queueItemIdIn +{ + if (self = [super init]) + { + self.dataSource = dataSourceIn; + self.queueItemId = queueItemIdIn; + self.lastFrameIndex = -1; + self.lastByteIndex = -1; + } + + return self; +} + +-(double) calculatedBitRate +{ + double retval; + + if (packetDuration && processedPacketsCount > STK_BIT_RATE_ESTIMATION_MIN_PACKETS) + { + double averagePacketByteSize = processedPacketsSizeTotal / processedPacketsCount; + + retval = averagePacketByteSize / packetDuration * 8; + + return retval; + } + + retval = (audioStreamBasicDescription.mBytesPerFrame * audioStreamBasicDescription.mSampleRate) * 8; + + return retval; +} + +-(void) updateAudioDataSource +{ + if ([self.dataSource conformsToProtocol:@protocol(AudioDataSource)]) + { + double calculatedBitrate = [self calculatedBitRate]; + + id audioDataSource = (id)self.dataSource; + + audioDataSource.averageBitRate = calculatedBitrate; + audioDataSource.audioDataOffset = audioDataOffset; + } +} + +-(Float64) calculateProgressWithTotalFramesPlayed:(Float64)framesPlayed +{ + return (Float64)self.seekTime + ((framesPlayed - self.firstFrameIndex) / (Float64)self->audioStreamBasicDescription.mSampleRate); +} + +-(double) calculateProgressWithBytesPlayed:(Float64)bytesPlayed +{ + double retval = lastProgress; + + if (self->sampleRate > 0) + { + double calculatedBitrate = [self calculatedBitRate]; + + retval = bytesPlayed / calculatedBitrate * 8; + + retval = self.seekTime + retval; + + [self updateAudioDataSource]; + } + + return retval; +} + +-(double) duration +{ + if (self->sampleRate <= 0) + { + return 0; + } + + UInt64 audioDataLengthInBytes = [self audioDataLengthInBytes]; + + double calculatedBitRate = [self calculatedBitRate]; + + if (calculatedBitRate < 1.0 || self.dataSource.length == 0) + { + return 0; + } + + return audioDataLengthInBytes / (calculatedBitRate / 8); +} + +-(UInt64) audioDataLengthInBytes +{ + if (audioDataByteCount) + { + return audioDataByteCount; + } + else + { + if (!self.dataSource.length) + { + return 0; + } + + return self.dataSource.length - audioDataOffset; + } +} + +-(BOOL) isDefinitelyCompatible:(AudioStreamBasicDescription*)basicDescription +{ + if (self->audioStreamBasicDescription.mSampleRate == 0) + { + return NO; + } + + return (memcmp(&(self->audioStreamBasicDescription), basicDescription, sizeof(*basicDescription)) == 0); +} + +-(BOOL) isKnownToBeIncompatible:(AudioStreamBasicDescription*)basicDescription +{ + if (self->audioStreamBasicDescription.mSampleRate == 0) + { + return NO; + } + + return (memcmp(&(self->audioStreamBasicDescription), basicDescription, sizeof(*basicDescription)) != 0); +} + +-(BOOL) couldBeIncompatible:(AudioStreamBasicDescription*)basicDescription +{ + if (self->audioStreamBasicDescription.mSampleRate == 0) + { + return YES; + } + + return memcmp(&(self->audioStreamBasicDescription), basicDescription, sizeof(*basicDescription)) != 0; +} + +-(NSString*) description +{ + return [[self queueItemId] description]; +} + +@end \ No newline at end of file From 511185c9291aff6fdbf35d977b8b67e86a1d6287 Mon Sep 17 00:00:00 2001 From: Thong Nguyen Date: Thu, 30 Jan 2014 18:22:31 +0000 Subject: [PATCH 11/33] More Audio Units work --- .../ExampleApp.xcodeproj/project.pbxproj | 2 + .../NSMutableArray+STKAudioPlayer.h | 2 +- .../NSMutableArray+STKAudioPlayer.m | 2 +- StreamingKit/StreamingKit/STKAudioPlayer.h | 2 +- StreamingKit/StreamingKit/STKAudioPlayer.m | 257 +++++------------- StreamingKit/StreamingKit/STKQueueEntry.h | 2 + StreamingKit/StreamingKit/STKQueueEntry.m | 4 +- 7 files changed, 80 insertions(+), 191 deletions(-) diff --git a/ExampleApp/ExampleApp.xcodeproj/project.pbxproj b/ExampleApp/ExampleApp.xcodeproj/project.pbxproj index bad1390..33d2d4c 100644 --- a/ExampleApp/ExampleApp.xcodeproj/project.pbxproj +++ b/ExampleApp/ExampleApp.xcodeproj/project.pbxproj @@ -412,6 +412,7 @@ GCC_PREFIX_HEADER = "ExampleApp/ExampleApp-Prefix.pch"; INFOPLIST_FILE = "ExampleApp/ExampleApp-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 6.0; + OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = app; }; @@ -427,6 +428,7 @@ GCC_PREFIX_HEADER = "ExampleApp/ExampleApp-Prefix.pch"; INFOPLIST_FILE = "ExampleApp/ExampleApp-Info.plist"; IPHONEOS_DEPLOYMENT_TARGET = 6.0; + OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = app; }; diff --git a/StreamingKit/StreamingKit/NSMutableArray+STKAudioPlayer.h b/StreamingKit/StreamingKit/NSMutableArray+STKAudioPlayer.h index 4d6bd51..c3644b4 100644 --- a/StreamingKit/StreamingKit/NSMutableArray+STKAudioPlayer.h +++ b/StreamingKit/StreamingKit/NSMutableArray+STKAudioPlayer.h @@ -8,7 +8,7 @@ #import -@interface NSMutableArray(STKAudioPlayer) +@interface NSMutableArray (STKAudioPlayer) -(void) enqueue:(id)obj; -(void) skipQueue:(id)obj; -(id) dequeue; diff --git a/StreamingKit/StreamingKit/NSMutableArray+STKAudioPlayer.m b/StreamingKit/StreamingKit/NSMutableArray+STKAudioPlayer.m index 687f91f..c339053 100644 --- a/StreamingKit/StreamingKit/NSMutableArray+STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/NSMutableArray+STKAudioPlayer.m @@ -8,7 +8,7 @@ #import "NSMutableArray+STKAudioPlayer.h" -@implementation NSMutableArray(STKAudioPlayer) +@implementation NSMutableArray (STKAudioPlayer) -(void) enqueue:(id)obj { diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.h b/StreamingKit/StreamingKit/STKAudioPlayer.h index c5919c0..f9107e8 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.h +++ b/StreamingKit/StreamingKit/STKAudioPlayer.h @@ -125,7 +125,7 @@ STKAudioPlayerErrorCode; @property (readwrite, unsafe_unretained) id delegate; -(id) init; --(id) initWithNumberOfAudioQueueBuffers:(int)numberOfAudioQueueBuffers andReadBufferSize:(int)readBufferSizeIn; +-(id) initWithReadBufferSize:(int)readBufferSizeIn; -(STKDataSource*) dataSourceFromURL:(NSURL*)url; -(void) play:(NSString*)urlString; -(void) playWithURL:(NSURL*)url; diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index f1cbb8b..1a78e47 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -49,7 +49,6 @@ #define STK_DEFAULT_PACKET_BUFFER_SIZE (2048) #define STK_FRAMES_MISSED_BEFORE_CONSIDERED_UNDERRUN (1024) - #define LOGINFO(x) [self logInfo:[NSString stringWithFormat:@"%s %@", sel_getName(_cmd), x]]; @interface STKAudioPlayer() @@ -57,6 +56,9 @@ UInt8* readBuffer; int readBufferSize; + UInt32 framesRequiredToStartPlaying; + UInt32 framesRequiredToPlayAfterRebuffering; + AudioComponentInstance audioUnit; STKQueueEntry* currentlyPlayingEntry; @@ -117,19 +119,6 @@ @property (readwrite) STKAudioPlayerInternalState internalState; @property (readwrite) STKAudioPlayerInternalState stateBeforePaused; --(void) logInfo:(NSString*)line; --(void) createAudioQueue; --(void) enqueueBuffer; --(void) resetAudioQueueWithReason:(NSString*)reason; --(BOOL) startAudioQueue; --(void) stopAudioQueueWithReason:(NSString*)reason; --(BOOL) processRunloop; --(void) wakeupPlaybackThread; --(void) audioQueueFinishedPlaying:(STKQueueEntry*)entry; --(void) processSeekToTime; --(void) didEncounterError:(STKAudioPlayerErrorCode)errorCode; --(void) setInternalState:(STKAudioPlayerInternalState)value; --(void) processFinishPlayingIfAnyAndPlayingNext:(STKQueueEntry*)entry withNext:(STKQueueEntry*)next; -(void) handlePropertyChangeForFileStream:(AudioFileStreamID)audioFileStreamIn fileStreamPropertyID:(AudioFileStreamPropertyID)propertyID ioFlags:(UInt32*)ioFlags; -(void) handleAudioPackets:(const void*)inputData numberBytes:(UInt32)numberBytes numberPackets:(UInt32)numberPackets packetDescriptions:(AudioStreamPacketDescription*)packetDescriptions; @end @@ -258,6 +247,8 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn canonicalAudioStreamBasicDescription.mBitsPerChannel = 8 * sizeof(AudioSampleType); canonicalAudioStreamBasicDescription.mBytesPerPacket = canonicalAudioStreamBasicDescription.mBytesPerFrame * canonicalAudioStreamBasicDescription.mFramesPerPacket; + framesRequiredToStartPlaying = canonicalAudioStreamBasicDescription.mSampleRate * STK_DEFAULT_SECONDS_REQUIRED_TO_START_PLAYING; + pcmAudioBuffer = &pcmAudioBufferList.mBuffers[0]; pcmAudioBufferList.mNumberBuffers = 1; pcmAudioBufferList.mBuffers[0].mDataByteSize = (canonicalAudioStreamBasicDescription.mSampleRate * STK_DEFAULT_PCM_BUFFER_SIZE_IN_SECONDS) * canonicalAudioStreamBasicDescription.mBytesPerFrame; @@ -288,6 +279,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn upcomingQueue = [[NSMutableArray alloc] init]; bufferingQueue = [[NSMutableArray alloc] init]; + [self createAudioUnit]; [self createPlaybackThread]; } @@ -561,6 +553,9 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn } [entryToUpdate updateAudioDataSource]; + currentAudioStreamBasicDescription = currentlyReadingEntry->audioStreamBasicDescription; + + [self createAudioConverter]; pthread_mutex_unlock(&playerMutex); } @@ -878,6 +873,8 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn [self clearQueueIncludingUpcoming:NO]; [self processFinishPlayingIfAnyAndPlayingNext:currentlyPlayingEntry withNext:entry]; + + [self startAudioUnit]; } else { @@ -1060,7 +1057,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn else if (self.internalState == STKAudioPlayerInternalStateStopped && (stopReason == AudioPlayerStopReasonUserAction || stopReason == AudioPlayerStopReasonUserActionFlushStop)) { - [self stopAudioQueueWithReason:@"from processRunLoop/1"]; + [self stopAudioUnitWithReason:@"from processRunLoop/1"]; currentlyReadingEntry.dataSource.delegate = nil; [currentlyReadingEntry.dataSource unregisterForEvents]; @@ -1129,7 +1126,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn { if (self.internalState != STKAudioPlayerInternalStateStopped) { - [self stopAudioQueueWithReason:@"from processRunLoop/2"]; + [self stopAudioUnitWithReason:@"from processRunLoop/2"]; stopReason = AudioPlayerStopReasonEof; } } @@ -1291,14 +1288,6 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn [self clearQueue]; } --(BOOL) startAudioQueue -{ - [self startAudioUnit]; - - self.internalState = STKAudioPlayerInternalStatePlaying; - - return YES; -} -(void) resetAudioQueueWithReason:(NSString*)reason { @@ -1307,10 +1296,11 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn -(void) resetAudioQueueWithReason:(NSString*)reason andPause:(BOOL)pause { - OSStatus error; +// OSStatus error; LOGINFO(([NSString stringWithFormat:@"With Reason: %@", reason])); + /* pthread_mutex_lock(&playerMutex); { audioQueueFlushing = YES; @@ -1371,6 +1361,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn fillBufferIndex = 0; packetsFilled = 0; framesQueued = 0; + */ if (currentlyPlayingEntry) { @@ -1471,17 +1462,6 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn return; } - LOGINFO(([NSString stringWithFormat:@"eof: %lld %lld", audioPacketsReadCount, audioPacketsPlayedCount])); - - if (bytesFilled > 0) - { - LOGINFO(([NSString stringWithFormat:@"eof: enqueueBuffer"])); - - [self enqueueBuffer]; - } - - LOGINFO(([NSString stringWithFormat:@" %@ (ptr:%d)", dataSourceIn, (int)dataSourceIn])); - NSObject* queueItemId = currentlyReadingEntry.queueItemId; if (disposeWasRequested) @@ -1494,67 +1474,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn [self.delegate audioPlayer:self didFinishBufferingSourceWithQueueItemId:queueItemId]; }]; - if (disposeWasRequested) - { - return; - } - - pthread_mutex_lock(&playerMutex); - - if (audioQueue) - { - currentlyReadingEntry.lastFrameIndex = self->framesQueued; - currentlyReadingEntry.lastByteIndex = audioPacketsReadCount; - - if (numberOfBuffersUsed == 0 && currentlyReadingEntry == currentlyPlayingEntry) - { - seekToTimeWasRequested = NO; - - if (audioQueue) - { - if ([self audioQueueIsRunning]) - { - self.internalState = STKAudioPlayerInternalStateFlushingAndStoppingButStillPlaying; - - LOGINFO(@"Stopping AudioQueue asynchronously"); - - if (AudioQueueStop(audioQueue, NO) != 0) - { - LOGINFO(@"Stopping AudioQueue asynchronously failed"); - - [self processFinishedPlayingViaAudioQueueStop]; - } - } - else - { - LOGINFO(@"AudioQueue already stopped"); - - [self processFinishedPlayingViaAudioQueueStop]; - } - } - } - } - else - { - stopReason = AudioPlayerStopReasonEof; - self.internalState = STKAudioPlayerInternalStateStopped; - } - - pthread_mutex_lock(&queueBuffersMutex); - - if (self.internalState == STKAudioPlayerInternalStateRebuffering && ([self readyToEndRebufferingState] || [self readyToEndWaitingForDataState])) - { - self->rebufferingStartFrames = 0; - [self startAudioQueue]; - } - - OSSpinLockLock(¤tEntryReferencesLock); currentlyReadingEntry = nil; - OSSpinLockUnlock(¤tEntryReferencesLock); - - pthread_mutex_unlock(&queueBuffersMutex); - - pthread_mutex_unlock(&playerMutex); } -(void) pause @@ -1568,9 +1488,9 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn self.stateBeforePaused = self.internalState; self.internalState = STKAudioPlayerInternalStatePaused; - if (audioQueue) + if (audioUnit) { - error = AudioQueuePause(audioQueue); + error = AudioOutputUnitStop(audioUnit); if (error) { @@ -1603,13 +1523,11 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn [self resetAudioQueueWithReason:@"from resume"]; } - if (audioQueue != nil) + if (audioUnit != nil) { - if(!((self.internalState == STKAudioPlayerInternalStateWaitingForData) || (self.internalState == STKAudioPlayerInternalStateRebuffering) || (self.internalState == STKAudioPlayerInternalStateWaitingForDataAfterSeek)) - || [self readyToEndRebufferingState] - || [self readyToEndWaitingForDataState]) + if(!((self.internalState == STKAudioPlayerInternalStateWaitingForData) || (self.internalState == STKAudioPlayerInternalStateRebuffering) || (self.internalState == STKAudioPlayerInternalStateWaitingForDataAfterSeek))) { - error = AudioQueueStart(audioQueue, 0); + error = AudioOutputUnitStop(audioUnit); if (error) { @@ -1678,19 +1596,16 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn [self invokeOnPlaybackThread:^ { - pthread_mutex_lock(&queueBuffersMutex); disposeWasRequested = YES; - pthread_mutex_unlock(&queueBuffersMutex); }]; - pthread_mutex_lock(&queueBuffersMutex); - pthread_cond_signal(&queueBufferReadyCondition); - pthread_mutex_unlock(&queueBuffersMutex); + pthread_mutex_lock(&playerMutex); + pthread_cond_signal(&playerThreadReadyCondition); + pthread_mutex_unlock(&playerMutex); pthread_mutex_lock(&mainThreadSyncCallMutex); pthread_cond_signal(&mainThreadSyncCallReadyCondition); pthread_mutex_unlock(&mainThreadSyncCallMutex); - CFRunLoopStop([runLoop getCFRunLoop]); } @@ -1704,12 +1619,12 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn -(void) mute { - AudioQueueSetParameter(audioQueue, kAudioQueueParam_Volume, 0); + // TODO } -(void) unmute { - AudioQueueSetParameter(audioQueue, kAudioQueueParam_Volume, 1); + // TODO } -(void) dispose @@ -1773,6 +1688,7 @@ BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* classDesc -(void) createAudioConverter { + OSStatus status; AudioClassDescription classDesc; if (GetHardwareCodecClassDesc(currentAudioStreamBasicDescription.mFormatID, &classDesc)) @@ -1788,22 +1704,22 @@ BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* classDesc Boolean writable; UInt32 cookieSize; - error = AudioFileStreamGetPropertyInfo(audioFileStream, kAudioFileStreamProperty_MagicCookieData, &cookieSize, &writable); + status = AudioFileStreamGetPropertyInfo(audioFileStream, kAudioFileStreamProperty_MagicCookieData, &cookieSize, &writable); - if (!error) + if (!status) { - void cookieData = alloca(cookieSize); + void* cookieData = alloca(cookieSize); - error = AudioFileStreamGetProperty(audioFileStream, kAudioFileStreamProperty_MagicCookieData, &cookieSize, cookieData); + status = AudioFileStreamGetProperty(audioFileStream, kAudioFileStreamProperty_MagicCookieData, &cookieSize, cookieData); - if (error) + if (status) { return; } - error = AudioConverterSetProperty(audioConverterRef, kAudioConverterDecompressionMagicCookie, cookieSize, &cookieData); + status = AudioConverterSetProperty(audioConverterRef, kAudioConverterDecompressionMagicCookie, cookieSize, &cookieData); - if (error) + if (status) { return; } @@ -1813,9 +1729,6 @@ BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* classDesc -(void) createAudioUnit { pthread_mutex_lock(&playerMutex); - pthread_mutex_lock(&queueBuffersMutex); - - currentAudioStreamBasicDescription = currentlyPlayingEntry->audioStreamBasicDescription; OSStatus status; AudioComponentDescription desc; @@ -1844,10 +1757,7 @@ BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* classDesc status = AudioUnitInitialize(audioUnit); - pthread_mutex_unlock(&queueBuffersMutex); pthread_mutex_unlock(&playerMutex); - - [self startAudioUnit]; } -(BOOL) startAudioUnit @@ -1861,7 +1771,7 @@ BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* classDesc -(void) stopAudioUnitWithReason:(NSString*)reason { - OSStatus error; + OSStatus status; LOGINFO(([NSString stringWithFormat:@"With Reason: %@", reason])); @@ -1878,14 +1788,10 @@ BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* classDesc LOGINFO(@"Stopping AudioUnit"); } - AudioOutputUnitStop(audioUnit); + status = AudioOutputUnitStop(audioUnit); - timelineAdjust = 0; rebufferingStartFrames = 0; - audioPacketsReadCount = 0; - audioPacketsPlayedCount = 0; - self.internalState = STKAudioPlayerInternalStateStopped; } @@ -1935,57 +1841,11 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu return; } - if (audioQueue == nil) - { - if (currentlyPlayingEntry == nil) - { - return; - } - - [self createAudioQueue]; - - if (audioQueue == nil) - { - return; - } - - if (self.internalState == STKAudioPlayerInternalStateStopped) - { - if (stopReason == AudioPlayerStopReasonEof) - { - stopReason = AudioPlayerStopReasonNoStop; - self.internalState = STKAudioPlayerInternalStateWaitingForData; - } - else - { - return; - } - } - } - else if (memcmp(¤tAudioStreamBasicDescription, ¤tlyReadingEntry->audioStreamBasicDescription, sizeof(currentAudioStreamBasicDescription)) != 0) - { - if (currentlyReadingEntry == currentlyPlayingEntry && currentlyReadingEntry != nil) - { - [self createAudioQueue]; - - if (audioQueue == nil) - { - return; - } - } - else - { - return; - } - } - if (discontinuous) { discontinuous = NO; } - [self startAudioUnit]; - OSStatus status; AudioConvertInfo convertInfo; @@ -2008,7 +1868,7 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu if (framesLeftInsideBuffer == 0) { - pthread_mutex_lock(&readerThreadMutex); + pthread_mutex_lock(&playerMutex); while (true) { @@ -2026,19 +1886,19 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu if (disposeWasRequested || seekToTimeWasRequested || self.internalState == STKAudioPlayerInternalStateStopped || self.internalState == STKAudioPlayerInternalStateStopping || self.internalState == STKAudioPlayerInternalStateDisposed) { - pthread_mutex_unlock(&readerThreadMutex); + pthread_mutex_unlock(&playerMutex); return; } waiting = YES; - pthread_cond_wait(&readerThreadReadyCondition, &readerThreadMutex); + pthread_cond_wait(&playerThreadReadyCondition, &playerMutex); waiting = NO; } - pthread_mutex_unlock(&readerThreadMutex); + pthread_mutex_unlock(&playerMutex); } AudioBuffer* localPcmAudioBuffer; @@ -2067,9 +1927,11 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu if (status == 100) { - pthread_mutex_lock(&readerThreadMutex); + OSSpinLockLock(&pcmBufferSpinLock); pcmBufferUsedFrameCount += framesAdded; - pthread_mutex_unlock(&readerThreadMutex); + currentlyReadingEntry->framesQueued += framesAdded; + OSSpinLockUnlock(&pcmBufferSpinLock); + return; } @@ -2084,6 +1946,7 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu { OSSpinLockLock(&pcmBufferSpinLock); pcmBufferUsedFrameCount += framesAdded; + currentlyReadingEntry->framesQueued += framesAdded; OSSpinLockUnlock(&pcmBufferSpinLock); continue; @@ -2101,6 +1964,7 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu { OSSpinLockLock(&pcmBufferSpinLock); pcmBufferUsedFrameCount += framesAdded; + currentlyReadingEntry->framesQueued += framesAdded; OSSpinLockUnlock(&pcmBufferSpinLock); return; @@ -2109,6 +1973,7 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu { OSSpinLockLock(&pcmBufferSpinLock); pcmBufferUsedFrameCount += framesAdded; + currentlyReadingEntry->framesQueued += framesAdded; OSSpinLockUnlock(&pcmBufferSpinLock); continue; @@ -2135,6 +2000,7 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu { OSSpinLockLock(&pcmBufferSpinLock); pcmBufferUsedFrameCount += framesAdded; + currentlyReadingEntry->framesQueued += framesAdded; OSSpinLockUnlock(&pcmBufferSpinLock); return; @@ -2143,6 +2009,7 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu { OSSpinLockLock(&pcmBufferSpinLock); pcmBufferUsedFrameCount += framesAdded; + currentlyReadingEntry->framesQueued += framesAdded; OSSpinLockUnlock(&pcmBufferSpinLock); continue; @@ -2166,11 +2033,25 @@ static OSStatus playbackCallback(void* inRefCon, AudioUnitRenderActionFlags* ioA UInt32 start = audioPlayer->pcmBufferFrameStartIndex; UInt32 end = (audioPlayer->pcmBufferFrameStartIndex + audioPlayer->pcmBufferUsedFrameCount) % audioPlayer->pcmBufferTotalFrameCount; BOOL signal = audioPlayer->waiting && used < audioPlayer->pcmBufferTotalFrameCount / 2; + BOOL waitForBuffer = NO; + + if (audioPlayer.internalState == STKAudioPlayerInternalStateWaitingForData) + { + if (audioPlayer->currentlyReadingEntry == audioPlayer->currentlyPlayingEntry && audioPlayer->currentlyPlayingEntry->framesQueued < audioPlayer->framesRequiredToStartPlaying) + { + waitForBuffer = YES; + } + else + { + audioPlayer.internalState = STKAudioPlayerInternalStatePlaying; + } + } + OSSpinLockUnlock(&audioPlayer->pcmBufferSpinLock); UInt32 totalFramesCopied = 0; - if (used > 0) + if (used > 0 && !waitForBuffer) { if (!(audioPlayer->buffering && used < audioPlayer->pcmBufferTotalFrameCount)) { @@ -2226,6 +2107,10 @@ static OSStatus playbackCallback(void* inRefCon, AudioUnitRenderActionFlags* ioA } } + OSSpinLockLock(&audioPlayer->pcmBufferSpinLock); + audioPlayer->currentlyPlayingEntry->framesPlayed += totalFramesCopied; + OSSpinLockUnlock(&audioPlayer->pcmBufferSpinLock); + if (totalFramesCopied < inNumberFrames) { UInt32 delta = inNumberFrames - totalFramesCopied; @@ -2242,9 +2127,9 @@ static OSStatus playbackCallback(void* inRefCon, AudioUnitRenderActionFlags* ioA if (signal) { - pthread_mutex_lock(&audioPlayer->readerThreadMutex); - pthread_cond_signal(&audioPlayer->readerThreadReadyCondition); - pthread_mutex_unlock(&audioPlayer->readerThreadMutex); + pthread_mutex_lock(&audioPlayer->playerMutex); + pthread_cond_signal(&audioPlayer->playerThreadReadyCondition); + pthread_mutex_unlock(&audioPlayer->playerMutex); } return 0; diff --git a/StreamingKit/StreamingKit/STKQueueEntry.h b/StreamingKit/StreamingKit/STKQueueEntry.h index 6cb9c05..ec655f4 100644 --- a/StreamingKit/StreamingKit/STKQueueEntry.h +++ b/StreamingKit/StreamingKit/STKQueueEntry.h @@ -16,6 +16,8 @@ double sampleRate; double lastProgress; double packetDuration; + Float64 framesQueued; + Float64 framesPlayed; UInt64 audioDataOffset; UInt64 audioDataByteCount; UInt32 packetBufferSize; diff --git a/StreamingKit/StreamingKit/STKQueueEntry.m b/StreamingKit/StreamingKit/STKQueueEntry.m index fa23f05..199b930 100644 --- a/StreamingKit/StreamingKit/STKQueueEntry.m +++ b/StreamingKit/StreamingKit/STKQueueEntry.m @@ -57,9 +57,9 @@ } } --(Float64) calculateProgressWithTotalFramesPlayed:(Float64)framesPlayed +-(Float64) calculateProgressWithTotalFramesPlayed:(Float64)framesPlayedIn { - return (Float64)self.seekTime + ((framesPlayed - self.firstFrameIndex) / (Float64)self->audioStreamBasicDescription.mSampleRate); + return (Float64)self.seekTime + ((framesPlayedIn - self.firstFrameIndex) / (Float64)self->audioStreamBasicDescription.mSampleRate); } -(double) calculateProgressWithBytesPlayed:(Float64)bytesPlayed From e7a952d248ab94cc8cc0ce2f1b2d5beda1f27c04 Mon Sep 17 00:00:00 2001 From: Thong Nguyen Date: Fri, 31 Jan 2014 12:22:22 +0000 Subject: [PATCH 12/33] Progress and seek now supported --- ExampleApp/ExampleApp/AudioPlayerView.m | 8 +- StreamingKit/StreamingKit/STKAudioPlayer.h | 10 +- StreamingKit/StreamingKit/STKAudioPlayer.m | 442 +++++++++------------ 3 files changed, 185 insertions(+), 275 deletions(-) diff --git a/ExampleApp/ExampleApp/AudioPlayerView.m b/ExampleApp/ExampleApp/AudioPlayerView.m index 6813dcc..c596b1d 100644 --- a/ExampleApp/ExampleApp/AudioPlayerView.m +++ b/ExampleApp/ExampleApp/AudioPlayerView.m @@ -70,6 +70,8 @@ [queueShortFileButton addTarget:self action:@selector(queueShortFileButtonTouched) forControlEvents:UIControlEventTouchUpInside]; [queueShortFileButton setTitle:@"Queue short file" forState:UIControlStateNormal]; + size = CGSizeMake(90, 50); + playButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; playButton.frame = CGRectMake(30, 380, size.width, size.height); [playButton addTarget:self action:@selector(playButtonPressed) forControlEvents:UIControlEventTouchUpInside]; @@ -190,10 +192,6 @@ return; } - [audioPlayer dispose]; - - return; - if (audioPlayer.state == STKAudioPlayerStatePaused) { [audioPlayer resume]; @@ -259,7 +257,7 @@ [self updateControls]; } --(void) audioPlayer:(STKAudioPlayer*)audioPlayer didEncounterError:(STKAudioPlayerErrorCode)errorCode +-(void) audioPlayer:(STKAudioPlayer*)audioPlayer unexpectedError:(STKAudioPlayerErrorCode)errorCode { [self updateControls]; } diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.h b/StreamingKit/StreamingKit/STKAudioPlayer.h index f9107e8..8deeff1 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.h +++ b/StreamingKit/StreamingKit/STKAudioPlayer.h @@ -55,10 +55,10 @@ typedef enum /* Same as STKAudioPlayerInternalStateWaitingForData but isn't immediately raised as a buffering event */ STKAudioPlayerInternalStateWaitingForDataAfterSeek = (1 << 5) | STKAudioPlayerInternalStateRunning, STKAudioPlayerInternalStatePaused = (1 << 6) | STKAudioPlayerInternalStateRunning, - STKAudioPlayerInternalStateFlushingAndStoppingButStillPlaying = (1 << 7) | STKAudioPlayerInternalStateRunning, STKAudioPlayerInternalStateStopping = (1 << 8), STKAudioPlayerInternalStateStopped = (1 << 9), - STKAudioPlayerInternalStateDisposed = (1 << 10), + STKAudioPlayerInternalStatePendingNext = (1 << 10), + STKAudioPlayerInternalStateDisposed = (1 << 30), STKAudioPlayerInternalStateError = (1 << 31) } STKAudioPlayerInternalState; @@ -80,8 +80,7 @@ typedef enum { AudioPlayerStopReasonNoStop = 0, AudioPlayerStopReasonEof, - AudioPlayerStopReasonUserAction, - AudioPlayerStopReasonUserActionFlushStop + AudioPlayerStopReasonUserAction } STKAudioPlayerStopReason; @@ -105,7 +104,7 @@ STKAudioPlayerErrorCode; @protocol STKAudioPlayerDelegate -(void) audioPlayer:(STKAudioPlayer*)audioPlayer stateChanged:(STKAudioPlayerState)state; --(void) audioPlayer:(STKAudioPlayer*)audioPlayer didEncounterError:(STKAudioPlayerErrorCode)errorCode; +-(void) audioPlayer:(STKAudioPlayer*)audioPlayer unexpectedError:(STKAudioPlayerErrorCode)errorCode; -(void) audioPlayer:(STKAudioPlayer*)audioPlayer didStartPlayingQueueItemId:(NSObject*)queueItemId; -(void) audioPlayer:(STKAudioPlayer*)audioPlayer didFinishBufferingSourceWithQueueItemId:(NSObject*)queueItemId; -(void) audioPlayer:(STKAudioPlayer*)audioPlayer didFinishPlayingQueueItemId:(NSObject*)queueItemId withReason:(STKAudioPlayerStopReason)stopReason andProgress:(double)progress andDuration:(double)duration; @@ -137,7 +136,6 @@ STKAudioPlayerErrorCode; -(void) pause; -(void) resume; -(void) stop; --(void) flushStop; -(void) mute; -(void) unmute; -(void) dispose; diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index 1a78e47..e78a2bc 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -41,7 +41,7 @@ #import "libkern/OSAtomic.h" #define STK_DEFAULT_PCM_BUFFER_SIZE_IN_SECONDS (5) -#define STK_DEFAULT_SECONDS_REQUIRED_TO_START_PLAYING (0.75) +#define STK_DEFAULT_SECONDS_REQUIRED_TO_START_PLAYING (0) #define STK_BUFFERS_NEEDED_TO_START (32) #define STK_BUFFERS_NEEDED_WHEN_UNDERUNNING (128) @@ -61,8 +61,8 @@ AudioComponentInstance audioUnit; - STKQueueEntry* currentlyPlayingEntry; - STKQueueEntry* currentlyReadingEntry; + STKQueueEntry* volatile currentlyPlayingEntry; + STKQueueEntry* volatile currentlyReadingEntry; NSMutableArray* upcomingQueue; NSMutableArray* bufferingQueue; @@ -80,8 +80,8 @@ AudioConverterRef audioConverterRef; - AudioStreamBasicDescription currentAudioStreamBasicDescription; AudioStreamBasicDescription canonicalAudioStreamBasicDescription; + AudioStreamBasicDescription audioConverterAudioStreamBasicDescription; NSThread* playbackThread; NSRunLoop* playbackThreadRunLoop; @@ -112,7 +112,6 @@ volatile BOOL waiting; volatile BOOL disposeWasRequested; volatile BOOL seekToTimeWasRequested; - volatile BOOL newFileToPlay; volatile double requestedSeekTime; } @@ -170,10 +169,10 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn newState = STKAudioPlayerStateReady; break; case STKAudioPlayerInternalStateRunning: + case STKAudioPlayerInternalStatePendingNext: case STKAudioPlayerInternalStateStartingThread: case STKAudioPlayerInternalStatePlaying: case STKAudioPlayerInternalStateWaitingForDataAfterSeek: - case STKAudioPlayerInternalStateFlushingAndStoppingButStillPlaying: newState = STKAudioPlayerStatePlaying; break; case STKAudioPlayerInternalStateRebuffering: @@ -449,14 +448,11 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn [self startSystemBackgroundTask]; - [self resetAudioQueueWithReason:@"from skipCurrent"]; - [self clearQueue]; - [upcomingQueue enqueue:[[STKQueueEntry alloc] initWithDataSource:dataSourceIn andQueueItemId:queueItemId]]; - self.internalState = STKAudioPlayerInternalStateRunning; + self.internalState = STKAudioPlayerInternalStatePendingNext; - newFileToPlay = YES; + [self wakeupPlaybackThread]; } pthread_mutex_unlock(&playerMutex); }]; @@ -553,9 +549,8 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn } [entryToUpdate updateAudioDataSource]; - currentAudioStreamBasicDescription = currentlyReadingEntry->audioStreamBasicDescription; - [self createAudioConverter]; + [self createAudioConverter:¤tlyReadingEntry->audioStreamBasicDescription]; pthread_mutex_unlock(&playerMutex); } @@ -631,6 +626,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn { pthread_mutex_lock(&playerMutex); + /* if (self->rebufferingStartFrames > 0) { if ([self currentTimeInFrames] > STK_FRAMES_MISSED_BEFORE_CONSIDERED_UNDERRUN @@ -649,6 +645,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn }]; } } + */ pthread_mutex_unlock(&playerMutex); } @@ -663,20 +660,20 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn return 0; } --(void) didEncounterError:(STKAudioPlayerErrorCode)errorCodeIn +-(void) unexpectedError:(STKAudioPlayerErrorCode)errorCodeIn { errorCode = errorCodeIn; self.internalState = STKAudioPlayerInternalStateError; [self playbackThreadQueueMainThreadSyncBlock:^ { - [self.delegate audioPlayer:self didEncounterError:errorCode]; + [self.delegate audioPlayer:self unexpectedError:errorCode]; }]; } -(double) duration { - if (newFileToPlay) + if (self.internalState == STKAudioPlayerInternalStatePendingNext) { return 0; } @@ -713,28 +710,19 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn return requestedSeekTime; } - if (newFileToPlay) + if (self.internalState == STKAudioPlayerInternalStatePendingNext) { return 0; } - OSSpinLockLock(¤tEntryReferencesLock); - - Float64 currentTime = [self currentTimeInFrames]; STKQueueEntry* entry = currentlyPlayingEntry; if (entry == nil) { - OSSpinLockUnlock(¤tEntryReferencesLock); - return 0; } - Float64 time = currentTime; - - double retval = [entry calculateProgressWithTotalFramesPlayed:time]; - - OSSpinLockUnlock(¤tEntryReferencesLock); + double retval = entry.seekTime + (entry->framesPlayed / canonicalAudioStreamBasicDescription.mSampleRate); return retval; } @@ -842,7 +830,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn if (startPlaying) { - // TODO: RESET AUDIOUNIT + memset(&pcmAudioBuffer->mData[0], 0, pcmBufferTotalFrameCount * pcmBufferFrameSizeInBytes); } if (audioFileStream) @@ -852,6 +840,8 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn audioFileStream = 0; } + STKQueueEntry* originalReadingEntry = currentlyReadingEntry; + if (currentlyReadingEntry) { currentlyReadingEntry.dataSource.delegate = nil; @@ -863,17 +853,30 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn currentlyReadingEntry = entry; OSSpinLockUnlock(¤tEntryReferencesLock); - currentlyReadingEntry.dataSource.delegate = self; + if (originalReadingEntry != currentlyReadingEntry) + { + if ([currentlyReadingEntry isDefinitelyCompatible:&audioConverterAudioStreamBasicDescription]) + { + AudioConverterReset(audioConverterRef); + } + else if (currentlyReadingEntry->parsedHeader) + { + [self createAudioConverter:¤tlyReadingEntry->audioStreamBasicDescription]; + } + else + { + [self destroyAudioConverter]; + } + } + currentlyReadingEntry.dataSource.delegate = self; [currentlyReadingEntry.dataSource registerForEvents:[NSRunLoop currentRunLoop]]; [currentlyReadingEntry.dataSource seekToOffset:0]; if (startPlaying) { [self clearQueueIncludingUpcoming:NO]; - [self processFinishPlayingIfAnyAndPlayingNext:currentlyPlayingEntry withNext:entry]; - [self startAudioUnit]; } else @@ -940,6 +943,8 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn if (!isPlayingSameItemProbablySeek) { + [self setInternalState:STKAudioPlayerInternalStateWaitingForData]; + [self playbackThreadQueueMainThreadSyncBlock:^ { [self.delegate audioPlayer:self didStartPlayingQueueItemId:playingQueueItemId]; @@ -1033,15 +1038,12 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn return YES; } - else if (newFileToPlay) + else if (self.internalState == STKAudioPlayerInternalStatePendingNext) { STKQueueEntry* entry = [upcomingQueue dequeue]; self.internalState = STKAudioPlayerInternalStateWaitingForData; - [self setCurrentlyReadingEntry:entry andStartPlaying:YES]; - - newFileToPlay = NO; } else if (seekToTimeWasRequested && currentlyPlayingEntry && currentlyPlayingEntry != currentlyReadingEntry) { @@ -1049,13 +1051,9 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn currentlyPlayingEntry.lastByteIndex = -1; self.internalState = STKAudioPlayerInternalStateWaitingForDataAfterSeek; - [self setCurrentlyReadingEntry:currentlyPlayingEntry andStartPlaying:YES]; - - currentlyReadingEntry->parsedHeader = NO; } - else if (self.internalState == STKAudioPlayerInternalStateStopped - && (stopReason == AudioPlayerStopReasonUserAction || stopReason == AudioPlayerStopReasonUserActionFlushStop)) + else if (self.internalState == STKAudioPlayerInternalStateStopped && (stopReason == AudioPlayerStopReasonUserAction)) { [self stopAudioUnitWithReason:@"from processRunLoop/1"]; @@ -1078,57 +1076,28 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn currentlyReadingEntry = nil; seekToTimeWasRequested = NO; OSSpinLockUnlock(¤tEntryReferencesLock); - - if (stopReason == AudioPlayerStopReasonUserActionFlushStop) - { - [self resetAudioQueueWithReason:@"from processRunLoop"]; - } } else if (currentlyReadingEntry == nil) { - BOOL processNextToRead = YES; - STKQueueEntry* next = [bufferingQueue peek]; + STKQueueEntry* next; - if (next != nil && next->audioStreamBasicDescription.mSampleRate == 0) - { - processNextToRead = NO; - } - - if (processNextToRead) - { - next = [upcomingQueue peek]; + next = [upcomingQueue peek]; - if ([next isKnownToBeIncompatible:¤tAudioStreamBasicDescription] && currentlyPlayingEntry != nil) - { - processNextToRead = NO; - } - } - - if (processNextToRead) + if (upcomingQueue.count > 0) { - if (upcomingQueue.count > 0) + STKQueueEntry* entry = [upcomingQueue dequeue]; + + BOOL startPlaying = currentlyPlayingEntry == nil; + + self.internalState = STKAudioPlayerInternalStateWaitingForData; + [self setCurrentlyReadingEntry:entry andStartPlaying:startPlaying]; + } + else if (currentlyPlayingEntry == nil) + { + if (self.internalState != STKAudioPlayerInternalStateStopped) { - STKQueueEntry* entry = [upcomingQueue dequeue]; - - BOOL startPlaying = currentlyPlayingEntry == nil; - BOOL wasCurrentlyPlayingNothing = currentlyPlayingEntry == nil; - - [self setCurrentlyReadingEntry:entry andStartPlaying:startPlaying]; - - if (wasCurrentlyPlayingNothing) - { - currentAudioStreamBasicDescription.mSampleRate = 0; - - [self setInternalState:STKAudioPlayerInternalStateWaitingForData]; - } - } - else if (currentlyPlayingEntry == nil) - { - if (self.internalState != STKAudioPlayerInternalStateStopped) - { - [self stopAudioUnitWithReason:@"from processRunLoop/2"]; - stopReason = AudioPlayerStopReasonEof; - } + [self stopAudioUnitWithReason:@"from processRunLoop/2"]; + stopReason = AudioPlayerStopReasonEof; } } } @@ -1233,6 +1202,8 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn return; } + NSLog(@"Seek to time: %f", requestedSeekTime); + long long seekByteOffset = currentEntry->audioDataOffset + (requestedSeekTime / self.duration) * (currentlyReadingEntry.audioDataLengthInBytes); if (seekByteOffset > currentEntry.dataSource.length - (2 * currentEntry->packetBufferSize)) @@ -1267,10 +1238,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn [currentEntry updateAudioDataSource]; [currentEntry.dataSource seekToOffset:seekByteOffset]; - if (self.internalState == STKAudioPlayerInternalStateFlushingAndStoppingButStillPlaying) - { - self.internalState = STKAudioPlayerInternalStatePlaying; - } + self.internalState = STKAudioPlayerInternalStateWaitingForDataAfterSeek; if (seekByteOffset > 0) { @@ -1279,96 +1247,16 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn if (audioUnit) { - // TODO: RESET AUDIO UNIT + [self resetPcmBuffers]; } currentEntry.bytesBuffered = 0; + currentEntry->framesPlayed = 0; currentEntry.firstFrameIndex = [self currentTimeInFrames]; [self clearQueue]; } - --(void) resetAudioQueueWithReason:(NSString*)reason -{ - [self resetAudioQueueWithReason:reason andPause:NO]; -} - --(void) resetAudioQueueWithReason:(NSString*)reason andPause:(BOOL)pause -{ -// OSStatus error; - - LOGINFO(([NSString stringWithFormat:@"With Reason: %@", reason])); - - /* - pthread_mutex_lock(&playerMutex); - { - audioQueueFlushing = YES; - - if (audioQueue) - { - AudioTimeStamp timeStamp; - Boolean outTimelineDiscontinuity; - - error = AudioQueueReset(audioQueue); - - if (pause) - { - AudioQueuePause(audioQueue); - } - - AudioQueueGetCurrentTime(audioQueue, NULL, &timeStamp, &outTimelineDiscontinuity); - - timelineAdjust = timeStamp.mSampleTime; - - BOOL startAudioQueue = NO; - - if (rebufferingStartFrames > 0) - { - startAudioQueue = YES; - rebufferingStartFrames = 0; - } - - if (!pause && startAudioQueue) - { - [self startAudioQueue]; - } - - if (error) - { - [self playbackThreadQueueMainThreadSyncBlock:^ - { - [self didEncounterError:STKAudioPlayerErrorQueueStopFailed];; - }]; - } - } - } - pthread_mutex_unlock(&playerMutex); - - pthread_mutex_lock(&queueBuffersMutex); - - if (numberOfBuffersUsed != 0) - { - numberOfBuffersUsed = 0; - - memset(&bufferUsed[0], 0, sizeof(bool) * audioQueueBufferCount); - } - - pthread_cond_signal(&queueBufferReadyCondition); - - - bytesFilled = 0; - fillBufferIndex = 0; - packetsFilled = 0; - framesQueued = 0; - */ - - if (currentlyPlayingEntry) - { - currentlyPlayingEntry->lastProgress = 0; - } -} - -(void) dataSourceDataAvailable:(STKDataSource*)dataSourceIn { OSStatus error; @@ -1427,7 +1315,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn { if (dataSourceIn == currentlyPlayingEntry.dataSource) { - [self didEncounterError:STKAudioPlayerErrorStreamParseBytesFailed]; + [self unexpectedError:STKAudioPlayerErrorStreamParseBytesFailed]; } return; @@ -1452,7 +1340,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn return; } - [self didEncounterError:STKAudioPlayerErrorDataNotFound]; + [self unexpectedError:STKAudioPlayerErrorDataNotFound]; } -(void) dataSourceEof:(STKDataSource*)dataSourceIn @@ -1474,7 +1362,9 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn [self.delegate audioPlayer:self didFinishBufferingSourceWithQueueItemId:queueItemId]; }]; + OSSpinLockLock(¤tEntryReferencesLock); currentlyReadingEntry = nil; + OSSpinLockUnlock(¤tEntryReferencesLock); } -(void) pause @@ -1494,7 +1384,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn if (error) { - [self didEncounterError:STKAudioPlayerErrorQueuePauseFailed]; + [self unexpectedError:STKAudioPlayerErrorQueuePauseFailed]; pthread_mutex_unlock(&playerMutex); @@ -1520,23 +1410,20 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn if (seekToTimeWasRequested) { - [self resetAudioQueueWithReason:@"from resume"]; + [self resetPcmBuffers]; } if (audioUnit != nil) { - if(!((self.internalState == STKAudioPlayerInternalStateWaitingForData) || (self.internalState == STKAudioPlayerInternalStateRebuffering) || (self.internalState == STKAudioPlayerInternalStateWaitingForDataAfterSeek))) + error = AudioOutputUnitStart(audioUnit); + + if (error) { - error = AudioOutputUnitStop(audioUnit); + [self unexpectedError:STKAudioPlayerErrorQueueStartFailed]; - if (error) - { - [self didEncounterError:STKAudioPlayerErrorQueueStartFailed]; - - pthread_mutex_unlock(&playerMutex); - - return; - } + pthread_mutex_unlock(&playerMutex); + + return; } } @@ -1546,6 +1433,16 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn pthread_mutex_unlock(&playerMutex); } +-(void) resetPcmBuffers +{ + OSSpinLockLock(&pcmBufferSpinLock); + + self->pcmBufferFrameStartIndex = 0; + self->pcmBufferUsedFrameCount = 0; + + OSSpinLockUnlock(&pcmBufferSpinLock); +} + -(void) stop { pthread_mutex_lock(&playerMutex); @@ -1565,25 +1462,6 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn pthread_mutex_unlock(&playerMutex); } --(void) flushStop -{ - pthread_mutex_lock(&playerMutex); - { - if (self.internalState == STKAudioPlayerInternalStateStopped) - { - pthread_mutex_unlock(&playerMutex); - - return; - } - - stopReason = AudioPlayerStopReasonUserActionFlushStop; - self.internalState = STKAudioPlayerInternalStateStopped; - - [self wakeupPlaybackThread]; - } - pthread_mutex_unlock(&playerMutex); -} - -(void) stopThread { BOOL wait = NO; @@ -1686,23 +1564,37 @@ BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* classDesc return NO; } --(void) createAudioConverter +-(void) destroyAudioConverter +{ + if (audioConverterRef) + { + AudioConverterDispose(audioConverterRef); + + audioConverterRef = nil; + } +} + +-(void) createAudioConverter:(AudioStreamBasicDescription*)asbd { OSStatus status; + Boolean writable; + UInt32 cookieSize; + + [self destroyAudioConverter]; + AudioClassDescription classDesc; - if (GetHardwareCodecClassDesc(currentAudioStreamBasicDescription.mFormatID, &classDesc)) + if (GetHardwareCodecClassDesc(asbd->mFormatID, &classDesc)) { - status = AudioConverterNewSpecific(¤tAudioStreamBasicDescription, &canonicalAudioStreamBasicDescription, 1, &classDesc, &audioConverterRef); + AudioConverterNewSpecific(asbd, &canonicalAudioStreamBasicDescription, 1, &classDesc, &audioConverterRef); } if (!audioConverterRef) { - status = AudioConverterNew(¤tAudioStreamBasicDescription, &canonicalAudioStreamBasicDescription, &audioConverterRef); + status = AudioConverterNew(asbd, &canonicalAudioStreamBasicDescription, &audioConverterRef); } - - Boolean writable; - UInt32 cookieSize; + + audioConverterAudioStreamBasicDescription = *asbd; status = AudioFileStreamGetPropertyInfo(audioFileStream, kAudioFileStreamProperty_MagicCookieData, &cookieSize, &writable); @@ -1841,6 +1733,11 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu return; } + if (audioConverterRef == nil) + { + return; + } + if (discontinuous) { discontinuous = NO; @@ -1855,8 +1752,19 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu convertInfo.packetDescriptions = packetDescriptionsIn; convertInfo.audioBuffer.mData = (void *)inputData; convertInfo.audioBuffer.mDataByteSize = numberBytes; - convertInfo.audioBuffer.mNumberChannels = currentAudioStreamBasicDescription.mChannelsPerFrame; + convertInfo.audioBuffer.mNumberChannels = audioConverterAudioStreamBasicDescription.mChannelsPerFrame; + if (currentlyReadingEntry->processedPacketsCount < 1024) + { + for (int i = 0; i < numberPackets && currentlyReadingEntry->processedPacketsCount < 1000; i++) + { + SInt64 packetSize = packetDescriptionsIn[i].mDataByteSize; + + OSAtomicAdd32((int32_t)packetSize, ¤tlyReadingEntry->processedPacketsSizeTotal); + OSAtomicIncrement32(¤tlyReadingEntry->processedPacketsCount); + } + } + while (true) { OSSpinLockLock(&pcmBufferSpinLock); @@ -2027,6 +1935,7 @@ static OSStatus playbackCallback(void* inRefCon, AudioUnitRenderActionFlags* ioA STKAudioPlayer* audioPlayer = (__bridge STKAudioPlayer*)inRefCon; OSSpinLockLock(&audioPlayer->pcmBufferSpinLock); + AudioBuffer* audioBuffer = audioPlayer->pcmAudioBuffer; UInt32 frameSizeInBytes = audioPlayer->pcmBufferFrameSizeInBytes; UInt32 used = audioPlayer->pcmBufferUsedFrameCount; @@ -2034,16 +1943,21 @@ static OSStatus playbackCallback(void* inRefCon, AudioUnitRenderActionFlags* ioA UInt32 end = (audioPlayer->pcmBufferFrameStartIndex + audioPlayer->pcmBufferUsedFrameCount) % audioPlayer->pcmBufferTotalFrameCount; BOOL signal = audioPlayer->waiting && used < audioPlayer->pcmBufferTotalFrameCount / 2; BOOL waitForBuffer = NO; + STKAudioPlayerInternalState state = audioPlayer.internalState; - if (audioPlayer.internalState == STKAudioPlayerInternalStateWaitingForData) + if (state == STKAudioPlayerInternalStateWaitingForData) { - if (audioPlayer->currentlyReadingEntry == audioPlayer->currentlyPlayingEntry && audioPlayer->currentlyPlayingEntry->framesQueued < audioPlayer->framesRequiredToStartPlaying) + if (audioPlayer->currentlyReadingEntry == audioPlayer->currentlyPlayingEntry + && audioPlayer->currentlyPlayingEntry->framesQueued < audioPlayer->framesRequiredToStartPlaying) { waitForBuffer = YES; } - else + } + else if (state == STKAudioPlayerInternalStateRebuffering) + { + if (used < audioPlayer->pcmBufferTotalFrameCount) { - audioPlayer.internalState = STKAudioPlayerInternalStatePlaying; + waitForBuffer = YES; } } @@ -2053,58 +1967,59 @@ static OSStatus playbackCallback(void* inRefCon, AudioUnitRenderActionFlags* ioA if (used > 0 && !waitForBuffer) { - if (!(audioPlayer->buffering && used < audioPlayer->pcmBufferTotalFrameCount)) + if (state == STKAudioPlayerInternalStateWaitingForData) { - if (audioPlayer->buffering) + NSLog(@"Starting"); + } + else if (state == STKAudioPlayerInternalStateRebuffering) + { + NSLog(@"Buffering resuming"); + } + + if (end > start) + { + UInt32 framesToCopy = MIN(inNumberFrames, used); + + ioData->mBuffers[0].mNumberChannels = 2; + ioData->mBuffers[0].mDataByteSize = frameSizeInBytes * framesToCopy; + memcpy(ioData->mBuffers[0].mData, audioBuffer->mData + (start * frameSizeInBytes), ioData->mBuffers[0].mDataByteSize); + + totalFramesCopied = framesToCopy; + + OSSpinLockLock(&audioPlayer->pcmBufferSpinLock); + audioPlayer->pcmBufferFrameStartIndex = (audioPlayer->pcmBufferFrameStartIndex + totalFramesCopied) % audioPlayer->pcmBufferTotalFrameCount; + audioPlayer->pcmBufferUsedFrameCount -= totalFramesCopied; + OSSpinLockUnlock(&audioPlayer->pcmBufferSpinLock); + } + else + { + UInt32 framesToCopy = MIN(inNumberFrames, audioPlayer->pcmBufferTotalFrameCount - start); + + ioData->mBuffers[0].mNumberChannels = 2; + ioData->mBuffers[0].mDataByteSize = frameSizeInBytes * framesToCopy; + memcpy(ioData->mBuffers[0].mData, audioBuffer->mData + (start * frameSizeInBytes), ioData->mBuffers[0].mDataByteSize); + + UInt32 moreFramesToCopy = 0; + UInt32 delta = inNumberFrames - framesToCopy; + + if (delta > 0) { - NSLog(@"Buffering resuming"); + moreFramesToCopy = MIN(delta, end); - audioPlayer->buffering = NO; + ioData->mBuffers[0].mNumberChannels = 2; + ioData->mBuffers[0].mDataByteSize += frameSizeInBytes * moreFramesToCopy; + memcpy(ioData->mBuffers[0].mData + (framesToCopy * frameSizeInBytes), audioBuffer->mData, frameSizeInBytes * moreFramesToCopy); } - if (end > start) - { - UInt32 framesToCopy = MIN(inNumberFrames, used); - - ioData->mBuffers[0].mNumberChannels = 2; - ioData->mBuffers[0].mDataByteSize = frameSizeInBytes * framesToCopy; - memcpy(ioData->mBuffers[0].mData, audioBuffer->mData + (start * frameSizeInBytes), ioData->mBuffers[0].mDataByteSize); - - totalFramesCopied = framesToCopy; - - OSSpinLockLock(&audioPlayer->pcmBufferSpinLock); - audioPlayer->pcmBufferFrameStartIndex = (audioPlayer->pcmBufferFrameStartIndex + totalFramesCopied) % audioPlayer->pcmBufferTotalFrameCount; - audioPlayer->pcmBufferUsedFrameCount -= totalFramesCopied; - OSSpinLockUnlock(&audioPlayer->pcmBufferSpinLock); - } - else - { - UInt32 framesToCopy = MIN(inNumberFrames, audioPlayer->pcmBufferTotalFrameCount - start); - - ioData->mBuffers[0].mNumberChannels = 2; - ioData->mBuffers[0].mDataByteSize = frameSizeInBytes * framesToCopy; - memcpy(ioData->mBuffers[0].mData, audioBuffer->mData + (start * frameSizeInBytes), ioData->mBuffers[0].mDataByteSize); - - UInt32 moreFramesToCopy = 0; - UInt32 delta = inNumberFrames - framesToCopy; - - if (delta > 0) - { - moreFramesToCopy = MIN(delta, end); - - ioData->mBuffers[0].mNumberChannels = 2; - ioData->mBuffers[0].mDataByteSize += frameSizeInBytes * moreFramesToCopy; - memcpy(ioData->mBuffers[0].mData + (framesToCopy * frameSizeInBytes), audioBuffer->mData, frameSizeInBytes * moreFramesToCopy); - } - - totalFramesCopied = framesToCopy + moreFramesToCopy; - - OSSpinLockLock(&audioPlayer->pcmBufferSpinLock); - audioPlayer->pcmBufferFrameStartIndex = (audioPlayer->pcmBufferFrameStartIndex + totalFramesCopied) % audioPlayer->pcmBufferTotalFrameCount; - audioPlayer->pcmBufferUsedFrameCount -= totalFramesCopied; - OSSpinLockUnlock(&audioPlayer->pcmBufferSpinLock); - } + totalFramesCopied = framesToCopy + moreFramesToCopy; + + OSSpinLockLock(&audioPlayer->pcmBufferSpinLock); + audioPlayer->pcmBufferFrameStartIndex = (audioPlayer->pcmBufferFrameStartIndex + totalFramesCopied) % audioPlayer->pcmBufferTotalFrameCount; + audioPlayer->pcmBufferUsedFrameCount -= totalFramesCopied; + OSSpinLockUnlock(&audioPlayer->pcmBufferSpinLock); } + + audioPlayer.internalState = STKAudioPlayerInternalStatePlaying; } OSSpinLockLock(&audioPlayer->pcmBufferSpinLock); @@ -2117,12 +2032,11 @@ static OSStatus playbackCallback(void* inRefCon, AudioUnitRenderActionFlags* ioA memset(ioData->mBuffers[0].mData + (totalFramesCopied * frameSizeInBytes), 0, delta * frameSizeInBytes); - if (!audioPlayer->buffering) + if (!(state == STKAudioPlayerInternalStateWaitingForDataAfterSeek || state == STKAudioPlayerInternalStateWaitingForData || state == STKAudioPlayerInternalStateRebuffering)) { NSLog(@"Buffering"); + audioPlayer.internalState = STKAudioPlayerInternalStateRebuffering; } - - audioPlayer->buffering = YES; } if (signal) From 22e954a5e2938ea8e1b983a981fdc1a940bf1d0a Mon Sep 17 00:00:00 2001 From: Thong Nguyen Date: Fri, 31 Jan 2014 14:40:20 +0000 Subject: [PATCH 13/33] Added AudioUnit based progress/seek and queueing support --- StreamingKit/StreamingKit/STKAudioPlayer.m | 177 +++++++++++++-------- StreamingKit/StreamingKit/STKQueueEntry.h | 7 +- StreamingKit/StreamingKit/STKQueueEntry.m | 6 + 3 files changed, 124 insertions(+), 66 deletions(-) diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index e78a2bc..5750e30 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -40,7 +40,7 @@ #import "NSMutableArray+STKAudioPlayer.h" #import "libkern/OSAtomic.h" -#define STK_DEFAULT_PCM_BUFFER_SIZE_IN_SECONDS (5) +#define STK_DEFAULT_PCM_BUFFER_SIZE_IN_SECONDS (15) #define STK_DEFAULT_SECONDS_REQUIRED_TO_START_PLAYING (0) #define STK_BUFFERS_NEEDED_TO_START (32) @@ -440,36 +440,28 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn -(void) setDataSource:(STKDataSource*)dataSourceIn withQueueItemId:(NSObject*)queueItemId { - [self invokeOnPlaybackThread:^ + pthread_mutex_lock(&playerMutex); { - pthread_mutex_lock(&playerMutex); - { - LOGINFO(([NSString stringWithFormat:@"Playing: %@", [queueItemId description]])); - - [self startSystemBackgroundTask]; + LOGINFO(([NSString stringWithFormat:@"Playing: %@", [queueItemId description]])); + + [self startSystemBackgroundTask]; - [upcomingQueue enqueue:[[STKQueueEntry alloc] initWithDataSource:dataSourceIn andQueueItemId:queueItemId]]; - - self.internalState = STKAudioPlayerInternalStatePendingNext; - - [self wakeupPlaybackThread]; - } - pthread_mutex_unlock(&playerMutex); - }]; + [upcomingQueue enqueue:[[STKQueueEntry alloc] initWithDataSource:dataSourceIn andQueueItemId:queueItemId]]; + + self.internalState = STKAudioPlayerInternalStatePendingNext; + } + pthread_mutex_unlock(&playerMutex); [self wakeupPlaybackThread]; } -(void) queueDataSource:(STKDataSource*)dataSourceIn withQueueItemId:(NSObject*)queueItemId { - [self invokeOnPlaybackThread:^ + pthread_mutex_lock(&playerMutex); { - pthread_mutex_lock(&playerMutex); - { - [upcomingQueue enqueue:[[STKQueueEntry alloc] initWithDataSource:dataSourceIn andQueueItemId:queueItemId]]; - } - pthread_mutex_unlock(&playerMutex); - }]; + [upcomingQueue enqueue:[[STKQueueEntry alloc] initWithDataSource:dataSourceIn andQueueItemId:queueItemId]]; + } + pthread_mutex_unlock(&playerMutex); [self wakeupPlaybackThread]; } @@ -478,6 +470,8 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn { OSStatus error; + NSLog(@"Handle property change"); + if (!currentlyReadingEntry) { return; @@ -622,34 +616,6 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn } } --(void) setRebufferingStateIfApplicable -{ - pthread_mutex_lock(&playerMutex); - - /* - if (self->rebufferingStartFrames > 0) - { - if ([self currentTimeInFrames] > STK_FRAMES_MISSED_BEFORE_CONSIDERED_UNDERRUN - && self.internalState != STKAudioPlayerInternalStateRebuffering - && self.internalState != STKAudioPlayerInternalStatePaused) - { - self.internalState = STKAudioPlayerInternalStateRebuffering; - } - else - { - Float64 interval = STK_FRAMES_MISSED_BEFORE_CONSIDERED_UNDERRUN / currentAudioStreamBasicDescription.mSampleRate / 2; - - [self invokeOnPlaybackThreadAtInterval:interval withBlock:^ - { - [self setRebufferingStateIfApplicable]; - }]; - } - } - */ - - pthread_mutex_unlock(&playerMutex); -} - -(Float64) currentTimeInFrames { if (audioUnit == nil) @@ -863,10 +829,6 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn { [self createAudioConverter:¤tlyReadingEntry->audioStreamBasicDescription]; } - else - { - [self destroyAudioConverter]; - } } currentlyReadingEntry.dataSource.delegate = self; @@ -875,7 +837,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn if (startPlaying) { - [self clearQueueIncludingUpcoming:NO]; + [self clearQueue]; [self processFinishPlayingIfAnyAndPlayingNext:currentlyPlayingEntry withNext:entry]; [self startAudioUnit]; } @@ -910,7 +872,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn LOGINFO(([NSString stringWithFormat:@"Finished: %@, Next: %@, buffering.count=%d,upcoming.count=%d", entry ? [entry description] : @"nothing", [next description], (int)bufferingQueue.count, (int)upcomingQueue.count])); NSObject* queueItemId = entry.queueItemId; - double progress = [entry calculateProgressWithTotalFramesPlayed:[self currentTimeInFrames]]; + double progress = [entry progressInFrames] / canonicalAudioStreamBasicDescription.mSampleRate; double duration = [entry duration]; BOOL isPlayingSameItemProbablySeek = currentlyPlayingEntry == next; @@ -1043,15 +1005,19 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn STKQueueEntry* entry = [upcomingQueue dequeue]; self.internalState = STKAudioPlayerInternalStateWaitingForData; + [self setCurrentlyReadingEntry:entry andStartPlaying:YES]; + [self resetPcmBuffers]; } else if (seekToTimeWasRequested && currentlyPlayingEntry && currentlyPlayingEntry != currentlyReadingEntry) { - currentlyPlayingEntry.lastFrameIndex = -1; + currentlyPlayingEntry->parsedHeader = NO; currentlyPlayingEntry.lastByteIndex = -1; + currentlyPlayingEntry.lastFrameIndex = -1; self.internalState = STKAudioPlayerInternalStateWaitingForDataAfterSeek; [self setCurrentlyReadingEntry:currentlyPlayingEntry andStartPlaying:YES]; + } else if (self.internalState == STKAudioPlayerInternalStateStopped && (stopReason == AudioPlayerStopReasonUserAction)) { @@ -1234,8 +1200,20 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn } } + if (audioConverterRef) + { + AudioConverterReset(audioConverterRef); + } + currentEntry.lastFrameIndex = -1; [currentEntry updateAudioDataSource]; + currentEntry.bytesBuffered = 0; + currentEntry->framesPlayed = 0; + currentEntry->framesQueued = 0; + currentEntry->lastFrameQueued = -1; + currentEntry.firstFrameIndex = [self currentTimeInFrames]; + currentEntry->finished = NO; + [currentEntry.dataSource seekToOffset:seekByteOffset]; self.internalState = STKAudioPlayerInternalStateWaitingForDataAfterSeek; @@ -1250,10 +1228,6 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn [self resetPcmBuffers]; } - currentEntry.bytesBuffered = 0; - currentEntry->framesPlayed = 0; - currentEntry.firstFrameIndex = [self currentTimeInFrames]; - [self clearQueue]; } @@ -1273,6 +1247,8 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn int read = [currentlyReadingEntry.dataSource readIntoBuffer:readBuffer withSize:readBufferSize]; + NSLog(@"dataAvailble read == %d", read); + if (read == 0) { return; @@ -1284,6 +1260,8 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn if (error) { + NSLog(@"dataAvailbleError"); + return; } } @@ -1293,6 +1271,8 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn // iOS will shutdown network connections if the app is backgrounded (i.e. device is locked when player is paused) // We try to reopen -- should probably add a back-off protocol in the future + NSLog(@"dataAvailble read < 0"); + long long position = currentlyReadingEntry.dataSource.position; [currentlyReadingEntry.dataSource seekToOffset:position]; @@ -1362,6 +1342,17 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn [self.delegate audioPlayer:self didFinishBufferingSourceWithQueueItemId:queueItemId]; }]; + if (currentlyReadingEntry->framesQueued == 0) + { + NSLog(@"EOF A"); + } + else + { + NSLog(@"EOF B"); + } + + currentlyReadingEntry->lastFrameQueued = currentlyReadingEntry->framesQueued; + OSSpinLockLock(¤tEntryReferencesLock); currentlyReadingEntry = nil; OSSpinLockUnlock(¤tEntryReferencesLock); @@ -1579,6 +1570,13 @@ BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* classDesc OSStatus status; Boolean writable; UInt32 cookieSize; + + if (memcmp(asbd, &audioConverterAudioStreamBasicDescription, sizeof(AudioStreamBasicDescription)) == 0) + { + AudioConverterReset(audioConverterRef); + + return; + } [self destroyAudioConverter]; @@ -1723,6 +1721,8 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu -(void) handleAudioPackets:(const void*)inputData numberBytes:(UInt32)numberBytes numberPackets:(UInt32)numberPackets packetDescriptions:(AudioStreamPacketDescription*)packetDescriptionsIn { + NSLog(@"handleAudio"); + if (currentlyReadingEntry == nil) { return; @@ -1792,7 +1792,12 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu break; } - if (disposeWasRequested || seekToTimeWasRequested || self.internalState == STKAudioPlayerInternalStateStopped || self.internalState == STKAudioPlayerInternalStateStopping || self.internalState == STKAudioPlayerInternalStateDisposed) + if (disposeWasRequested + || seekToTimeWasRequested + || self.internalState == STKAudioPlayerInternalStateStopped + || self.internalState == STKAudioPlayerInternalStateStopping + || self.internalState == STKAudioPlayerInternalStateDisposed + || self.internalState == STKAudioPlayerInternalStatePendingNext) { pthread_mutex_unlock(&playerMutex); @@ -1936,6 +1941,7 @@ static OSStatus playbackCallback(void* inRefCon, AudioUnitRenderActionFlags* ioA OSSpinLockLock(&audioPlayer->pcmBufferSpinLock); + STKQueueEntry* entry = audioPlayer->currentlyPlayingEntry; AudioBuffer* audioBuffer = audioPlayer->pcmAudioBuffer; UInt32 frameSizeInBytes = audioPlayer->pcmBufferFrameSizeInBytes; UInt32 used = audioPlayer->pcmBufferUsedFrameCount; @@ -2022,8 +2028,19 @@ static OSStatus playbackCallback(void* inRefCon, AudioUnitRenderActionFlags* ioA audioPlayer.internalState = STKAudioPlayerInternalStatePlaying; } + UInt32 extraFramesPlayedNotAssigned = 0; + OSSpinLockLock(&audioPlayer->pcmBufferSpinLock); - audioPlayer->currentlyPlayingEntry->framesPlayed += totalFramesCopied; + UInt32 framesPlayedForCurrent = totalFramesCopied; + + if (entry->lastFrameQueued > 0) + { + framesPlayedForCurrent = MIN(entry->lastFrameQueued - entry->framesPlayed, framesPlayedForCurrent); + } + + entry->framesPlayed += framesPlayedForCurrent; + extraFramesPlayedNotAssigned = totalFramesCopied - framesPlayedForCurrent; + OSSpinLockUnlock(&audioPlayer->pcmBufferSpinLock); if (totalFramesCopied < inNumberFrames) @@ -2039,9 +2056,41 @@ static OSStatus playbackCallback(void* inRefCon, AudioUnitRenderActionFlags* ioA } } - if (signal) + BOOL lastFramePlayed = entry->framesPlayed == entry->lastFrameQueued; + + if (signal || lastFramePlayed) { pthread_mutex_lock(&audioPlayer->playerMutex); + + if (lastFramePlayed && entry == audioPlayer->currentlyPlayingEntry) + { + [audioPlayer audioQueueFinishedPlaying:entry]; + + while (extraFramesPlayedNotAssigned > 0) + { + STKQueueEntry* newEntry = audioPlayer->currentlyPlayingEntry; + + if (entry != nil) + { + UInt32 framesPlayedForCurrent = extraFramesPlayedNotAssigned; + + if (newEntry->lastFrameQueued > 0) + { + framesPlayedForCurrent = MIN(newEntry->lastFrameQueued - newEntry->framesPlayed, framesPlayedForCurrent); + } + + entry->framesPlayed += framesPlayedForCurrent; + + if (newEntry->framesPlayed == newEntry->lastFrameQueued) + { + [audioPlayer audioQueueFinishedPlaying:newEntry]; + } + + extraFramesPlayedNotAssigned -= framesPlayedForCurrent; + } + } + } + pthread_cond_signal(&audioPlayer->playerThreadReadyCondition); pthread_mutex_unlock(&audioPlayer->playerMutex); } diff --git a/StreamingKit/StreamingKit/STKQueueEntry.h b/StreamingKit/StreamingKit/STKQueueEntry.h index ec655f4..ef030de 100644 --- a/StreamingKit/StreamingKit/STKQueueEntry.h +++ b/StreamingKit/StreamingKit/STKQueueEntry.h @@ -16,12 +16,14 @@ double sampleRate; double lastProgress; double packetDuration; - Float64 framesQueued; - Float64 framesPlayed; + int framesQueued; + int framesPlayed; + Float64 lastFrameQueued; UInt64 audioDataOffset; UInt64 audioDataByteCount; UInt32 packetBufferSize; volatile BOOL cancel; + volatile BOOL finished; volatile int processedPacketsCount; volatile int processedPacketsSizeTotal; AudioStreamBasicDescription audioStreamBasicDescription; @@ -38,6 +40,7 @@ @property (readonly) UInt64 audioDataLengthInBytes; -(double) duration; +-(Float64) progressInFrames; -(double) calculatedBitRate; -(void) updateAudioDataSource; -(BOOL) isDefinitelyCompatible:(AudioStreamBasicDescription*)basicDescription; diff --git a/StreamingKit/StreamingKit/STKQueueEntry.m b/StreamingKit/StreamingKit/STKQueueEntry.m index 199b930..66bb25b 100644 --- a/StreamingKit/StreamingKit/STKQueueEntry.m +++ b/StreamingKit/StreamingKit/STKQueueEntry.m @@ -21,6 +21,7 @@ self.queueItemId = queueItemIdIn; self.lastFrameIndex = -1; self.lastByteIndex = -1; + self->lastFrameQueued = -1; } return self; @@ -146,6 +147,11 @@ return memcmp(&(self->audioStreamBasicDescription), basicDescription, sizeof(*basicDescription)) != 0; } +-(Float64) progressInFrames +{ + return self.seekTime + self->framesPlayed; +} + -(NSString*) description { return [[self queueItemId] description]; From ddc6cf4b2f27a28a26afc070e86baa2239d68e04 Mon Sep 17 00:00:00 2001 From: Thong Nguyen Date: Fri, 31 Jan 2014 15:12:30 +0000 Subject: [PATCH 14/33] Fixed newly set datasource is not immediately loaded because render callback changes resets the STKAudioPlayerInternalStatePendingNext state --- StreamingKit/StreamingKit/STKAudioPlayer.m | 31 +++++++++------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index 5750e30..cbfa4e4 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -42,6 +42,7 @@ #define STK_DEFAULT_PCM_BUFFER_SIZE_IN_SECONDS (15) #define STK_DEFAULT_SECONDS_REQUIRED_TO_START_PLAYING (0) +#define STK_MAX_COMPRESSED_PACKETS_FOR_BITRATE_CALCULATION (2048) #define STK_BUFFERS_NEEDED_TO_START (32) #define STK_BUFFERS_NEEDED_WHEN_UNDERUNNING (128) @@ -470,8 +471,6 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn { OSStatus error; - NSLog(@"Handle property change"); - if (!currentlyReadingEntry) { return; @@ -1017,7 +1016,6 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn self.internalState = STKAudioPlayerInternalStateWaitingForDataAfterSeek; [self setCurrentlyReadingEntry:currentlyPlayingEntry andStartPlaying:YES]; - } else if (self.internalState == STKAudioPlayerInternalStateStopped && (stopReason == AudioPlayerStopReasonUserAction)) { @@ -1247,8 +1245,6 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn int read = [currentlyReadingEntry.dataSource readIntoBuffer:readBuffer withSize:readBufferSize]; - NSLog(@"dataAvailble read == %d", read); - if (read == 0) { return; @@ -1342,15 +1338,6 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn [self.delegate audioPlayer:self didFinishBufferingSourceWithQueueItemId:queueItemId]; }]; - if (currentlyReadingEntry->framesQueued == 0) - { - NSLog(@"EOF A"); - } - else - { - NSLog(@"EOF B"); - } - currentlyReadingEntry->lastFrameQueued = currentlyReadingEntry->framesQueued; OSSpinLockLock(¤tEntryReferencesLock); @@ -1721,8 +1708,6 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu -(void) handleAudioPackets:(const void*)inputData numberBytes:(UInt32)numberBytes numberPackets:(UInt32)numberPackets packetDescriptions:(AudioStreamPacketDescription*)packetDescriptionsIn { - NSLog(@"handleAudio"); - if (currentlyReadingEntry == nil) { return; @@ -1754,9 +1739,11 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu convertInfo.audioBuffer.mDataByteSize = numberBytes; convertInfo.audioBuffer.mNumberChannels = audioConverterAudioStreamBasicDescription.mChannelsPerFrame; - if (currentlyReadingEntry->processedPacketsCount < 1024) + if (currentlyReadingEntry->processedPacketsCount < STK_MAX_COMPRESSED_PACKETS_FOR_BITRATE_CALCULATION) { - for (int i = 0; i < numberPackets && currentlyReadingEntry->processedPacketsCount < 1000; i++) + int count = MIN(numberPackets, STK_MAX_COMPRESSED_PACKETS_FOR_BITRATE_CALCULATION - currentlyReadingEntry->processedPacketsCount); + + for (int i = 0; i < count; i++) { SInt64 packetSize = packetDescriptionsIn[i].mDataByteSize; @@ -1966,6 +1953,12 @@ static OSStatus playbackCallback(void* inRefCon, AudioUnitRenderActionFlags* ioA waitForBuffer = YES; } } + else if (state == STKAudioPlayerInternalStatePendingNext) + { + OSSpinLockUnlock(&audioPlayer->pcmBufferSpinLock); + + return 0; + } OSSpinLockUnlock(&audioPlayer->pcmBufferSpinLock); @@ -2079,7 +2072,7 @@ static OSStatus playbackCallback(void* inRefCon, AudioUnitRenderActionFlags* ioA framesPlayedForCurrent = MIN(newEntry->lastFrameQueued - newEntry->framesPlayed, framesPlayedForCurrent); } - entry->framesPlayed += framesPlayedForCurrent; + newEntry->framesPlayed += framesPlayedForCurrent; if (newEntry->framesPlayed == newEntry->lastFrameQueued) { From a0a955363169a0535279b9bb2d68c68b738fb3ce Mon Sep 17 00:00:00 2001 From: Thong Nguyen Date: Fri, 31 Jan 2014 20:20:20 +0000 Subject: [PATCH 15/33] Big refactor to remove remanents of AudioQueue code. Added safety locks when working with 64bit floats and ints across threads --- ExampleApp/ExampleApp/AppDelegate.m | 6 +- ExampleApp/ExampleApp/AudioPlayerView.m | 6 +- .../NSMutableArray+STKAudioPlayer.h | 1 + .../NSMutableArray+STKAudioPlayer.m | 18 +- StreamingKit/StreamingKit/STKAudioPlayer.h | 31 +- StreamingKit/StreamingKit/STKAudioPlayer.m | 511 ++++++++++-------- StreamingKit/StreamingKit/STKQueueEntry.h | 31 +- StreamingKit/StreamingKit/STKQueueEntry.m | 60 +- 8 files changed, 345 insertions(+), 319 deletions(-) diff --git a/ExampleApp/ExampleApp/AppDelegate.m b/ExampleApp/ExampleApp/AppDelegate.m index bbc64bf..cf44a16 100644 --- a/ExampleApp/ExampleApp/AppDelegate.m +++ b/ExampleApp/ExampleApp/AppDelegate.m @@ -57,7 +57,7 @@ { NSURL* url = [NSURL URLWithString:@"http://fs.bloom.fm/oss/audiosamples/sample.mp3"]; - STKAutoRecoveringHTTPDataSource* dataSource = [[STKAutoRecoveringHTTPDataSource alloc] initWithHTTPDataSource:(STKHTTPDataSource*)[audioPlayer dataSourceFromURL:url]]; + STKAutoRecoveringHTTPDataSource* dataSource = [[STKAutoRecoveringHTTPDataSource alloc] initWithHTTPDataSource:(STKHTTPDataSource*)[STKAudioPlayer dataSourceFromURL:url]]; [audioPlayer setDataSource:dataSource withQueueItemId:[[SampleQueueId alloc] initWithUrl:url andCount:0]]; } @@ -67,7 +67,7 @@ NSString* path = [[NSBundle mainBundle] pathForResource:@"airplane" ofType:@"aac"]; NSURL* url = [NSURL fileURLWithPath:path]; - [audioPlayer queueDataSource:[audioPlayer dataSourceFromURL:url] withQueueItemId:[[SampleQueueId alloc] initWithUrl:url andCount:0]]; + [audioPlayer queueDataSource:[STKAudioPlayer dataSourceFromURL:url] withQueueItemId:[[SampleQueueId alloc] initWithUrl:url andCount:0]]; } -(void) audioPlayerViewPlayFromLocalFileSelected:(AudioPlayerView*)audioPlayerView @@ -75,7 +75,7 @@ NSString* path = [[NSBundle mainBundle] pathForResource:@"sample" ofType:@"m4a"]; NSURL* url = [NSURL fileURLWithPath:path]; - [audioPlayer setDataSource:[audioPlayer dataSourceFromURL:url] withQueueItemId:[[SampleQueueId alloc] initWithUrl:url andCount:0]]; + [audioPlayer setDataSource:[STKAudioPlayer dataSourceFromURL:url] withQueueItemId:[[SampleQueueId alloc] initWithUrl:url andCount:0]]; } @end diff --git a/ExampleApp/ExampleApp/AudioPlayerView.m b/ExampleApp/ExampleApp/AudioPlayerView.m index c596b1d..e844b24 100644 --- a/ExampleApp/ExampleApp/AudioPlayerView.m +++ b/ExampleApp/ExampleApp/AudioPlayerView.m @@ -181,8 +181,8 @@ -(void) disposeButtonPressed { - [audioPlayer dispose]; - audioPlayer = nil; + [audioPlayer stop]; +// audioPlayer = nil; } -(void) playButtonPressed @@ -283,7 +283,7 @@ NSLog(@"Requeuing: %@", [queueId.url description]); - [self->audioPlayer queueDataSource:[self->audioPlayer dataSourceFromURL:queueId.url] withQueueItemId:[[SampleQueueId alloc] initWithUrl:queueId.url andCount:queueId.count + 1]]; + [self->audioPlayer queueDataSource:[STKAudioPlayer dataSourceFromURL:queueId.url] withQueueItemId:[[SampleQueueId alloc] initWithUrl:queueId.url andCount:queueId.count + 1]]; } } diff --git a/StreamingKit/StreamingKit/NSMutableArray+STKAudioPlayer.h b/StreamingKit/StreamingKit/NSMutableArray+STKAudioPlayer.h index c3644b4..12ca839 100644 --- a/StreamingKit/StreamingKit/NSMutableArray+STKAudioPlayer.h +++ b/StreamingKit/StreamingKit/NSMutableArray+STKAudioPlayer.h @@ -11,6 +11,7 @@ @interface NSMutableArray (STKAudioPlayer) -(void) enqueue:(id)obj; -(void) skipQueue:(id)obj; +-(void) skipQueueWithQueue:(NSMutableArray*)queue; -(id) dequeue; -(id) peek; @end diff --git a/StreamingKit/StreamingKit/NSMutableArray+STKAudioPlayer.m b/StreamingKit/StreamingKit/NSMutableArray+STKAudioPlayer.m index c339053..493868f 100644 --- a/StreamingKit/StreamingKit/NSMutableArray+STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/NSMutableArray+STKAudioPlayer.m @@ -20,6 +20,14 @@ [self addObject:obj]; } +-(void) skipQueueWithQueue:(NSMutableArray*)queue +{ + for (id item in queue) + { + [self addObject:item]; + } +} + -(id) dequeue { if ([self count] == 0) @@ -39,14 +47,4 @@ return [self lastObject]; } --(id) peekRecent -{ - if (self.count == 0) - { - return nil; - } - - return [self objectAtIndex:0]; -} - @end diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.h b/StreamingKit/StreamingKit/STKAudioPlayer.h index 8deeff1..2c6e063 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.h +++ b/StreamingKit/StreamingKit/STKAudioPlayer.h @@ -55,7 +55,6 @@ typedef enum /* Same as STKAudioPlayerInternalStateWaitingForData but isn't immediately raised as a buffering event */ STKAudioPlayerInternalStateWaitingForDataAfterSeek = (1 << 5) | STKAudioPlayerInternalStateRunning, STKAudioPlayerInternalStatePaused = (1 << 6) | STKAudioPlayerInternalStateRunning, - STKAudioPlayerInternalStateStopping = (1 << 8), STKAudioPlayerInternalStateStopped = (1 << 9), STKAudioPlayerInternalStatePendingNext = (1 << 10), STKAudioPlayerInternalStateDisposed = (1 << 30), @@ -78,9 +77,9 @@ STKAudioPlayerState; typedef enum { - AudioPlayerStopReasonNoStop = 0, - AudioPlayerStopReasonEof, - AudioPlayerStopReasonUserAction + STKAudioPlayerStopReasonNoStop = 0, + STKAudioPlayerStopReasonEof, + STKAudioPlayerStopReasonUserAction } STKAudioPlayerStopReason; @@ -89,12 +88,9 @@ typedef enum STKAudioPlayerErrorNone = 0, STKAudioPlayerErrorDataSource, STKAudioPlayerErrorStreamParseBytesFailed, + STKAudioPlayerErrorAudioSystemError, + STKAudioPlayerErrorCodecError, STKAudioPlayerErrorDataNotFound, - STKAudioPlayerErrorQueueStartFailed, - STKAudioPlayerErrorQueuePauseFailed, - STKAudioPlayerErrorUnknownBuffer, - STKAudioPlayerErrorQueueStopFailed, - STKAudioPlayerErrorQueueCreationFailed, STKAudioPlayerErrorOther = -1 } STKAudioPlayerErrorCode; @@ -125,20 +121,35 @@ STKAudioPlayerErrorCode; -(id) init; -(id) initWithReadBufferSize:(int)readBufferSizeIn; --(STKDataSource*) dataSourceFromURL:(NSURL*)url; + ++(STKDataSource*) dataSourceFromURL:(NSURL*)url; +/// Plays an item from the given URL (all pending queued items are removed) -(void) play:(NSString*)urlString; +/// Plays an item from the given URL (all pending queued items are removed) -(void) playWithURL:(NSURL*)url; +/// Plays the given item (all pending queued items are removed) -(void) playWithDataSource:(STKDataSource*)dataSource; +/// Queues a DataSource with the given Item ID for playback -(void) queueDataSource:(STKDataSource*)dataSource withQueueItemId:(NSObject*)queueItemId; +/// Plays the given item (all pending queued items are removed) -(void) setDataSource:(STKDataSource*)dataSourceIn withQueueItemId:(NSObject*)queueItemId; +/// Seeks to a specific time (in seconds) -(void) seekToTime:(double)value; +/// Clears any upcoming items already queued for playback (does not stop the current item) -(void) clearQueue; +/// Pauses playback -(void) pause; +/// Resumes playback from pause -(void) resume; +/// Stops playback of the current file, flushes all the buffers and removes any pending queued items -(void) stop; +/// Mutes playback -(void) mute; +/// Unmutes playback -(void) unmute; +/// Disposes the STKAudioPlayer and frees up all resources before returning -(void) dispose; +/// The QueueItemId of the currently playing item -(NSObject*) currentlyPlayingQueueItemId; @end diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index cbfa4e4..210d5ef 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -40,7 +40,7 @@ #import "NSMutableArray+STKAudioPlayer.h" #import "libkern/OSAtomic.h" -#define STK_DEFAULT_PCM_BUFFER_SIZE_IN_SECONDS (15) +#define STK_DEFAULT_PCM_BUFFER_SIZE_IN_SECONDS (65) #define STK_DEFAULT_SECONDS_REQUIRED_TO_START_PLAYING (0) #define STK_MAX_COMPRESSED_PACKETS_FOR_BITRATE_CALCULATION (2048) @@ -57,20 +57,18 @@ UInt8* readBuffer; int readBufferSize; + AudioComponentInstance audioUnit; + UInt32 framesRequiredToStartPlaying; UInt32 framesRequiredToPlayAfterRebuffering; - AudioComponentInstance audioUnit; - STKQueueEntry* volatile currentlyPlayingEntry; STKQueueEntry* volatile currentlyReadingEntry; NSMutableArray* upcomingQueue; NSMutableArray* bufferingQueue; - volatile BOOL buffering; OSSpinLock pcmBufferSpinLock; - int32_t rebufferingStartFrames; volatile UInt32 pcmBufferTotalFrameCount; volatile UInt32 pcmBufferFrameStartIndex; volatile UInt32 pcmBufferUsedFrameCount; @@ -78,29 +76,22 @@ AudioBuffer* pcmAudioBuffer; AudioBufferList pcmAudioBufferList; - AudioConverterRef audioConverterRef; AudioStreamBasicDescription canonicalAudioStreamBasicDescription; AudioStreamBasicDescription audioConverterAudioStreamBasicDescription; + BOOL discontinuous; NSThread* playbackThread; NSRunLoop* playbackThreadRunLoop; + AudioFileStreamID audioFileStream; NSConditionLock* threadStartedLock; NSConditionLock* threadFinishedCondLock; - Float64 averageHardwareDelay; - - AudioFileStreamID audioFileStream; - - BOOL discontinuous; #if TARGET_OS_IPHONE UIBackgroundTaskIdentifier backgroundTaskId; #endif - STKAudioPlayerErrorCode errorCode; - STKAudioPlayerStopReason stopReason; - int32_t seekVersion; OSSpinLock seekLock; OSSpinLock currentEntryReferencesLock; @@ -111,9 +102,10 @@ pthread_cond_t mainThreadSyncCallReadyCondition; volatile BOOL waiting; + volatile double requestedSeekTime; volatile BOOL disposeWasRequested; volatile BOOL seekToTimeWasRequested; - volatile double requestedSeekTime; + volatile STKAudioPlayerStopReason stopReason; } @property (readwrite) STKAudioPlayerInternalState internalState; @@ -180,7 +172,6 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn case STKAudioPlayerInternalStateWaitingForData: newState = STKAudioPlayerStateBuffering; break; - case STKAudioPlayerInternalStateStopping: case STKAudioPlayerInternalStateStopped: newState = STKAudioPlayerStateStopped; break; @@ -248,8 +239,10 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn canonicalAudioStreamBasicDescription.mBytesPerPacket = canonicalAudioStreamBasicDescription.mBytesPerFrame * canonicalAudioStreamBasicDescription.mFramesPerPacket; framesRequiredToStartPlaying = canonicalAudioStreamBasicDescription.mSampleRate * STK_DEFAULT_SECONDS_REQUIRED_TO_START_PLAYING; + framesRequiredToPlayAfterRebuffering = canonicalAudioStreamBasicDescription.mSampleRate * STK_DEFAULT_PCM_BUFFER_SIZE_IN_SECONDS; pcmAudioBuffer = &pcmAudioBufferList.mBuffers[0]; + pcmAudioBufferList.mNumberBuffers = 1; pcmAudioBufferList.mBuffers[0].mDataByteSize = (canonicalAudioStreamBasicDescription.mSampleRate * STK_DEFAULT_PCM_BUFFER_SIZE_IN_SECONDS) * canonicalAudioStreamBasicDescription.mBytesPerFrame; pcmAudioBufferList.mBuffers[0].mData = (void*)calloc(pcmAudioBuffer->mDataByteSize, 1); @@ -291,29 +284,37 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn if (currentlyReadingEntry) { currentlyReadingEntry.dataSource.delegate = nil; + [currentlyReadingEntry.dataSource unregisterForEvents]; } if (currentlyPlayingEntry) { currentlyPlayingEntry.dataSource.delegate = nil; + [currentlyReadingEntry.dataSource unregisterForEvents]; } - pthread_mutex_destroy(&playerMutex); - pthread_mutex_destroy(&mainThreadSyncCallMutex); - pthread_cond_destroy(&playerThreadReadyCondition); - pthread_cond_destroy(&mainThreadSyncCallReadyCondition); + [self stopAudioUnitWithReason:STKAudioPlayerStopReasonEof]; - if (audioFileStream) { AudioFileStreamClose(audioFileStream); } + if (audioConverterRef) + { + AudioConverterDispose(audioConverterRef); + } + if (audioUnit) { AudioComponentInstanceDispose(audioUnit); } + pthread_mutex_destroy(&playerMutex); + pthread_mutex_destroy(&mainThreadSyncCallMutex); + pthread_cond_destroy(&playerThreadReadyCondition); + pthread_cond_destroy(&mainThreadSyncCallReadyCondition); + free(readBuffer); } @@ -354,7 +355,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn #endif } --(STKDataSource*) dataSourceFromURL:(NSURL*)url ++(STKDataSource*) dataSourceFromURL:(NSURL*)url { STKDataSource* retval; @@ -371,52 +372,52 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn } -(void) clearQueue -{ - [self clearQueueIncludingUpcoming:YES]; -} - --(void) clearQueueIncludingUpcoming:(BOOL)includeUpcoming { pthread_mutex_lock(&playerMutex); { - NSMutableArray* array = [[NSMutableArray alloc] initWithCapacity:bufferingQueue.count + (includeUpcoming ? upcomingQueue.count : 0)]; - - STKQueueEntry* entry = [bufferingQueue dequeue]; - - if (entry && entry != currentlyPlayingEntry) + if ([self.delegate respondsToSelector:@selector(audioPlayer:didCancelQueuedItems:)]) { - [array addObject:[entry queueItemId]]; - } - - while (bufferingQueue.count > 0) - { - id queueItemId = [[bufferingQueue dequeue] queueItemId]; + NSMutableArray* array = [[NSMutableArray alloc] initWithCapacity:bufferingQueue.count + upcomingQueue.count]; - if (queueItemId != nil) + STKQueueEntry* entry = [bufferingQueue dequeue]; + + if (entry && entry != currentlyPlayingEntry) { - [array addObject:queueItemId]; + [array addObject:[entry queueItemId]]; } - } - - if (includeUpcoming) - { + + while (bufferingQueue.count > 0) + { + id queueItemId = [[bufferingQueue dequeue] queueItemId]; + + if (queueItemId != nil) + { + [array addObject:queueItemId]; + } + } + for (STKQueueEntry* entry in upcomingQueue) { [array addObject:entry.queueItemId]; } [upcomingQueue removeAllObjects]; - } - - if (array.count > 0) - { - [self playbackThreadQueueMainThreadSyncBlock:^ + + if (array.count > 0) { - if ([self.delegate respondsToSelector:@selector(audioPlayer:didCancelQueuedItems:)]) + [self playbackThreadQueueMainThreadSyncBlock:^ { - [self.delegate audioPlayer:self didCancelQueuedItems:array]; - } - }]; + if ([self.delegate respondsToSelector:@selector(audioPlayer:didCancelQueuedItems:)]) + { + [self.delegate audioPlayer:self didCancelQueuedItems:array]; + } + }]; + } + } + else + { + [bufferingQueue removeAllObjects]; + [upcomingQueue removeAllObjects]; } } pthread_mutex_unlock(&playerMutex); @@ -426,12 +427,12 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn { NSURL* url = [NSURL URLWithString:urlString]; - [self setDataSource:[self dataSourceFromURL:url] withQueueItemId:urlString]; + [self setDataSource:[STKAudioPlayer dataSourceFromURL:url] withQueueItemId:urlString]; } -(void) playWithURL:(NSURL*)url { - [self setDataSource:[self dataSourceFromURL:url] withQueueItemId:url]; + [self setDataSource:[STKAudioPlayer dataSourceFromURL:url] withQueueItemId:url]; } -(void) playWithDataSource:(STKDataSource*)dataSource @@ -446,6 +447,8 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn LOGINFO(([NSString stringWithFormat:@"Playing: %@", [queueItemId description]])); [self startSystemBackgroundTask]; + + [self clearQueue]; [upcomingQueue enqueue:[[STKQueueEntry alloc] initWithDataSource:dataSourceIn andQueueItemId:queueItemId]]; @@ -461,6 +464,11 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn pthread_mutex_lock(&playerMutex); { [upcomingQueue enqueue:[[STKQueueEntry alloc] initWithDataSource:dataSourceIn andQueueItemId:queueItemId]]; + + if (self.internalState == STKAudioPlayerInternalStateStopped && stopReason == STKAudioPlayerStopReasonEof) + { + [self startAudioUnit]; + } } pthread_mutex_unlock(&playerMutex); @@ -506,7 +514,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn AudioStreamBasicDescription newBasicDescription; STKQueueEntry* entryToUpdate = currentlyReadingEntry; - if (currentlyReadingEntry->audioStreamBasicDescription.mSampleRate == 0) + if (!currentlyReadingEntry->parsedHeader) { UInt32 size = sizeof(newBasicDescription); @@ -627,12 +635,11 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn -(void) unexpectedError:(STKAudioPlayerErrorCode)errorCodeIn { - errorCode = errorCodeIn; self.internalState = STKAudioPlayerInternalStateError; [self playbackThreadQueueMainThreadSyncBlock:^ { - [self.delegate audioPlayer:self unexpectedError:errorCode]; + [self.delegate audioPlayer:self unexpectedError:errorCodeIn]; }]; } @@ -687,7 +694,9 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn return 0; } - double retval = entry.seekTime + (entry->framesPlayed / canonicalAudioStreamBasicDescription.mSampleRate); + OSSpinLockLock(&entry->spinLock); + double retval = entry->seekTime + (entry->framesPlayed / canonicalAudioStreamBasicDescription.mSampleRate); + OSSpinLockUnlock(&entry->spinLock); return retval; } @@ -787,12 +796,23 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn NSAssert(playbackThreadRunLoop != nil, @"playbackThreadRunLoop != nil"); } +-(void) audioQueueFinishedPlaying:(STKQueueEntry*)entry +{ + STKQueueEntry* next = [bufferingQueue dequeue]; + + [self processFinishPlayingIfAnyAndPlayingNext:entry withNext:next]; + [self processRunloop]; +} + -(void) setCurrentlyReadingEntry:(STKQueueEntry*)entry andStartPlaying:(BOOL)startPlaying +{ + [self setCurrentlyReadingEntry:entry andStartPlaying:startPlaying clearQueue:YES]; +} + +-(void) setCurrentlyReadingEntry:(STKQueueEntry*)entry andStartPlaying:(BOOL)startPlaying clearQueue:(BOOL)clearQueue { LOGINFO(([entry description])); - - NSAssert([NSThread currentThread] == playbackThread, @"[NSThread currentThread] == playbackThread"); - + if (startPlaying) { memset(&pcmAudioBuffer->mData[0], 0, pcmBufferTotalFrameCount * pcmBufferFrameSizeInBytes); @@ -805,8 +825,6 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn audioFileStream = 0; } - STKQueueEntry* originalReadingEntry = currentlyReadingEntry; - if (currentlyReadingEntry) { currentlyReadingEntry.dataSource.delegate = nil; @@ -818,25 +836,17 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn currentlyReadingEntry = entry; OSSpinLockUnlock(¤tEntryReferencesLock); - if (originalReadingEntry != currentlyReadingEntry) - { - if ([currentlyReadingEntry isDefinitelyCompatible:&audioConverterAudioStreamBasicDescription]) - { - AudioConverterReset(audioConverterRef); - } - else if (currentlyReadingEntry->parsedHeader) - { - [self createAudioConverter:¤tlyReadingEntry->audioStreamBasicDescription]; - } - } - currentlyReadingEntry.dataSource.delegate = self; [currentlyReadingEntry.dataSource registerForEvents:[NSRunLoop currentRunLoop]]; [currentlyReadingEntry.dataSource seekToOffset:0]; if (startPlaying) { - [self clearQueue]; + if (clearQueue) + { + [self clearQueue]; + } + [self processFinishPlayingIfAnyAndPlayingNext:currentlyPlayingEntry withNext:entry]; [self startAudioUnit]; } @@ -846,21 +856,6 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn } } --(void) audioQueueFinishedPlaying:(STKQueueEntry*)entry -{ - STKQueueEntry* next = [bufferingQueue peek]; - - if (next == nil) - { - [self processRunloop]; - } - - next = [bufferingQueue dequeue]; - - [self processFinishPlayingIfAnyAndPlayingNext:entry withNext:next]; - [self processRunloop]; -} - -(void) processFinishPlayingIfAnyAndPlayingNext:(STKQueueEntry*)entry withNext:(STKQueueEntry*)next { if (entry != currentlyPlayingEntry) @@ -880,7 +875,9 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn { if (!isPlayingSameItemProbablySeek) { - next.seekTime = 0; + OSSpinLockLock(&next->spinLock); + next->seekTime = 0; + OSSpinLockUnlock(&next->spinLock); OSSpinLockLock(&seekLock); seekToTimeWasRequested = NO; @@ -889,8 +886,6 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn OSSpinLockLock(¤tEntryReferencesLock); currentlyPlayingEntry = next; - currentlyPlayingEntry.bytesBuffered = 0; - currentlyPlayingEntry.firstFrameIndex = [self currentTimeInFrames]; NSObject* playingQueueItemId = playingQueueItemId = currentlyPlayingEntry.queueItemId; OSSpinLockUnlock(¤tEntryReferencesLock); @@ -918,15 +913,6 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn currentlyPlayingEntry = nil; OSSpinLockUnlock(¤tEntryReferencesLock); - if (currentlyReadingEntry == nil) - { - if (upcomingQueue.count == 0) - { - stopReason = AudioPlayerStopReasonEof; - self.internalState = STKAudioPlayerInternalStateStopping; - } - } - if (!isPlayingSameItemProbablySeek && entry) { [self playbackThreadQueueMainThreadSyncBlock:^ @@ -934,7 +920,17 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn [self.delegate audioPlayer:self didFinishPlayingQueueItemId:queueItemId withReason:stopReason andProgress:progress andDuration:duration]; }]; } + + if (currentlyReadingEntry == nil) + { + if (upcomingQueue.count == 0) + { + [self stopAudioUnitWithReason:STKAudioPlayerStopReasonEof]; + } + } } + + [self wakeupPlaybackThread]; } -(void) dispatchSyncOnMainThread:(void(^)())block @@ -989,6 +985,23 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn }]; } +-(void) requeueBufferingEntries +{ + if (bufferingQueue.count > 0) + { + for (STKQueueEntry* queueEntry in bufferingQueue) + { + queueEntry->parsedHeader = NO; + + [queueEntry reset]; + } + + [upcomingQueue skipQueueWithQueue:bufferingQueue]; + + [bufferingQueue removeAllObjects]; + } +} + -(BOOL) processRunloop { pthread_mutex_lock(&playerMutex); @@ -1011,42 +1024,21 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn else if (seekToTimeWasRequested && currentlyPlayingEntry && currentlyPlayingEntry != currentlyReadingEntry) { currentlyPlayingEntry->parsedHeader = NO; - currentlyPlayingEntry.lastByteIndex = -1; - currentlyPlayingEntry.lastFrameIndex = -1; + [currentlyPlayingEntry reset]; + + if (currentlyReadingEntry != nil) + { + currentlyReadingEntry.dataSource.delegate = nil; + [currentlyReadingEntry.dataSource unregisterForEvents]; + } + + [self requeueBufferingEntries]; self.internalState = STKAudioPlayerInternalStateWaitingForDataAfterSeek; - [self setCurrentlyReadingEntry:currentlyPlayingEntry andStartPlaying:YES]; - } - else if (self.internalState == STKAudioPlayerInternalStateStopped && (stopReason == AudioPlayerStopReasonUserAction)) - { - [self stopAudioUnitWithReason:@"from processRunLoop/1"]; - - currentlyReadingEntry.dataSource.delegate = nil; - [currentlyReadingEntry.dataSource unregisterForEvents]; - [currentlyReadingEntry.dataSource close]; - - if (currentlyPlayingEntry) - { - [self processFinishPlayingIfAnyAndPlayingNext:currentlyPlayingEntry withNext:nil]; - } - - if ([bufferingQueue peek] == currentlyPlayingEntry) - { - [bufferingQueue dequeue]; - } - - OSSpinLockLock(¤tEntryReferencesLock); - currentlyPlayingEntry = nil; - currentlyReadingEntry = nil; - seekToTimeWasRequested = NO; - OSSpinLockUnlock(¤tEntryReferencesLock); + [self setCurrentlyReadingEntry:currentlyPlayingEntry andStartPlaying:YES clearQueue:NO]; } else if (currentlyReadingEntry == nil) { - STKQueueEntry* next; - - next = [upcomingQueue peek]; - if (upcomingQueue.count > 0) { STKQueueEntry* entry = [upcomingQueue dequeue]; @@ -1060,8 +1052,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn { if (self.internalState != STKAudioPlayerInternalStateStopped) { - [self stopAudioUnitWithReason:@"from processRunLoop/2"]; - stopReason = AudioPlayerStopReasonEof; + [self stopAudioUnitWithReason:STKAudioPlayerStopReasonEof]; } } } @@ -1155,9 +1146,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn -(void) processSeekToTime { OSStatus error; - OSSpinLockLock(¤tEntryReferencesLock); STKQueueEntry* currentEntry = currentlyReadingEntry; - OSSpinLockUnlock(¤tEntryReferencesLock); NSAssert(currentEntry == currentlyPlayingEntry, @"playing and reading must be the same"); @@ -1166,8 +1155,6 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn return; } - NSLog(@"Seek to time: %f", requestedSeekTime); - long long seekByteOffset = currentEntry->audioDataOffset + (requestedSeekTime / self.duration) * (currentlyReadingEntry.audioDataLengthInBytes); if (seekByteOffset > currentEntry.dataSource.length - (2 * currentEntry->packetBufferSize)) @@ -1175,8 +1162,9 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn seekByteOffset = currentEntry.dataSource.length - 2 * currentEntry->packetBufferSize; } - currentEntry.seekTime = requestedSeekTime; - currentEntry->lastProgress = requestedSeekTime; + OSSpinLockLock(¤tEntry->spinLock); + currentEntry->seekTime = requestedSeekTime; + OSSpinLockUnlock(¤tEntry->spinLock); double calculatedBitRate = [currentEntry calculatedBitRate]; @@ -1192,7 +1180,9 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn { double delta = ((seekByteOffset - (SInt64)currentEntry->audioDataOffset) - packetAlignedByteOffset) / calculatedBitRate * 8; - currentEntry.seekTime -= delta; + OSSpinLockLock(¤tEntry->spinLock); + currentEntry->seekTime -= delta; + OSSpinLockUnlock(¤tEntry->spinLock); seekByteOffset = packetAlignedByteOffset + currentEntry->audioDataOffset; } @@ -1203,14 +1193,9 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn AudioConverterReset(audioConverterRef); } - currentEntry.lastFrameIndex = -1; [currentEntry updateAudioDataSource]; - currentEntry.bytesBuffered = 0; - currentEntry->framesPlayed = 0; - currentEntry->framesQueued = 0; - currentEntry->lastFrameQueued = -1; - currentEntry.firstFrameIndex = [self currentTimeInFrames]; - currentEntry->finished = NO; + + [currentEntry reset]; [currentEntry.dataSource seekToOffset:seekByteOffset]; @@ -1225,8 +1210,6 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn { [self resetPcmBuffers]; } - - [self clearQueue]; } -(void) dataSourceDataAvailable:(STKDataSource*)dataSourceIn @@ -1256,26 +1239,12 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn if (error) { - NSLog(@"dataAvailbleError"); + [self unexpectedError:STKAudioPlayerErrorAudioSystemError]; return; } } - if (read < 0) - { - // iOS will shutdown network connections if the app is backgrounded (i.e. device is locked when player is paused) - // We try to reopen -- should probably add a back-off protocol in the future - - NSLog(@"dataAvailble read < 0"); - - long long position = currentlyReadingEntry.dataSource.position; - - [currentlyReadingEntry.dataSource seekToOffset:position]; - - return; - } - int flags = 0; if (discontinuous) @@ -1321,28 +1290,53 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn -(void) dataSourceEof:(STKDataSource*)dataSourceIn { - if (currentlyReadingEntry.dataSource != dataSourceIn) + if (currentlyReadingEntry == nil || currentlyReadingEntry.dataSource != dataSourceIn) { + dataSourceIn.delegate = nil; + [dataSourceIn unregisterForEvents]; + [dataSourceIn close]; + return; } - NSObject* queueItemId = currentlyReadingEntry.queueItemId; - if (disposeWasRequested) { return; } + NSObject* queueItemId = currentlyReadingEntry.queueItemId; + [self dispatchSyncOnMainThread:^ { [self.delegate audioPlayer:self didFinishBufferingSourceWithQueueItemId:queueItemId]; }]; + + pthread_mutex_lock(&playerMutex); + if (currentlyReadingEntry == nil) + { + dataSourceIn.delegate = nil; + [dataSourceIn unregisterForEvents]; + [dataSourceIn close]; + + return; + } + + OSSpinLockLock(¤tlyReadingEntry->spinLock); currentlyReadingEntry->lastFrameQueued = currentlyReadingEntry->framesQueued; + OSSpinLockUnlock(¤tlyReadingEntry->spinLock); + + currentlyReadingEntry.dataSource.delegate = nil; + [currentlyReadingEntry.dataSource unregisterForEvents]; + [currentlyReadingEntry.dataSource close]; OSSpinLockLock(¤tEntryReferencesLock); currentlyReadingEntry = nil; OSSpinLockUnlock(¤tEntryReferencesLock); + + pthread_mutex_unlock(&playerMutex); + + [self processRunloop]; } -(void) pause @@ -1362,7 +1356,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn if (error) { - [self unexpectedError:STKAudioPlayerErrorQueuePauseFailed]; + [self unexpectedError:STKAudioPlayerErrorAudioSystemError]; pthread_mutex_unlock(&playerMutex); @@ -1397,7 +1391,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn if (error) { - [self unexpectedError:STKAudioPlayerErrorQueueStartFailed]; + [self unexpectedError:STKAudioPlayerErrorAudioSystemError]; pthread_mutex_unlock(&playerMutex); @@ -1432,9 +1426,34 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn return; } - stopReason = AudioPlayerStopReasonUserAction; - self.internalState = STKAudioPlayerInternalStateStopped; + [self stopAudioUnitWithReason:STKAudioPlayerStopReasonUserAction]; + + [self resetPcmBuffers]; + [self invokeOnPlaybackThread:^ + { + pthread_mutex_lock(&playerMutex); + { + currentlyReadingEntry.dataSource.delegate = nil; + [currentlyReadingEntry.dataSource unregisterForEvents]; + [currentlyReadingEntry.dataSource close]; + + if (currentlyPlayingEntry) + { + [self processFinishPlayingIfAnyAndPlayingNext:currentlyPlayingEntry withNext:nil]; + } + + [self clearQueue]; + + OSSpinLockLock(¤tEntryReferencesLock); + currentlyPlayingEntry = nil; + currentlyReadingEntry = nil; + seekToTimeWasRequested = NO; + OSSpinLockUnlock(¤tEntryReferencesLock); + } + pthread_mutex_unlock(&playerMutex); + }]; + [self wakeupPlaybackThread]; } pthread_mutex_unlock(&playerMutex); @@ -1577,6 +1596,13 @@ BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* classDesc if (!audioConverterRef) { status = AudioConverterNew(asbd, &canonicalAudioStreamBasicDescription, &audioConverterRef); + + if (status) + { + [self unexpectedError:STKAudioPlayerErrorAudioSystemError]; + + return; + } } audioConverterAudioStreamBasicDescription = *asbd; @@ -1646,29 +1672,21 @@ BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* classDesc return YES; } --(void) stopAudioUnitWithReason:(NSString*)reason +-(void) stopAudioUnitWithReason:(STKAudioPlayerStopReason)stopReasonIn { OSStatus status; - LOGINFO(([NSString stringWithFormat:@"With Reason: %@", reason])); - if (!audioUnit) { - LOGINFO(@"No AudioUnit"); - + stopReason = stopReasonIn; self.internalState = STKAudioPlayerInternalStateStopped; return; } - else - { - LOGINFO(@"Stopping AudioUnit"); - } status = AudioOutputUnitStop(audioUnit); - rebufferingStartFrames = 0; - + stopReason = stopReasonIn; self.internalState = STKAudioPlayerInternalStateStopped; } @@ -1713,6 +1731,11 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu return; } + if (!currentlyReadingEntry->parsedHeader) + { + return; + } + if (seekToTimeWasRequested || disposeWasRequested) { return; @@ -1782,7 +1805,6 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu if (disposeWasRequested || seekToTimeWasRequested || self.internalState == STKAudioPlayerInternalStateStopped - || self.internalState == STKAudioPlayerInternalStateStopping || self.internalState == STKAudioPlayerInternalStateDisposed || self.internalState == STKAudioPlayerInternalStatePendingNext) { @@ -1807,11 +1829,6 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu localPcmBufferList.mNumberBuffers = 1; localPcmAudioBuffer = &localPcmBufferList.mBuffers[0]; - if (start == end) - { - NSLog(@""); - } - if (end >= start) { UInt32 framesAdded = 0; @@ -1829,15 +1846,19 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu { OSSpinLockLock(&pcmBufferSpinLock); pcmBufferUsedFrameCount += framesAdded; - currentlyReadingEntry->framesQueued += framesAdded; OSSpinLockUnlock(&pcmBufferSpinLock); - + + OSSpinLockLock(¤tlyReadingEntry->spinLock); + currentlyReadingEntry->framesQueued += framesAdded; + OSSpinLockUnlock(¤tlyReadingEntry->spinLock); return; } else if (status != 0) { - NSLog(@""); + [self unexpectedError:STKAudioPlayerErrorCodecError]; + + return; } framesToDecode = start; @@ -1846,9 +1867,12 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu { OSSpinLockLock(&pcmBufferSpinLock); pcmBufferUsedFrameCount += framesAdded; - currentlyReadingEntry->framesQueued += framesAdded; OSSpinLockUnlock(&pcmBufferSpinLock); + OSSpinLockLock(¤tlyReadingEntry->spinLock); + currentlyReadingEntry->framesQueued += framesAdded; + OSSpinLockUnlock(¤tlyReadingEntry->spinLock); + continue; } @@ -1864,23 +1888,31 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu { OSSpinLockLock(&pcmBufferSpinLock); pcmBufferUsedFrameCount += framesAdded; - currentlyReadingEntry->framesQueued += framesAdded; OSSpinLockUnlock(&pcmBufferSpinLock); + OSSpinLockLock(¤tlyReadingEntry->spinLock); + currentlyReadingEntry->framesQueued += framesAdded; + OSSpinLockUnlock(¤tlyReadingEntry->spinLock); + return; } else if (status == 0) { OSSpinLockLock(&pcmBufferSpinLock); pcmBufferUsedFrameCount += framesAdded; - currentlyReadingEntry->framesQueued += framesAdded; OSSpinLockUnlock(&pcmBufferSpinLock); + OSSpinLockLock(¤tlyReadingEntry->spinLock); + currentlyReadingEntry->framesQueued += framesAdded; + OSSpinLockUnlock(¤tlyReadingEntry->spinLock); + continue; } else if (status != 0) { - NSLog(@""); + [self unexpectedError:STKAudioPlayerErrorCodecError]; + + return; } } else @@ -1900,23 +1932,31 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu { OSSpinLockLock(&pcmBufferSpinLock); pcmBufferUsedFrameCount += framesAdded; - currentlyReadingEntry->framesQueued += framesAdded; OSSpinLockUnlock(&pcmBufferSpinLock); + OSSpinLockLock(¤tlyReadingEntry->spinLock); + currentlyReadingEntry->framesQueued += framesAdded; + OSSpinLockUnlock(¤tlyReadingEntry->spinLock); + return; } else if (status == 0) { OSSpinLockLock(&pcmBufferSpinLock); pcmBufferUsedFrameCount += framesAdded; - currentlyReadingEntry->framesQueued += framesAdded; OSSpinLockUnlock(&pcmBufferSpinLock); + OSSpinLockLock(¤tlyReadingEntry->spinLock); + currentlyReadingEntry->framesQueued += framesAdded; + OSSpinLockUnlock(¤tlyReadingEntry->spinLock); + continue; } else if (status != 0) { - NSLog(@""); + [self unexpectedError:STKAudioPlayerErrorCodecError]; + + return; } } } @@ -1928,6 +1968,7 @@ static OSStatus playbackCallback(void* inRefCon, AudioUnitRenderActionFlags* ioA OSSpinLockLock(&audioPlayer->pcmBufferSpinLock); + BOOL waitForBuffer = NO; STKQueueEntry* entry = audioPlayer->currentlyPlayingEntry; AudioBuffer* audioBuffer = audioPlayer->pcmAudioBuffer; UInt32 frameSizeInBytes = audioPlayer->pcmBufferFrameSizeInBytes; @@ -1935,12 +1976,13 @@ static OSStatus playbackCallback(void* inRefCon, AudioUnitRenderActionFlags* ioA UInt32 start = audioPlayer->pcmBufferFrameStartIndex; UInt32 end = (audioPlayer->pcmBufferFrameStartIndex + audioPlayer->pcmBufferUsedFrameCount) % audioPlayer->pcmBufferTotalFrameCount; BOOL signal = audioPlayer->waiting && used < audioPlayer->pcmBufferTotalFrameCount / 2; - BOOL waitForBuffer = NO; + STKAudioPlayerInternalState state = audioPlayer.internalState; if (state == STKAudioPlayerInternalStateWaitingForData) { - if (audioPlayer->currentlyReadingEntry == audioPlayer->currentlyPlayingEntry + if (audioPlayer->currentlyPlayingEntry + && audioPlayer->currentlyReadingEntry == audioPlayer->currentlyPlayingEntry && audioPlayer->currentlyPlayingEntry->framesQueued < audioPlayer->framesRequiredToStartPlaying) { waitForBuffer = YES; @@ -1948,7 +1990,7 @@ static OSStatus playbackCallback(void* inRefCon, AudioUnitRenderActionFlags* ioA } else if (state == STKAudioPlayerInternalStateRebuffering) { - if (used < audioPlayer->pcmBufferTotalFrameCount) + if (used < audioPlayer->framesRequiredToPlayAfterRebuffering) { waitForBuffer = YES; } @@ -1964,7 +2006,7 @@ static OSStatus playbackCallback(void* inRefCon, AudioUnitRenderActionFlags* ioA UInt32 totalFramesCopied = 0; - if (used > 0 && !waitForBuffer) + if (used > 0 && !waitForBuffer && entry != nil) { if (state == STKAudioPlayerInternalStateWaitingForData) { @@ -2021,12 +2063,29 @@ static OSStatus playbackCallback(void* inRefCon, AudioUnitRenderActionFlags* ioA audioPlayer.internalState = STKAudioPlayerInternalStatePlaying; } - UInt32 extraFramesPlayedNotAssigned = 0; + if (totalFramesCopied < inNumberFrames) + { + UInt32 delta = inNumberFrames - totalFramesCopied; + + memset(ioData->mBuffers[0].mData + (totalFramesCopied * frameSizeInBytes), 0, delta * frameSizeInBytes); + + if (!(entry == nil || state == STKAudioPlayerInternalStateWaitingForDataAfterSeek || state == STKAudioPlayerInternalStateWaitingForData || state == STKAudioPlayerInternalStateRebuffering)) + { + NSLog(@"Buffering"); + audioPlayer.internalState = STKAudioPlayerInternalStateRebuffering; + } + } - OSSpinLockLock(&audioPlayer->pcmBufferSpinLock); - UInt32 framesPlayedForCurrent = totalFramesCopied; + if (entry == nil) + { + return 0; + } + + OSSpinLockLock(&entry->spinLock); + int64_t extraFramesPlayedNotAssigned = 0; + int64_t framesPlayedForCurrent = totalFramesCopied; - if (entry->lastFrameQueued > 0) + if (entry->lastFrameQueued >= 0) { framesPlayedForCurrent = MIN(entry->lastFrameQueued - entry->framesPlayed, framesPlayedForCurrent); } @@ -2034,22 +2093,9 @@ static OSStatus playbackCallback(void* inRefCon, AudioUnitRenderActionFlags* ioA entry->framesPlayed += framesPlayedForCurrent; extraFramesPlayedNotAssigned = totalFramesCopied - framesPlayedForCurrent; - OSSpinLockUnlock(&audioPlayer->pcmBufferSpinLock); - - if (totalFramesCopied < inNumberFrames) - { - UInt32 delta = inNumberFrames - totalFramesCopied; - - memset(ioData->mBuffers[0].mData + (totalFramesCopied * frameSizeInBytes), 0, delta * frameSizeInBytes); - - if (!(state == STKAudioPlayerInternalStateWaitingForDataAfterSeek || state == STKAudioPlayerInternalStateWaitingForData || state == STKAudioPlayerInternalStateRebuffering)) - { - NSLog(@"Buffering"); - audioPlayer.internalState = STKAudioPlayerInternalStateRebuffering; - } - } - BOOL lastFramePlayed = entry->framesPlayed == entry->lastFrameQueued; + + OSSpinLockUnlock(&entry->spinLock); if (signal || lastFramePlayed) { @@ -2057,6 +2103,8 @@ static OSStatus playbackCallback(void* inRefCon, AudioUnitRenderActionFlags* ioA if (lastFramePlayed && entry == audioPlayer->currentlyPlayingEntry) { + NSLog(@"Finished %@", [entry description]); + [audioPlayer audioQueueFinishedPlaying:entry]; while (extraFramesPlayedNotAssigned > 0) @@ -2065,7 +2113,9 @@ static OSStatus playbackCallback(void* inRefCon, AudioUnitRenderActionFlags* ioA if (entry != nil) { - UInt32 framesPlayedForCurrent = extraFramesPlayedNotAssigned; + int64_t framesPlayedForCurrent = extraFramesPlayedNotAssigned; + + OSSpinLockLock(&newEntry->spinLock); if (newEntry->lastFrameQueued > 0) { @@ -2076,14 +2126,20 @@ static OSStatus playbackCallback(void* inRefCon, AudioUnitRenderActionFlags* ioA if (newEntry->framesPlayed == newEntry->lastFrameQueued) { + OSSpinLockUnlock(&newEntry->spinLock); + [audioPlayer audioQueueFinishedPlaying:newEntry]; } + else + { + OSSpinLockUnlock(&newEntry->spinLock); + } extraFramesPlayedNotAssigned -= framesPlayedForCurrent; } } } - + pthread_cond_signal(&audioPlayer->playerThreadReadyCondition); pthread_mutex_unlock(&audioPlayer->playerMutex); } @@ -2092,4 +2148,3 @@ static OSStatus playbackCallback(void* inRefCon, AudioUnitRenderActionFlags* ioA } @end - diff --git a/StreamingKit/StreamingKit/STKQueueEntry.h b/StreamingKit/StreamingKit/STKQueueEntry.h index ef030de..9033fec 100644 --- a/StreamingKit/StreamingKit/STKQueueEntry.h +++ b/StreamingKit/StreamingKit/STKQueueEntry.h @@ -7,47 +7,40 @@ // #import "STKDataSource.h" +#import "libkern/OSAtomic.h" #import "AudioToolbox/AudioToolbox.h" @interface STKQueueEntry : NSObject { @public + OSSpinLock spinLock; + BOOL parsedHeader; - double sampleRate; - double lastProgress; + Float64 sampleRate; double packetDuration; - int framesQueued; - int framesPlayed; - Float64 lastFrameQueued; UInt64 audioDataOffset; UInt64 audioDataByteCount; UInt32 packetBufferSize; - volatile BOOL cancel; - volatile BOOL finished; + volatile Float64 seekTime; + volatile int64_t framesQueued; + volatile int64_t framesPlayed; + volatile int64_t lastFrameQueued; volatile int processedPacketsCount; volatile int processedPacketsSizeTotal; AudioStreamBasicDescription audioStreamBasicDescription; } +@property (readonly) UInt64 audioDataLengthInBytes; @property (readwrite, retain) NSObject* queueItemId; @property (readwrite, retain) STKDataSource* dataSource; -@property (readwrite) Float64 seekTime; -@property (readwrite) int bytesBuffered; -@property (readwrite) int lastByteIndex; -@property (readwrite) Float64 lastFrameIndex; -@property (readwrite) Float64 timeWhenLastBufferReturned; -@property (readwrite) Float64 firstFrameIndex; -@property (readonly) UInt64 audioDataLengthInBytes; +-(id) initWithDataSource:(STKDataSource*)dataSource andQueueItemId:(NSObject*)queueItemId; + +-(void) reset; -(double) duration; -(Float64) progressInFrames; -(double) calculatedBitRate; -(void) updateAudioDataSource; -(BOOL) isDefinitelyCompatible:(AudioStreamBasicDescription*)basicDescription; --(BOOL) isKnownToBeIncompatible:(AudioStreamBasicDescription*)basicDescription; --(BOOL) couldBeIncompatible:(AudioStreamBasicDescription*)basicDescription; --(Float64) calculateProgressWithTotalFramesPlayed:(Float64)framesPlayed; - --(id) initWithDataSource:(STKDataSource*)dataSource andQueueItemId:(NSObject*)queueItemId; @end \ No newline at end of file diff --git a/StreamingKit/StreamingKit/STKQueueEntry.m b/StreamingKit/StreamingKit/STKQueueEntry.m index 66bb25b..caf9c42 100644 --- a/StreamingKit/StreamingKit/STKQueueEntry.m +++ b/StreamingKit/StreamingKit/STKQueueEntry.m @@ -19,14 +19,21 @@ { self.dataSource = dataSourceIn; self.queueItemId = queueItemIdIn; - self.lastFrameIndex = -1; - self.lastByteIndex = -1; self->lastFrameQueued = -1; } return self; } +-(void) reset +{ + OSSpinLockLock(&self->spinLock); + self->framesQueued = 0; + self->framesPlayed = 0; + self->lastFrameQueued = -1; + OSSpinLockUnlock(&self->spinLock); +} + -(double) calculatedBitRate { double retval; @@ -58,29 +65,6 @@ } } --(Float64) calculateProgressWithTotalFramesPlayed:(Float64)framesPlayedIn -{ - return (Float64)self.seekTime + ((framesPlayedIn - self.firstFrameIndex) / (Float64)self->audioStreamBasicDescription.mSampleRate); -} - --(double) calculateProgressWithBytesPlayed:(Float64)bytesPlayed -{ - double retval = lastProgress; - - if (self->sampleRate > 0) - { - double calculatedBitrate = [self calculatedBitRate]; - - retval = bytesPlayed / calculatedBitrate * 8; - - retval = self.seekTime + retval; - - [self updateAudioDataSource]; - } - - return retval; -} - -(double) duration { if (self->sampleRate <= 0) @@ -127,29 +111,13 @@ return (memcmp(&(self->audioStreamBasicDescription), basicDescription, sizeof(*basicDescription)) == 0); } --(BOOL) isKnownToBeIncompatible:(AudioStreamBasicDescription*)basicDescription -{ - if (self->audioStreamBasicDescription.mSampleRate == 0) - { - return NO; - } - - return (memcmp(&(self->audioStreamBasicDescription), basicDescription, sizeof(*basicDescription)) != 0); -} - --(BOOL) couldBeIncompatible:(AudioStreamBasicDescription*)basicDescription -{ - if (self->audioStreamBasicDescription.mSampleRate == 0) - { - return YES; - } - - return memcmp(&(self->audioStreamBasicDescription), basicDescription, sizeof(*basicDescription)) != 0; -} - -(Float64) progressInFrames { - return self.seekTime + self->framesPlayed; + OSSpinLockLock(&self->spinLock); + Float64 retval = self->seekTime + self->framesPlayed; + OSSpinLockUnlock(&self->spinLock); + + return retval; } -(NSString*) description From f321f1a0d7641c30ba82e0a66e65bd21cbe6d371 Mon Sep 17 00:00:00 2001 From: Thong Nguyen Date: Fri, 31 Jan 2014 20:33:15 +0000 Subject: [PATCH 16/33] More code tidy-ups --- StreamingKit/StreamingKit/STKAudioPlayer.h | 10 +++++- StreamingKit/StreamingKit/STKAudioPlayer.m | 41 ++++++++++++++++------ 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.h b/StreamingKit/StreamingKit/STKAudioPlayer.h index 2c6e063..b7f1d49 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.h +++ b/StreamingKit/StreamingKit/STKAudioPlayer.h @@ -95,6 +95,13 @@ typedef enum } STKAudioPlayerErrorCode; +typedef enum +{ + STKAudioPlayerOptionNone = 0, + STKAudioPlayerOptionDontFlushQueueOnSeek = 1 +} +STKAudioPlayerOptions; + @class STKAudioPlayer; @protocol STKAudioPlayerDelegate @@ -116,11 +123,12 @@ STKAudioPlayerErrorCode; @property (readonly) double duration; @property (readonly) double progress; @property (readwrite) STKAudioPlayerState state; +@property (readonly) STKAudioPlayerOptions options; @property (readonly) STKAudioPlayerStopReason stopReason; @property (readwrite, unsafe_unretained) id delegate; -(id) init; --(id) initWithReadBufferSize:(int)readBufferSizeIn; +-(id) initWithReadBufferSize:(int)readBufferSizeIn andOptions:(STKAudioPlayerOptions)options; +(STKDataSource*) dataSourceFromURL:(NSURL*)url; /// Plays an item from the given URL (all pending queued items are removed) diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index 210d5ef..b67bafc 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -40,7 +40,7 @@ #import "NSMutableArray+STKAudioPlayer.h" #import "libkern/OSAtomic.h" -#define STK_DEFAULT_PCM_BUFFER_SIZE_IN_SECONDS (65) +#define STK_DEFAULT_PCM_BUFFER_SIZE_IN_SECONDS (10) #define STK_DEFAULT_SECONDS_REQUIRED_TO_START_PLAYING (0) #define STK_MAX_COMPRESSED_PACKETS_FOR_BITRATE_CALCULATION (2048) @@ -57,6 +57,8 @@ UInt8* readBuffer; int readBufferSize; + STKAudioPlayerOptions options; + AudioComponentInstance audioUnit; UInt32 framesRequiredToStartPlaying; @@ -132,6 +134,12 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn @implementation STKAudioPlayer @synthesize delegate, internalState, state; + +-(STKAudioPlayerOptions) options +{ + return options; +} + -(STKAudioPlayerInternalState) internalState { return internalState; @@ -222,13 +230,15 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn -(id) init { - return [self initWithReadBufferSize:STK_DEFAULT_READ_BUFFER_SIZE]; + return [self initWithReadBufferSize:STK_DEFAULT_READ_BUFFER_SIZE andOptions:STKAudioPlayerOptionNone]; } --(id) initWithReadBufferSize:(int)readBufferSizeIn +-(id) initWithReadBufferSize:(int)readBufferSizeIn andOptions:(STKAudioPlayerOptions)optionsIn { if (self = [super init]) { + options = optionsIn; + canonicalAudioStreamBasicDescription.mSampleRate = 44100.00; canonicalAudioStreamBasicDescription.mFormatID = kAudioFormatLinearPCM; canonicalAudioStreamBasicDescription.mFormatFlags = kAudioFormatFlagsCanonical; @@ -1032,10 +1042,18 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn [currentlyReadingEntry.dataSource unregisterForEvents]; } - [self requeueBufferingEntries]; - - self.internalState = STKAudioPlayerInternalStateWaitingForDataAfterSeek; - [self setCurrentlyReadingEntry:currentlyPlayingEntry andStartPlaying:YES clearQueue:NO]; + if (self->options & STKAudioPlayerOptionDontFlushQueueOnSeek) + { + [self requeueBufferingEntries]; + + self.internalState = STKAudioPlayerInternalStateWaitingForDataAfterSeek; + [self setCurrentlyReadingEntry:currentlyPlayingEntry andStartPlaying:YES clearQueue:NO]; + } + else + { + self.internalState = STKAudioPlayerInternalStateWaitingForDataAfterSeek; + [self setCurrentlyReadingEntry:currentlyPlayingEntry andStartPlaying:YES clearQueue:YES]; + } } else if (currentlyReadingEntry == nil) { @@ -1531,7 +1549,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn #define kOutputBus 0 #define kInputBus 1 -BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* classDesc) +static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* classDesc) { UInt32 size; @@ -2010,11 +2028,11 @@ static OSStatus playbackCallback(void* inRefCon, AudioUnitRenderActionFlags* ioA { if (state == STKAudioPlayerInternalStateWaitingForData) { - NSLog(@"Starting"); + // Starting } else if (state == STKAudioPlayerInternalStateRebuffering) { - NSLog(@"Buffering resuming"); + // Resuming from buffering } if (end > start) @@ -2071,7 +2089,8 @@ static OSStatus playbackCallback(void* inRefCon, AudioUnitRenderActionFlags* ioA if (!(entry == nil || state == STKAudioPlayerInternalStateWaitingForDataAfterSeek || state == STKAudioPlayerInternalStateWaitingForData || state == STKAudioPlayerInternalStateRebuffering)) { - NSLog(@"Buffering"); + // Buffering + audioPlayer.internalState = STKAudioPlayerInternalStateRebuffering; } } From 70fe37cd4a59360bcc9226168a7d82d4704a8cdc Mon Sep 17 00:00:00 2001 From: Thong Nguyen Date: Fri, 31 Jan 2014 20:35:44 +0000 Subject: [PATCH 17/33] Added missing status checks --- StreamingKit/StreamingKit/STKAudioPlayer.m | 50 ++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index b67bafc..9ddf398 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -1664,20 +1664,56 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl status = AudioComponentInstanceNew(component, &audioUnit); + if (status) + { + [self unexpectedError:STKAudioPlayerErrorAudioSystemError]; + + return; + } + UInt32 flag = 1; status = AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, kOutputBus, &flag, sizeof(flag)); + + if (status) + { + [self unexpectedError:STKAudioPlayerErrorAudioSystemError]; + + return; + } + status = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &canonicalAudioStreamBasicDescription, sizeof(canonicalAudioStreamBasicDescription)); + if (status) + { + [self unexpectedError:STKAudioPlayerErrorAudioSystemError]; + + return; + } + AURenderCallbackStruct callbackStruct; callbackStruct.inputProc = playbackCallback; callbackStruct.inputProcRefCon = (__bridge void*)self; status = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, kOutputBus, &callbackStruct, sizeof(callbackStruct)); + + if (status) + { + [self unexpectedError:STKAudioPlayerErrorAudioSystemError]; + + return; + } status = AudioUnitInitialize(audioUnit); + if (status) + { + [self unexpectedError:STKAudioPlayerErrorAudioSystemError]; + + return; + } + pthread_mutex_unlock(&playerMutex); } @@ -1687,6 +1723,13 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl status = AudioOutputUnitStart(audioUnit); + if (status) + { + [self unexpectedError:STKAudioPlayerErrorAudioSystemError]; + + return NO; + } + return YES; } @@ -1704,6 +1747,13 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl status = AudioOutputUnitStop(audioUnit); + if (status) + { + [self unexpectedError:STKAudioPlayerErrorAudioSystemError]; + + return; + } + stopReason = stopReasonIn; self.internalState = STKAudioPlayerInternalStateStopped; } From b903ee58ccd3a77baf2932d0796ebb1b4f38fd66 Mon Sep 17 00:00:00 2001 From: Thong Nguyen Date: Fri, 31 Jan 2014 20:39:51 +0000 Subject: [PATCH 18/33] Formatting fixes --- StreamingKit/StreamingKit/STKAudioPlayer.m | 116 +++++++++------------ 1 file changed, 48 insertions(+), 68 deletions(-) diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index 9ddf398..9d81650 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -726,44 +726,21 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn return NO; } --(void)invokeOnPlaybackThreadAtIntervalHelper:(NSTimer*)timer -{ - void(^block)() = (void(^)())timer.userInfo; - - block(); -} - --(BOOL) invokeOnPlaybackThreadAtInterval:(NSTimeInterval)interval withBlock:(void(^)())block -{ - NSRunLoop* runLoop = playbackThreadRunLoop; - - if (runLoop) - { - NSTimer* timer = [NSTimer timerWithTimeInterval:interval target:self selector:@selector(invokeOnPlaybackThreadAtIntervalHelper:) userInfo:[block copy] repeats:NO]; - - [runLoop addTimer:timer forMode:NSRunLoopCommonModes]; - - return YES; - } - - return NO; -} - -(void) wakeupPlaybackThread { [self invokeOnPlaybackThread:^ - { - [self processRunloop]; - }]; - - pthread_mutex_lock(&playerMutex); - - if (waiting) - { - pthread_cond_signal(&playerThreadReadyCondition); - } - - pthread_mutex_unlock(&playerMutex); + { + [self processRunloop]; + }]; + + pthread_mutex_lock(&playerMutex); + + if (waiting) + { + pthread_cond_signal(&playerThreadReadyCondition); + } + + pthread_mutex_unlock(&playerMutex); } -(void) seekToTime:(double)value @@ -945,39 +922,42 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn -(void) dispatchSyncOnMainThread:(void(^)())block { - __block BOOL finished = NO; - - if (disposeWasRequested) - { - return; - } - - dispatch_async(dispatch_get_main_queue(), ^ - { - block(); - - pthread_mutex_lock(&mainThreadSyncCallMutex); - finished = YES; - pthread_cond_signal(&mainThreadSyncCallReadyCondition); - pthread_mutex_unlock(&mainThreadSyncCallMutex); - }); - - while (true) - { - if (disposeWasRequested) - { - break; - } - - if (finished) - { - break; - } - - pthread_mutex_lock(&mainThreadSyncCallMutex); - pthread_cond_wait(&mainThreadSyncCallReadyCondition, &mainThreadSyncCallMutex); - pthread_mutex_unlock(&mainThreadSyncCallMutex); - } + __block BOOL finished = NO; + + if (disposeWasRequested) + { + return; + } + + dispatch_async(dispatch_get_main_queue(), ^ + { + if (!disposeWasRequested) + { + block(); + } + + pthread_mutex_lock(&mainThreadSyncCallMutex); + finished = YES; + pthread_cond_signal(&mainThreadSyncCallReadyCondition); + pthread_mutex_unlock(&mainThreadSyncCallMutex); + }); + + while (true) + { + if (disposeWasRequested) + { + break; + } + + if (finished) + { + break; + } + + pthread_mutex_lock(&mainThreadSyncCallMutex); + pthread_cond_wait(&mainThreadSyncCallReadyCondition, &mainThreadSyncCallMutex); + pthread_mutex_unlock(&mainThreadSyncCallMutex); + } } -(void) playbackThreadQueueMainThreadSyncBlock:(void(^)())block From 83bc40d5fc26b33875aa69cd61b25f5cd8a43777 Mon Sep 17 00:00:00 2001 From: Thong Nguyen Date: Fri, 31 Jan 2014 20:46:41 +0000 Subject: [PATCH 19/33] More documentation. STKAutoRecoveringHTTPDataSource is now default for HTTP --- ExampleApp/ExampleApp/AppDelegate.m | 10 +++++++--- StreamingKit/StreamingKit/STKAudioPlayer.h | 4 ++++ StreamingKit/StreamingKit/STKAudioPlayer.m | 7 ++++--- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/ExampleApp/ExampleApp/AppDelegate.m b/ExampleApp/ExampleApp/AppDelegate.m index cf44a16..8ea5856 100644 --- a/ExampleApp/ExampleApp/AppDelegate.m +++ b/ExampleApp/ExampleApp/AppDelegate.m @@ -57,7 +57,7 @@ { NSURL* url = [NSURL URLWithString:@"http://fs.bloom.fm/oss/audiosamples/sample.mp3"]; - STKAutoRecoveringHTTPDataSource* dataSource = [[STKAutoRecoveringHTTPDataSource alloc] initWithHTTPDataSource:(STKHTTPDataSource*)[STKAudioPlayer dataSourceFromURL:url]]; + STKDataSource* dataSource = [STKAudioPlayer dataSourceFromURL:url]; [audioPlayer setDataSource:dataSource withQueueItemId:[[SampleQueueId alloc] initWithUrl:url andCount:0]]; } @@ -66,8 +66,10 @@ { NSString* path = [[NSBundle mainBundle] pathForResource:@"airplane" ofType:@"aac"]; NSURL* url = [NSURL fileURLWithPath:path]; + + STKDataSource* dataSource = [STKAudioPlayer dataSourceFromURL:url]; - [audioPlayer queueDataSource:[STKAudioPlayer dataSourceFromURL:url] withQueueItemId:[[SampleQueueId alloc] initWithUrl:url andCount:0]]; + [audioPlayer queueDataSource:dataSource withQueueItemId:[[SampleQueueId alloc] initWithUrl:url andCount:0]]; } -(void) audioPlayerViewPlayFromLocalFileSelected:(AudioPlayerView*)audioPlayerView @@ -75,7 +77,9 @@ NSString* path = [[NSBundle mainBundle] pathForResource:@"sample" ofType:@"m4a"]; NSURL* url = [NSURL fileURLWithPath:path]; - [audioPlayer setDataSource:[STKAudioPlayer dataSourceFromURL:url] withQueueItemId:[[SampleQueueId alloc] initWithUrl:url andCount:0]]; + STKDataSource* dataSource = [STKAudioPlayer dataSourceFromURL:url]; + + [audioPlayer setDataSource:dataSource withQueueItemId:[[SampleQueueId alloc] initWithUrl:url andCount:0]]; } @end diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.h b/StreamingKit/StreamingKit/STKAudioPlayer.h index b7f1d49..5756905 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.h +++ b/StreamingKit/StreamingKit/STKAudioPlayer.h @@ -130,6 +130,10 @@ STKAudioPlayerOptions; -(id) init; -(id) initWithReadBufferSize:(int)readBufferSizeIn andOptions:(STKAudioPlayerOptions)options; +/// Creates a datasource from a given URL +/// URLs with FILE schemes will return an STKLocalFileDataSource +/// URLs with HTTP schemes will return an STKHTTPDataSource wrapped within an STKAutoRecoveringHTTPDataSource +/// URLs with unrecognised schemes will return nil +(STKDataSource*) dataSourceFromURL:(NSURL*)url; /// Plays an item from the given URL (all pending queued items are removed) -(void) play:(NSString*)urlString; diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index 9d81650..e8afc24 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -35,6 +35,7 @@ #import "STKAudioPlayer.h" #import "AudioToolbox/AudioToolbox.h" #import "STKHTTPDataSource.h" +#import "STKAutoRecoveringHTTPDataSource.h" #import "STKLocalFileDataSource.h" #import "STKQueueEntry.h" #import "NSMutableArray+STKAudioPlayer.h" @@ -367,15 +368,15 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn +(STKDataSource*) dataSourceFromURL:(NSURL*)url { - STKDataSource* retval; + STKDataSource* retval = nil; if ([url.scheme isEqualToString:@"file"]) { retval = [[STKLocalFileDataSource alloc] initWithFilePath:url.path]; } - else + else if ([url.scheme isEqualToString:@"http"] || [url.scheme isEqualToString:@"https"]) { - retval = [[STKHTTPDataSource alloc] initWithURL:url]; + retval = [[STKAutoRecoveringHTTPDataSource alloc] initWithHTTPDataSource:[[STKHTTPDataSource alloc] initWithURL:url]]; } return retval; From c2fe2c68b2aed0241183086f55a2acce0d6b5460 Mon Sep 17 00:00:00 2001 From: Thong Nguyen Date: Fri, 31 Jan 2014 22:18:09 +0000 Subject: [PATCH 20/33] Started adding framefilter support for EQ etc. Fixed seek bug with WAV files --- ExampleApp/ExampleApp/AppDelegate.m | 2 +- StreamingKit/StreamingKit/STKAudioPlayer.h | 6 + StreamingKit/StreamingKit/STKAudioPlayer.m | 114 +++++++++++++++--- .../STKAutoRecoveringHTTPDataSource.m | 4 + 4 files changed, 105 insertions(+), 21 deletions(-) diff --git a/ExampleApp/ExampleApp/AppDelegate.m b/ExampleApp/ExampleApp/AppDelegate.m index 8ea5856..766a627 100644 --- a/ExampleApp/ExampleApp/AppDelegate.m +++ b/ExampleApp/ExampleApp/AppDelegate.m @@ -55,7 +55,7 @@ -(void) audioPlayerViewPlayFromHTTPSelected:(AudioPlayerView*)audioPlayerView { - NSURL* url = [NSURL URLWithString:@"http://fs.bloom.fm/oss/audiosamples/sample.mp3"]; + NSURL* url = [NSURL URLWithString:@"http://my.halloo.com/audio/perfectly.wav"]; STKDataSource* dataSource = [STKAudioPlayer dataSourceFromURL:url]; diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.h b/StreamingKit/StreamingKit/STKAudioPlayer.h index 5756905..4e5149c 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.h +++ b/StreamingKit/StreamingKit/STKAudioPlayer.h @@ -118,10 +118,13 @@ STKAudioPlayerOptions; @end +typedef void(^STKFrameFilter)(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UInt32 frameCount, UInt32* frames); + @interface STKAudioPlayer : NSObject @property (readonly) double duration; @property (readonly) double progress; +@property (readonly) NSArray* frameFilters; @property (readwrite) STKAudioPlayerState state; @property (readonly) STKAudioPlayerOptions options; @property (readonly) STKAudioPlayerStopReason stopReason; @@ -164,4 +167,7 @@ STKAudioPlayerOptions; /// The QueueItemId of the currently playing item -(NSObject*) currentlyPlayingQueueItemId; +-(void) appendFrameFilter:(STKFrameFilter)frameFilter withName:(NSString*)name; +-(void) addFrameFilter:(STKFrameFilter)frameFilter withName:(NSString*)name afterFilterWithName:(NSString*)afterFilterWithName; + @end diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index e8afc24..582da51 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -53,6 +53,27 @@ #define LOGINFO(x) [self logInfo:[NSString stringWithFormat:@"%s %@", sel_getName(_cmd), x]]; +@interface STKFrameFilterEntry : NSObject +{ +@public + NSString* name; + STKFrameFilter filter; +} +@end + +@implementation STKFrameFilterEntry +-(id) initWithFilter:(STKFrameFilter)filterIn andName:(NSString*)nameIn +{ + if (self = [super init]) + { + self->filter = [filterIn copy]; + self->name = nameIn; + } + + return self; +} +@end + @interface STKAudioPlayer() { UInt8* readBuffer; @@ -85,6 +106,7 @@ AudioStreamBasicDescription audioConverterAudioStreamBasicDescription; BOOL discontinuous; + NSArray* frameFilters; NSThread* playbackThread; NSRunLoop* playbackThreadRunLoop; AudioFileStreamID audioFileStream; @@ -584,7 +606,10 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn } case kAudioFileStreamProperty_ReadyToProducePackets: { - discontinuous = YES; + if (!audioConverterAudioStreamBasicDescription.mFormatID == kAudioFormatLinearPCM) + { + discontinuous = YES; + } break; } @@ -1200,11 +1225,6 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn self.internalState = STKAudioPlayerInternalStateWaitingForDataAfterSeek; - if (seekByteOffset > 0) - { - discontinuous = YES; - } - if (audioUnit) { [self resetPcmBuffers]; @@ -1674,7 +1694,7 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl AURenderCallbackStruct callbackStruct; - callbackStruct.inputProc = playbackCallback; + callbackStruct.inputProc = OutputRenderCallback; callbackStruct.inputProcRefCon = (__bridge void*)self; status = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, kOutputBus, &callbackStruct, sizeof(callbackStruct)); @@ -1795,11 +1815,8 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu return; } - if (discontinuous) - { - discontinuous = NO; - } - + discontinuous = NO; + OSStatus status; AudioConvertInfo convertInfo; @@ -1811,14 +1828,16 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu convertInfo.audioBuffer.mDataByteSize = numberBytes; convertInfo.audioBuffer.mNumberChannels = audioConverterAudioStreamBasicDescription.mChannelsPerFrame; - if (currentlyReadingEntry->processedPacketsCount < STK_MAX_COMPRESSED_PACKETS_FOR_BITRATE_CALCULATION) + if (packetDescriptionsIn && currentlyReadingEntry->processedPacketsCount < STK_MAX_COMPRESSED_PACKETS_FOR_BITRATE_CALCULATION) { int count = MIN(numberPackets, STK_MAX_COMPRESSED_PACKETS_FOR_BITRATE_CALCULATION - currentlyReadingEntry->processedPacketsCount); for (int i = 0; i < count; i++) { - SInt64 packetSize = packetDescriptionsIn[i].mDataByteSize; - + SInt64 packetSize; + + packetSize = packetDescriptionsIn[i].mDataByteSize; + OSAtomicAdd32((int32_t)packetSize, ¤tlyReadingEntry->processedPacketsSizeTotal); OSAtomicIncrement32(¤tlyReadingEntry->processedPacketsCount); } @@ -2011,7 +2030,7 @@ OSStatus AudioConverterCallback(AudioConverterRef inAudioConverter, UInt32* ioNu } } -static OSStatus playbackCallback(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData) +static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData) { STKAudioPlayer* audioPlayer = (__bridge STKAudioPlayer*)inRefCon; @@ -2050,6 +2069,8 @@ static OSStatus playbackCallback(void* inRefCon, AudioUnitRenderActionFlags* ioA return 0; } + + NSArray* frameFilters = audioPlayer->frameFilters; OSSpinLockUnlock(&audioPlayer->pcmBufferSpinLock); @@ -2125,13 +2146,27 @@ static OSStatus playbackCallback(void* inRefCon, AudioUnitRenderActionFlags* ioA audioPlayer.internalState = STKAudioPlayerInternalStateRebuffering; } } - + + if (frameFilters) + { + int count = frameFilters.count; + AudioStreamBasicDescription asbd = audioPlayer->canonicalAudioStreamBasicDescription; + + for (int i = 0; i < count; i++) + { + STKFrameFilterEntry* entry = [frameFilters objectAtIndex:i]; + + entry->filter(asbd.mChannelsPerFrame, asbd.mBytesPerFrame, inNumberFrames, ioData->mBuffers[0].mData); + } + } + if (entry == nil) { return 0; } OSSpinLockLock(&entry->spinLock); + int64_t extraFramesPlayedNotAssigned = 0; int64_t framesPlayedForCurrent = totalFramesCopied; @@ -2153,15 +2188,13 @@ static OSStatus playbackCallback(void* inRefCon, AudioUnitRenderActionFlags* ioA if (lastFramePlayed && entry == audioPlayer->currentlyPlayingEntry) { - NSLog(@"Finished %@", [entry description]); - [audioPlayer audioQueueFinishedPlaying:entry]; while (extraFramesPlayedNotAssigned > 0) { STKQueueEntry* newEntry = audioPlayer->currentlyPlayingEntry; - if (entry != nil) + if (newEntry != nil) { int64_t framesPlayedForCurrent = extraFramesPlayedNotAssigned; @@ -2197,4 +2230,45 @@ static OSStatus playbackCallback(void* inRefCon, AudioUnitRenderActionFlags* ioA return 0; } +-(NSArray*) frameFilters +{ + return frameFilters; +} + +-(void) appendFrameFilter:(STKFrameFilter)frameFilter withName:(NSString*)name +{ + [self addFrameFilter:frameFilter withName:name afterFilterWithName:nil]; +} + +-(void) addFrameFilter:(STKFrameFilter)frameFilter withName:(NSString*)name afterFilterWithName:(NSString*)afterFilterWithName +{ + pthread_mutex_lock(&self->playerMutex); + + NSMutableArray* newFrameFilters = [[NSMutableArray alloc] initWithCapacity:frameFilters.count + 1]; + + if (afterFilterWithName == nil) + { + [newFrameFilters addObjectsFromArray:frameFilters]; + [newFrameFilters addObject:[[STKFrameFilterEntry alloc] initWithFilter:frameFilter andName:name]]; + } + else + { + for (STKFrameFilterEntry* filterEntry in frameFilters) + { + [newFrameFilters addObject:filterEntry]; + + if (afterFilterWithName != nil && [filterEntry->name isEqualToString:afterFilterWithName]) + { + [newFrameFilters addObject:[[STKFrameFilterEntry alloc] initWithFilter:frameFilter andName:name]]; + } + } + } + + OSSpinLockLock(&pcmBufferSpinLock); + frameFilters = [NSArray arrayWithArray:newFrameFilters]; + OSSpinLockUnlock(&pcmBufferSpinLock); + + pthread_mutex_unlock(&self->playerMutex); +} + @end diff --git a/StreamingKit/StreamingKit/STKAutoRecoveringHTTPDataSource.m b/StreamingKit/StreamingKit/STKAutoRecoveringHTTPDataSource.m index cab2c0d..286ac7d 100644 --- a/StreamingKit/StreamingKit/STKAutoRecoveringHTTPDataSource.m +++ b/StreamingKit/StreamingKit/STKAutoRecoveringHTTPDataSource.m @@ -208,6 +208,8 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach return; } + waitingForNetwork = NO; + NSRunLoop* runLoop = self.innerDataSource.eventsRunLoop; if (runLoop == nil) @@ -226,6 +228,8 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach -(void) dataSourceEof:(STKDataSource*)dataSource { + NSLog(@"dataSourceEof"); + if ([self position] != [self length]) { [self processRetryOnError]; From 8c94b9ba5cd35573d4ab02e5ea105ea5112b25be Mon Sep 17 00:00:00 2001 From: Thong Nguyen Date: Fri, 31 Jan 2014 22:45:17 +0000 Subject: [PATCH 21/33] Added WAV file test to test app --- ExampleApp/ExampleApp/AppDelegate.m | 11 +++++++- ExampleApp/ExampleApp/AudioPlayerView.h | 4 ++- ExampleApp/ExampleApp/AudioPlayerView.m | 34 ++++++++++++++++--------- 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/ExampleApp/ExampleApp/AppDelegate.m b/ExampleApp/ExampleApp/AppDelegate.m index 766a627..9f654f1 100644 --- a/ExampleApp/ExampleApp/AppDelegate.m +++ b/ExampleApp/ExampleApp/AppDelegate.m @@ -55,7 +55,7 @@ -(void) audioPlayerViewPlayFromHTTPSelected:(AudioPlayerView*)audioPlayerView { - NSURL* url = [NSURL URLWithString:@"http://my.halloo.com/audio/perfectly.wav"]; + NSURL* url = [NSURL URLWithString:@"http://fs.bloom.fm/oss/audiosamples/sample.mp3"]; STKDataSource* dataSource = [STKAudioPlayer dataSourceFromURL:url]; @@ -82,4 +82,13 @@ [audioPlayer setDataSource:dataSource withQueueItemId:[[SampleQueueId alloc] initWithUrl:url andCount:0]]; } +-(void) audioPlayerViewQueuePcmWaveFileSelected:(AudioPlayerView*)audioPlayerView +{ + NSURL* url = [NSURL URLWithString:@"http://fs.bloom.fm/oss/audiosamples/perfectly.wav"]; + + STKDataSource* dataSource = [STKAudioPlayer dataSourceFromURL:url]; + + [audioPlayer queueDataSource:dataSource withQueueItemId:[[SampleQueueId alloc] initWithUrl:url andCount:0]]; +} + @end diff --git a/ExampleApp/ExampleApp/AudioPlayerView.h b/ExampleApp/ExampleApp/AudioPlayerView.h index 9f50974..0cbf808 100644 --- a/ExampleApp/ExampleApp/AudioPlayerView.h +++ b/ExampleApp/ExampleApp/AudioPlayerView.h @@ -41,6 +41,7 @@ -(void) audioPlayerViewPlayFromHTTPSelected:(AudioPlayerView*)audioPlayerView; -(void) audioPlayerViewQueueShortFileSelected:(AudioPlayerView*)audioPlayerView; -(void) audioPlayerViewPlayFromLocalFileSelected:(AudioPlayerView*)audioPlayerView; +-(void) audioPlayerViewQueuePcmWaveFileSelected:(AudioPlayerView*)audioPlayerView; @end @interface AudioPlayerView : UIView @@ -52,9 +53,10 @@ UISlider* slider; UISwitch* repeatSwitch; UIButton* playButton; - UIButton* disposeButton; + UIButton* stopButton; UIButton* playFromHTTPButton; UIButton* queueShortFileButton; + UIButton* queuePcmWaveFileFromHTTPButton; UIButton* playFromLocalFileButton; } diff --git a/ExampleApp/ExampleApp/AudioPlayerView.m b/ExampleApp/ExampleApp/AudioPlayerView.m index e844b24..98c40bc 100644 --- a/ExampleApp/ExampleApp/AudioPlayerView.m +++ b/ExampleApp/ExampleApp/AudioPlayerView.m @@ -53,22 +53,27 @@ if (self) { - CGSize size = CGSizeMake(180, 50); + CGSize size = CGSizeMake(220, 50); playFromHTTPButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; - playFromHTTPButton.frame = CGRectMake((320 - size.width) / 2, frame.size.height * 0.15, size.width, size.height); + playFromHTTPButton.frame = CGRectMake((320 - size.width) / 2, frame.size.height * 0.10, size.width, size.height); [playFromHTTPButton addTarget:self action:@selector(playFromHTTPButtonTouched) forControlEvents:UIControlEventTouchUpInside]; [playFromHTTPButton setTitle:@"Play from HTTP" forState:UIControlStateNormal]; playFromLocalFileButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; - playFromLocalFileButton.frame = CGRectMake((320 - size.width) / 2, frame.size.height * 0.15 + 50, size.width, size.height); + playFromLocalFileButton.frame = CGRectMake((320 - size.width) / 2, frame.size.height * 0.10 + 50, size.width, size.height); [playFromLocalFileButton addTarget:self action:@selector(playFromLocalFileButtonTouched) forControlEvents:UIControlEventTouchUpInside]; [playFromLocalFileButton setTitle:@"Play from Local File" forState:UIControlStateNormal]; queueShortFileButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; - queueShortFileButton.frame = CGRectMake((320 - size.width) / 2, frame.size.height * 0.15 + 100, size.width, size.height); + queueShortFileButton.frame = CGRectMake((320 - size.width) / 2, frame.size.height * 0.10 + 100, size.width, size.height); [queueShortFileButton addTarget:self action:@selector(queueShortFileButtonTouched) forControlEvents:UIControlEventTouchUpInside]; [queueShortFileButton setTitle:@"Queue short file" forState:UIControlStateNormal]; + + queuePcmWaveFileFromHTTPButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; + queuePcmWaveFileFromHTTPButton.frame = CGRectMake((320 - size.width) / 2, frame.size.height * 0.10 + 150, size.width, size.height); + [queuePcmWaveFileFromHTTPButton addTarget:self action:@selector(queuePcmWaveFileButtonTouched) forControlEvents:UIControlEventTouchUpInside]; + [queuePcmWaveFileFromHTTPButton setTitle:@"Queue PCM/WAVE from HTTP" forState:UIControlStateNormal]; size = CGSizeMake(90, 50); @@ -76,10 +81,10 @@ playButton.frame = CGRectMake(30, 380, size.width, size.height); [playButton addTarget:self action:@selector(playButtonPressed) forControlEvents:UIControlEventTouchUpInside]; - disposeButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; - disposeButton.frame = CGRectMake((320 - size.width) - 30, 380, size.width, size.height); - [disposeButton addTarget:self action:@selector(disposeButtonPressed) forControlEvents:UIControlEventTouchUpInside]; - [disposeButton setTitle:@"Dispose" forState:UIControlStateNormal]; + stopButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; + stopButton.frame = CGRectMake((320 - size.width) - 30, 380, size.width, size.height); + [stopButton addTarget:self action:@selector(stopButtonPressed) forControlEvents:UIControlEventTouchUpInside]; + [stopButton setTitle:@"Stop" forState:UIControlStateNormal]; slider = [[UISlider alloc] initWithFrame:CGRectMake(20, 320, 280, 20)]; slider.continuous = YES; @@ -87,7 +92,7 @@ size = CGSizeMake(80, 50); - repeatSwitch = [[UISwitch alloc] initWithFrame:CGRectMake((320 - size.width) / 2, frame.size.height * 0.15 + 170, size.width, size.height)]; + repeatSwitch = [[UISwitch alloc] initWithFrame:CGRectMake((320 - size.width) / 2, frame.size.height * 0.15 + 180, size.width, size.height)]; label = [[UILabel alloc] initWithFrame:CGRectMake(0, slider.frame.origin.y + slider.frame.size.height, frame.size.width, 50)]; @@ -102,10 +107,11 @@ [self addSubview:playFromHTTPButton]; [self addSubview:playFromLocalFileButton]; [self addSubview:queueShortFileButton]; + [self addSubview:queuePcmWaveFileFromHTTPButton]; [self addSubview:repeatSwitch]; [self addSubview:label]; [self addSubview:statusLabel]; - [self addSubview:disposeButton]; + [self addSubview:stopButton]; [self setupTimer]; [self updateControls]; @@ -179,10 +185,14 @@ [self.delegate audioPlayerViewQueueShortFileSelected:self]; } --(void) disposeButtonPressed +-(void) queuePcmWaveFileButtonTouched +{ + [self.delegate audioPlayerViewQueuePcmWaveFileSelected:self]; +} + +-(void) stopButtonPressed { [audioPlayer stop]; -// audioPlayer = nil; } -(void) playButtonPressed From dd7165b3d2ca2300cffb13f7ac3972c42ac254c6 Mon Sep 17 00:00:00 2001 From: Thong Nguyen Date: Fri, 31 Jan 2014 22:58:07 +0000 Subject: [PATCH 22/33] Fixed player somtimes waiting in buffering state for very small files --- StreamingKit/StreamingKit/STKAudioPlayer.m | 24 +++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index 582da51..71fd5de 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -42,7 +42,7 @@ #import "libkern/OSAtomic.h" #define STK_DEFAULT_PCM_BUFFER_SIZE_IN_SECONDS (10) -#define STK_DEFAULT_SECONDS_REQUIRED_TO_START_PLAYING (0) +#define STK_DEFAULT_SECONDS_REQUIRED_TO_START_PLAYING (0.1) #define STK_MAX_COMPRESSED_PACKETS_FOR_BITRATE_CALCULATION (2048) #define STK_BUFFERS_NEEDED_TO_START (32) @@ -2049,16 +2049,30 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* if (state == STKAudioPlayerInternalStateWaitingForData) { + int64_t framesRequiredToStartPlaying = audioPlayer->framesRequiredToStartPlaying; + + if (audioPlayer->currentlyPlayingEntry->lastFrameQueued >= 0) + { + framesRequiredToStartPlaying = MIN(framesRequiredToStartPlaying, audioPlayer->currentlyPlayingEntry->lastFrameQueued); + } + if (audioPlayer->currentlyPlayingEntry && audioPlayer->currentlyReadingEntry == audioPlayer->currentlyPlayingEntry - && audioPlayer->currentlyPlayingEntry->framesQueued < audioPlayer->framesRequiredToStartPlaying) + && audioPlayer->currentlyPlayingEntry->framesQueued < framesRequiredToStartPlaying) { waitForBuffer = YES; } } else if (state == STKAudioPlayerInternalStateRebuffering) { - if (used < audioPlayer->framesRequiredToPlayAfterRebuffering) + int64_t framesRequiredToStartPlaying = audioPlayer->framesRequiredToStartPlaying; + + if (audioPlayer->currentlyPlayingEntry->lastFrameQueued >= 0) + { + framesRequiredToStartPlaying = MIN(framesRequiredToStartPlaying, audioPlayer->currentlyPlayingEntry->lastFrameQueued - audioPlayer->currentlyPlayingEntry->framesQueued); + } + + if (used < framesRequiredToStartPlaying) { waitForBuffer = YES; } @@ -2220,6 +2234,10 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* extraFramesPlayedNotAssigned -= framesPlayedForCurrent; } + else + { + break; + } } } From ae63d04b872bb8d0eb13cde17d111100b00ab6bf Mon Sep 17 00:00:00 2001 From: Thong Nguyen Date: Fri, 31 Jan 2014 23:02:19 +0000 Subject: [PATCH 23/33] Don't stop audio player immediately when finished playing---wait until next runloop --- StreamingKit/StreamingKit/STKAudioPlayer.m | 8 -------- 1 file changed, 8 deletions(-) diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index 71fd5de..2f752fe 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -933,14 +933,6 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn [self.delegate audioPlayer:self didFinishPlayingQueueItemId:queueItemId withReason:stopReason andProgress:progress andDuration:duration]; }]; } - - if (currentlyReadingEntry == nil) - { - if (upcomingQueue.count == 0) - { - [self stopAudioUnitWithReason:STKAudioPlayerStopReasonEof]; - } - } } [self wakeupPlaybackThread]; From c4a7251d19792a332f11b159b36356888c76ac55 Mon Sep 17 00:00:00 2001 From: Thong Nguyen Date: Fri, 31 Jan 2014 23:07:49 +0000 Subject: [PATCH 24/33] Fixed potential crash when OutputRenderCallback is called with no items in the queue or playing --- StreamingKit/StreamingKit/STKAudioPlayer.m | 72 +++++++++++----------- 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index 2f752fe..36f5736 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -2036,47 +2036,49 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* UInt32 start = audioPlayer->pcmBufferFrameStartIndex; UInt32 end = (audioPlayer->pcmBufferFrameStartIndex + audioPlayer->pcmBufferUsedFrameCount) % audioPlayer->pcmBufferTotalFrameCount; BOOL signal = audioPlayer->waiting && used < audioPlayer->pcmBufferTotalFrameCount / 2; - + NSArray* frameFilters = audioPlayer->frameFilters; + STKAudioPlayerInternalState state = audioPlayer.internalState; - if (state == STKAudioPlayerInternalStateWaitingForData) - { - int64_t framesRequiredToStartPlaying = audioPlayer->framesRequiredToStartPlaying; - - if (audioPlayer->currentlyPlayingEntry->lastFrameQueued >= 0) - { - framesRequiredToStartPlaying = MIN(framesRequiredToStartPlaying, audioPlayer->currentlyPlayingEntry->lastFrameQueued); - } - - if (audioPlayer->currentlyPlayingEntry - && audioPlayer->currentlyReadingEntry == audioPlayer->currentlyPlayingEntry - && audioPlayer->currentlyPlayingEntry->framesQueued < framesRequiredToStartPlaying) - { - waitForBuffer = YES; - } - } - else if (state == STKAudioPlayerInternalStateRebuffering) - { - int64_t framesRequiredToStartPlaying = audioPlayer->framesRequiredToStartPlaying; - - if (audioPlayer->currentlyPlayingEntry->lastFrameQueued >= 0) - { - framesRequiredToStartPlaying = MIN(framesRequiredToStartPlaying, audioPlayer->currentlyPlayingEntry->lastFrameQueued - audioPlayer->currentlyPlayingEntry->framesQueued); - } - - if (used < framesRequiredToStartPlaying) - { - waitForBuffer = YES; - } - } - else if (state == STKAudioPlayerInternalStatePendingNext) + if (state == STKAudioPlayerInternalStatePendingNext) { OSSpinLockUnlock(&audioPlayer->pcmBufferSpinLock); return 0; } - - NSArray* frameFilters = audioPlayer->frameFilters; + + if (entry) + { + if (state == STKAudioPlayerInternalStateWaitingForData) + { + int64_t framesRequiredToStartPlaying = audioPlayer->framesRequiredToStartPlaying; + + if (entry->lastFrameQueued >= 0) + { + framesRequiredToStartPlaying = MIN(framesRequiredToStartPlaying, audioPlayer->currentlyPlayingEntry->lastFrameQueued); + } + + if (entry && audioPlayer->currentlyReadingEntry == entry + && entry->framesQueued < framesRequiredToStartPlaying) + { + waitForBuffer = YES; + } + } + else if (state == STKAudioPlayerInternalStateRebuffering) + { + int64_t framesRequiredToStartPlaying = audioPlayer->framesRequiredToStartPlaying; + + if (audioPlayer->currentlyPlayingEntry->lastFrameQueued >= 0) + { + framesRequiredToStartPlaying = MIN(framesRequiredToStartPlaying, entry->lastFrameQueued - entry->framesQueued); + } + + if (used < framesRequiredToStartPlaying) + { + waitForBuffer = YES; + } + } + } OSSpinLockUnlock(&audioPlayer->pcmBufferSpinLock); @@ -2155,7 +2157,7 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* if (frameFilters) { - int count = frameFilters.count; + NSUInteger count = frameFilters.count; AudioStreamBasicDescription asbd = audioPlayer->canonicalAudioStreamBasicDescription; for (int i = 0; i < count; i++) From a9b1e143a0dd1aa9fdf028c3435f9166b26213ef Mon Sep 17 00:00:00 2001 From: Thong Nguyen Date: Fri, 31 Jan 2014 23:19:29 +0000 Subject: [PATCH 25/33] queueDataSource shold let playback thread automatically start the audio unit --- StreamingKit/StreamingKit/STKAudioPlayer.m | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index 36f5736..defa6cc 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -497,11 +497,6 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn pthread_mutex_lock(&playerMutex); { [upcomingQueue enqueue:[[STKQueueEntry alloc] initWithDataSource:dataSourceIn andQueueItemId:queueItemId]]; - - if (self.internalState == STKAudioPlayerInternalStateStopped && stopReason == STKAudioPlayerStopReasonEof) - { - [self startAudioUnit]; - } } pthread_mutex_unlock(&playerMutex); @@ -1714,6 +1709,8 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl { OSStatus status; + [self resetPcmBuffers]; + status = AudioOutputUnitStart(audioUnit); if (status) From 56b2dd4ace08afcfea7acb7925a01a876ce9a964 Mon Sep 17 00:00:00 2001 From: Thong Nguyen Date: Sat, 1 Feb 2014 00:23:23 +0000 Subject: [PATCH 26/33] Better range/length handling in STKHTTPDataSource. Handles out-of-range requests gracefully --- StreamingKit/StreamingKit/STKAudioPlayer.m | 2 - .../STKAutoRecoveringHTTPDataSource.m | 2 +- StreamingKit/StreamingKit/STKHTTPDataSource.m | 81 ++++++++++++------- 3 files changed, 53 insertions(+), 32 deletions(-) diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index defa6cc..e6d0205 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -1205,9 +1205,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn } [currentEntry updateAudioDataSource]; - [currentEntry reset]; - [currentEntry.dataSource seekToOffset:seekByteOffset]; self.internalState = STKAudioPlayerInternalStateWaitingForDataAfterSeek; diff --git a/StreamingKit/StreamingKit/STKAutoRecoveringHTTPDataSource.m b/StreamingKit/StreamingKit/STKAutoRecoveringHTTPDataSource.m index 286ac7d..87db858 100644 --- a/StreamingKit/StreamingKit/STKAutoRecoveringHTTPDataSource.m +++ b/StreamingKit/StreamingKit/STKAutoRecoveringHTTPDataSource.m @@ -230,7 +230,7 @@ static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReach { NSLog(@"dataSourceEof"); - if ([self position] != [self length]) + if ([self position] < [self length]) { [self processRetryOnError]; diff --git a/StreamingKit/StreamingKit/STKHTTPDataSource.m b/StreamingKit/StreamingKit/STKHTTPDataSource.m index 287e8b5..7874015 100644 --- a/StreamingKit/StreamingKit/STKHTTPDataSource.m +++ b/StreamingKit/StreamingKit/STKHTTPDataSource.m @@ -138,40 +138,61 @@ -(void) dataAvailable { - if (fileLength < 0) - { - CFTypeRef response = CFReadStreamCopyProperty(stream, kCFStreamPropertyHTTPResponseHeader); + if (self.httpStatusCode == 0) + { + CFTypeRef response = CFReadStreamCopyProperty(stream, kCFStreamPropertyHTTPResponseHeader); httpHeaders = (__bridge_transfer NSDictionary*)CFHTTPMessageCopyAllHeaderFields((CFHTTPMessageRef)response); self.httpStatusCode = CFHTTPMessageGetResponseStatusCode((CFHTTPMessageRef)response); CFRelease(response); - - if (self.httpStatusCode == 200) - { - if (seekStart == 0) - { - fileLength = (long long)[[httpHeaders objectForKey:@"Content-Length"] integerValue]; - } - - NSString* contentType = [httpHeaders objectForKey:@"Content-Type"]; - AudioFileTypeID typeIdFromMimeType = [STKHTTPDataSource audioFileTypeHintFromMimeType:contentType]; - - if (typeIdFromMimeType != 0) - { - audioFileTypeHint = typeIdFromMimeType; - } - } - else - { - [self errorOccured]; - - return; - } - } - - [super dataAvailable]; + + if (self.httpStatusCode == 200) + { + if (seekStart == 0) + { + fileLength = (long long)[[httpHeaders objectForKey:@"Content-Length"] integerValue]; + } + + NSString* contentType = [httpHeaders objectForKey:@"Content-Type"]; + AudioFileTypeID typeIdFromMimeType = [STKHTTPDataSource audioFileTypeHintFromMimeType:contentType]; + + if (typeIdFromMimeType != 0) + { + audioFileTypeHint = typeIdFromMimeType; + } + } + else if (self.httpStatusCode == 206) + { + NSString* contentRange = [httpHeaders objectForKey:@"Content-Range"]; + NSArray* components = [contentRange componentsSeparatedByString:@"/"]; + + if (components.count == 2) + { + fileLength = [[components objectAtIndex:1] integerValue]; + } + } + else if (self.httpStatusCode == 416) + { + if (self.length >= 0) + { + seekStart = self.length; + } + + [self eof]; + + return; + } + else if (self.httpStatusCode >= 300) + { + [self errorOccured]; + + return; + } + } + + [super dataAvailable]; } -(long long) position @@ -196,7 +217,7 @@ CFReadStreamClose(stream); CFRelease(stream); } - + NSAssert([NSRunLoop currentRunLoop] == eventsRunLoop, @"Seek called on wrong thread"); stream = 0; @@ -296,6 +317,8 @@ [self reregisterForEvents]; + self.httpStatusCode = 0; + // Open if (!CFReadStreamOpen(stream)) From 98c7d9b8f036fc7e8a70a0feee8f11b4d57afdf5 Mon Sep 17 00:00:00 2001 From: Thong Nguyen Date: Sat, 1 Feb 2014 00:29:02 +0000 Subject: [PATCH 27/33] STKAudioPlayer:dataSourceFromURL uses case insensitive scheme compare --- StreamingKit/StreamingKit/STKAudioPlayer.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index e6d0205..9597b48 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -396,7 +396,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn { retval = [[STKLocalFileDataSource alloc] initWithFilePath:url.path]; } - else if ([url.scheme isEqualToString:@"http"] || [url.scheme isEqualToString:@"https"]) + else if ([url.scheme caseInsensitiveCompare:@"http"] == NSOrderedSame || [url.scheme caseInsensitiveCompare:@"https"] == NSOrderedSame) { retval = [[STKAutoRecoveringHTTPDataSource alloc] initWithHTTPDataSource:[[STKHTTPDataSource alloc] initWithURL:url]]; } From 4d73784338e36a9bec25ee49fc7673d7ed58f2d9 Mon Sep 17 00:00:00 2001 From: Thong Nguyen Date: Sat, 1 Feb 2014 23:04:55 +0000 Subject: [PATCH 28/33] Added audio frame filtering and level metering support using Audio Units --- ExampleApp/ExampleApp/AppDelegate.m | 2 + ExampleApp/ExampleApp/AudioPlayerView.h | 1 + ExampleApp/ExampleApp/AudioPlayerView.m | 12 +- StreamingKit/StreamingKit/STKAudioPlayer.h | 11 +- StreamingKit/StreamingKit/STKAudioPlayer.m | 175 ++++++++++++++++++++- 5 files changed, 192 insertions(+), 9 deletions(-) diff --git a/ExampleApp/ExampleApp/AppDelegate.m b/ExampleApp/ExampleApp/AppDelegate.m index 9f654f1..1bba755 100644 --- a/ExampleApp/ExampleApp/AppDelegate.m +++ b/ExampleApp/ExampleApp/AppDelegate.m @@ -33,6 +33,8 @@ self.window.backgroundColor = [UIColor whiteColor]; audioPlayer = [[STKAudioPlayer alloc] init]; + audioPlayer.meteringEnabled = YES; + AudioPlayerView* audioPlayerView = [[AudioPlayerView alloc] initWithFrame:self.window.bounds]; audioPlayerView.delegate = self; diff --git a/ExampleApp/ExampleApp/AudioPlayerView.h b/ExampleApp/ExampleApp/AudioPlayerView.h index 0cbf808..fff2561 100644 --- a/ExampleApp/ExampleApp/AudioPlayerView.h +++ b/ExampleApp/ExampleApp/AudioPlayerView.h @@ -58,6 +58,7 @@ UIButton* queueShortFileButton; UIButton* queuePcmWaveFileFromHTTPButton; UIButton* playFromLocalFileButton; + UIView* meter; } @property (readwrite, retain) STKAudioPlayer* audioPlayer; diff --git a/ExampleApp/ExampleApp/AudioPlayerView.m b/ExampleApp/ExampleApp/AudioPlayerView.m index 98c40bc..70cb75d 100644 --- a/ExampleApp/ExampleApp/AudioPlayerView.m +++ b/ExampleApp/ExampleApp/AudioPlayerView.m @@ -101,6 +101,11 @@ statusLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, slider.frame.origin.y + slider.frame.size.height + label.frame.size.height + 8, frame.size.width, 50)]; statusLabel.textAlignment = NSTextAlignmentCenter; + + + meter = [[UIView alloc] initWithFrame:CGRectMake(0, 460, 0, 20)]; + + meter.backgroundColor = [UIColor greenColor]; [self addSubview:slider]; [self addSubview:playButton]; @@ -112,6 +117,7 @@ [self addSubview:label]; [self addSubview:statusLabel]; [self addSubview:stopButton]; + [self addSubview:meter]; [self setupTimer]; [self updateControls]; @@ -134,7 +140,7 @@ -(void) setupTimer { - timer = [NSTimer timerWithTimeInterval:0.05 target:self selector:@selector(tick) userInfo:nil repeats:YES]; + timer = [NSTimer timerWithTimeInterval:0.01 target:self selector:@selector(tick) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; } @@ -168,6 +174,10 @@ } statusLabel.text = audioPlayer.state == STKAudioPlayerStateBuffering ? @"buffering" : @""; + + CGFloat newWidth = 320 * (([audioPlayer peakPowerInDecibelsForChannel:1] + 60) / 60); + + meter.frame = CGRectMake(0, 460, newWidth, 20); } -(void) playFromHTTPButtonTouched diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.h b/StreamingKit/StreamingKit/STKAudioPlayer.h index 4e5149c..ad49d88 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.h +++ b/StreamingKit/StreamingKit/STKAudioPlayer.h @@ -118,12 +118,13 @@ STKAudioPlayerOptions; @end -typedef void(^STKFrameFilter)(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UInt32 frameCount, UInt32* frames); +typedef void(^STKFrameFilter)(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UInt32 frameCount, void* frames); @interface STKAudioPlayer : NSObject @property (readonly) double duration; @property (readonly) double progress; +@property (readwrite) BOOL meteringEnabled; @property (readonly) NSArray* frameFilters; @property (readwrite) STKAudioPlayerState state; @property (readonly) STKAudioPlayerOptions options; @@ -167,7 +168,11 @@ typedef void(^STKFrameFilter)(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UIn /// The QueueItemId of the currently playing item -(NSObject*) currentlyPlayingQueueItemId; --(void) appendFrameFilter:(STKFrameFilter)frameFilter withName:(NSString*)name; --(void) addFrameFilter:(STKFrameFilter)frameFilter withName:(NSString*)name afterFilterWithName:(NSString*)afterFilterWithName; +-(void) removeFrameFilterWithName:(NSString*)name; +-(void) appendFrameFilterWithName:(NSString*)name block:(STKFrameFilter)block; +-(void) addFrameFilterWithName:(NSString*)name afterFilterWithName:(NSString*)afterFilterWithName block:(STKFrameFilter)block; + +-(float) peakPowerInDecibelsForChannel:(NSUInteger)channelNumber; +-(float) averagePowerInDecibelsForChannel:(NSUInteger)channelNumber; @end diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index 9597b48..28a4e53 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -41,6 +41,10 @@ #import "NSMutableArray+STKAudioPlayer.h" #import "libkern/OSAtomic.h" +#define STK_DBMIN (-60) +#define STK_DBOFFSET (-74.0) +#define STK_LOWPASSFILTERTIMESLICE (0.0005) + #define STK_DEFAULT_PCM_BUFFER_SIZE_IN_SECONDS (10) #define STK_DEFAULT_SECONDS_REQUIRED_TO_START_PLAYING (0.1) #define STK_MAX_COMPRESSED_PACKETS_FOR_BITRATE_CALCULATION (2048) @@ -79,8 +83,11 @@ UInt8* readBuffer; int readBufferSize; + Float32 peakPowerDb[2]; + Float32 averagePowerDb[2]; + + BOOL meteringEnabled; STKAudioPlayerOptions options; - AudioComponentInstance audioUnit; UInt32 framesRequiredToStartPlaying; @@ -280,7 +287,7 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn pcmAudioBufferList.mBuffers[0].mDataByteSize = (canonicalAudioStreamBasicDescription.mSampleRate * STK_DEFAULT_PCM_BUFFER_SIZE_IN_SECONDS) * canonicalAudioStreamBasicDescription.mBytesPerFrame; pcmAudioBufferList.mBuffers[0].mData = (void*)calloc(pcmAudioBuffer->mDataByteSize, 1); pcmAudioBufferList.mBuffers[0].mNumberChannels = 2; - + pcmBufferFrameSizeInBytes = canonicalAudioStreamBasicDescription.mBytesPerFrame; pcmBufferTotalFrameCount = pcmAudioBuffer->mDataByteSize / pcmBufferFrameSizeInBytes; @@ -304,6 +311,8 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn upcomingQueue = [[NSMutableArray alloc] init]; bufferingQueue = [[NSMutableArray alloc] init]; + + [self resetPcmBuffers]; [self createAudioUnit]; [self createPlaybackThread]; @@ -1415,6 +1424,10 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn self->pcmBufferFrameStartIndex = 0; self->pcmBufferUsedFrameCount = 0; + self->peakPowerDb[0] = STK_DBMIN; + self->peakPowerDb[1] = STK_DBMIN; + self->averagePowerDb[0] = STK_DBMIN; + self->averagePowerDb[1] = STK_DBMIN; OSSpinLockUnlock(&pcmBufferSpinLock); } @@ -1734,6 +1747,8 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl } status = AudioOutputUnitStop(audioUnit); + + [self resetPcmBuffers]; if (status) { @@ -2237,14 +2252,162 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* return 0; } +-(float) peakPowerInDecibelsForChannel:(NSUInteger)channelNumber +{ + if (channelNumber >= canonicalAudioStreamBasicDescription.mChannelsPerFrame) + { + return 0; + } + + return peakPowerDb[channelNumber]; +} + +-(float) averagePowerInDecibelsForChannel:(NSUInteger)channelNumber +{ + if (channelNumber >= canonicalAudioStreamBasicDescription.mChannelsPerFrame) + { + return 0; + } + + return averagePowerDb[channelNumber]; +} + +-(BOOL) meteringEnabled +{ + return self->meteringEnabled; +} + +#define CALCULATE_METER(channel) \ + Float32 currentFilteredValueOfSampleAmplitude##channel = STK_LOWPASSFILTERTIMESLICE * absoluteValueOfSampleAmplitude##channel + (1.0 - STK_LOWPASSFILTERTIMESLICE) * previousFilteredValueOfSampleAmplitude##channel; \ + previousFilteredValueOfSampleAmplitude##channel = currentFilteredValueOfSampleAmplitude##channel; \ + Float32 sampleDB##channel = 20.0 * log10(currentFilteredValueOfSampleAmplitude##channel) + STK_DBOFFSET; \ + if ((sampleDB##channel == sampleDB##channel) && (sampleDB##channel != -DBL_MAX)) \ + { \ + if(sampleDB##channel > peakValue##channel) \ + { \ + peakValue##channel = MIN(sampleDB##channel, 0); \ + } \ + if (sampleDB##channel > -DBL_MAX) \ + { \ + totalValue##channel += sampleDB##channel; \ + } \ + decibels##channel = peakValue##channel; \ + }; + +-(void) setMeteringEnabled:(BOOL)value +{ + if (self->meteringEnabled == value) + { + return; + } + + if (!value) + { + [self removeFrameFilterWithName:@"STKMeteringFilter"]; + self->meteringEnabled = NO; + } + else + { + [self appendFrameFilterWithName:@"STKMeteringFilter" block:^(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UInt32 frameCount, void* frames) + { + SInt16* samples = (SInt16*)frames; + Float32 decibelsLeft = STK_DBMIN; + Float32 peakValueLeft = STK_DBMIN; + Float64 totalValueLeft = STK_DBMIN; + Float32 previousFilteredValueOfSampleAmplitudeLeft = 0; + Float32 decibelsRight = STK_DBMIN; + Float32 peakValueRight = STK_DBMIN; + Float64 totalValueRight = STK_DBMIN; + Float32 previousFilteredValueOfSampleAmplitudeRight = 0; + + for (int i = 0; i < frameCount * 2; i++) + { + Float32 absoluteValueOfSampleAmplitudeLeft = abs(samples[i]); + Float32 absoluteValueOfSampleAmplitudeRight = abs(samples[i]); + + CALCULATE_METER(Left); + CALCULATE_METER(Right); + } + + 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); + }]; + } +} + -(NSArray*) frameFilters { return frameFilters; } --(void) appendFrameFilter:(STKFrameFilter)frameFilter withName:(NSString*)name +-(void) appendFrameFilterWithName:(NSString*)name block:(STKFrameFilter)block { - [self addFrameFilter:frameFilter withName:name afterFilterWithName:nil]; + [self addFrameFilterWithName:name afterFilterWithName:nil block:block]; +} + +-(void) removeFrameFilterWithName:(NSString*)name +{ + pthread_mutex_lock(&self->playerMutex); + + NSMutableArray* newFrameFilters = [[NSMutableArray alloc] initWithCapacity:frameFilters.count + 1]; + + for (STKFrameFilterEntry* filterEntry in frameFilters) + { + if (![filterEntry->name isEqualToString:name]) + { + [newFrameFilters addObject:filterEntry]; + } + } + + NSArray* replacement = [NSArray arrayWithArray:newFrameFilters]; + + OSSpinLockLock(&pcmBufferSpinLock); + if (newFrameFilters.count > 0) + { + frameFilters = replacement; + } + else + { + frameFilters = nil; + } + OSSpinLockUnlock(&pcmBufferSpinLock); + + pthread_mutex_unlock(&self->playerMutex); +} + +-(void) addFrameFilterWithName:(NSString*)name afterFilterWithName:(NSString*)afterFilterWithName block:(STKFrameFilter)block +{ + pthread_mutex_lock(&self->playerMutex); + + NSMutableArray* newFrameFilters = [[NSMutableArray alloc] initWithCapacity:frameFilters.count + 1]; + + if (afterFilterWithName == nil) + { + [newFrameFilters addObject:[[STKFrameFilterEntry alloc] initWithFilter:block andName:name]]; + [newFrameFilters addObjectsFromArray:frameFilters]; + } + else + { + for (STKFrameFilterEntry* filterEntry in frameFilters) + { + if (afterFilterWithName != nil && [filterEntry->name isEqualToString:afterFilterWithName]) + { + [newFrameFilters addObject:[[STKFrameFilterEntry alloc] initWithFilter:block andName:name]]; + } + + [newFrameFilters addObject:filterEntry]; + } + } + + NSArray* replacement = [NSArray arrayWithArray:newFrameFilters]; + + OSSpinLockLock(&pcmBufferSpinLock); + frameFilters = replacement; + OSSpinLockUnlock(&pcmBufferSpinLock); + + pthread_mutex_unlock(&self->playerMutex); } -(void) addFrameFilter:(STKFrameFilter)frameFilter withName:(NSString*)name afterFilterWithName:(NSString*)afterFilterWithName @@ -2271,8 +2434,10 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* } } + NSArray* replacement = [NSArray arrayWithArray:newFrameFilters]; + OSSpinLockLock(&pcmBufferSpinLock); - frameFilters = [NSArray arrayWithArray:newFrameFilters]; + frameFilters = replacement; OSSpinLockUnlock(&pcmBufferSpinLock); pthread_mutex_unlock(&self->playerMutex); From d347e5e80c920652d797a3e6667a65632bbaba5a Mon Sep 17 00:00:00 2001 From: Thong Nguyen Date: Sat, 1 Feb 2014 23:24:06 +0000 Subject: [PATCH 29/33] Added mute/unmute support --- ExampleApp/ExampleApp/AudioPlayerView.h | 1 + ExampleApp/ExampleApp/AudioPlayerView.m | 26 ++++++++++-- StreamingKit/StreamingKit/STKAudioPlayer.h | 1 + StreamingKit/StreamingKit/STKAudioPlayer.m | 49 +++++++++++++++++++--- 4 files changed, 68 insertions(+), 9 deletions(-) diff --git a/ExampleApp/ExampleApp/AudioPlayerView.h b/ExampleApp/ExampleApp/AudioPlayerView.h index fff2561..92d06cb 100644 --- a/ExampleApp/ExampleApp/AudioPlayerView.h +++ b/ExampleApp/ExampleApp/AudioPlayerView.h @@ -52,6 +52,7 @@ UILabel* statusLabel; UISlider* slider; UISwitch* repeatSwitch; + UIButton* muteButton; UIButton* playButton; UIButton* stopButton; UIButton* playFromHTTPButton; diff --git a/ExampleApp/ExampleApp/AudioPlayerView.m b/ExampleApp/ExampleApp/AudioPlayerView.m index 70cb75d..ac3b71e 100644 --- a/ExampleApp/ExampleApp/AudioPlayerView.m +++ b/ExampleApp/ExampleApp/AudioPlayerView.m @@ -75,7 +75,7 @@ [queuePcmWaveFileFromHTTPButton addTarget:self action:@selector(queuePcmWaveFileButtonTouched) forControlEvents:UIControlEventTouchUpInside]; [queuePcmWaveFileFromHTTPButton setTitle:@"Queue PCM/WAVE from HTTP" forState:UIControlStateNormal]; - size = CGSizeMake(90, 50); + size = CGSizeMake(90, 40); playButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; playButton.frame = CGRectMake(30, 380, size.width, size.height); @@ -86,6 +86,11 @@ [stopButton addTarget:self action:@selector(stopButtonPressed) forControlEvents:UIControlEventTouchUpInside]; [stopButton setTitle:@"Stop" forState:UIControlStateNormal]; + muteButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; + muteButton.frame = CGRectMake((320 - size.width) - 30, 410, size.width, size.height); + [muteButton addTarget:self action:@selector(muteButtonPressed) forControlEvents:UIControlEventTouchUpInside]; + [muteButton setTitle:@"Mute" forState:UIControlStateNormal]; + slider = [[UISlider alloc] initWithFrame:CGRectMake(20, 320, 280, 20)]; slider.continuous = YES; [slider addTarget:self action:@selector(sliderChanged) forControlEvents:UIControlEventValueChanged]; @@ -94,7 +99,7 @@ repeatSwitch = [[UISwitch alloc] initWithFrame:CGRectMake((320 - size.width) / 2, frame.size.height * 0.15 + 180, size.width, size.height)]; - label = [[UILabel alloc] initWithFrame:CGRectMake(0, slider.frame.origin.y + slider.frame.size.height, frame.size.width, 50)]; + label = [[UILabel alloc] initWithFrame:CGRectMake(0, slider.frame.origin.y + slider.frame.size.height, frame.size.width, 25)]; label.textAlignment = NSTextAlignmentCenter; @@ -103,7 +108,7 @@ statusLabel.textAlignment = NSTextAlignmentCenter; - meter = [[UIView alloc] initWithFrame:CGRectMake(0, 460, 0, 20)]; + meter = [[UIView alloc] initWithFrame:CGRectMake(0, 450, 0, 20)]; meter.backgroundColor = [UIColor greenColor]; @@ -118,6 +123,7 @@ [self addSubview:statusLabel]; [self addSubview:stopButton]; [self addSubview:meter]; + [self addSubview:muteButton]; [self setupTimer]; [self updateControls]; @@ -200,6 +206,20 @@ [self.delegate audioPlayerViewQueuePcmWaveFileSelected:self]; } +-(void) muteButtonPressed +{ + audioPlayer.muted = !audioPlayer.muted; + + if (audioPlayer.muted) + { + [muteButton setTitle:@"Unmute" forState:UIControlStateNormal]; + } + else + { + [muteButton setTitle:@"Mute" forState:UIControlStateNormal]; + } +} + -(void) stopButtonPressed { [audioPlayer stop]; diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.h b/StreamingKit/StreamingKit/STKAudioPlayer.h index ad49d88..2f7a43c 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.h +++ b/StreamingKit/StreamingKit/STKAudioPlayer.h @@ -122,6 +122,7 @@ typedef void(^STKFrameFilter)(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UIn @interface STKAudioPlayer : NSObject +@property (readwrite) BOOL muted; @property (readonly) double duration; @property (readonly) double progress; @property (readwrite) BOOL meteringEnabled; diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index 28a4e53..201449e 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -80,6 +80,8 @@ @interface STKAudioPlayer() { + BOOL muted; + UInt8* readBuffer; int readBufferSize; @@ -1509,14 +1511,24 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn } } +-(BOOL) muted +{ + return self->muted; +} + +-(void) setMuted:(BOOL)value +{ + self->muted = value; +} + -(void) mute { - // TODO + self.muted = YES; } -(void) unmute { - // TODO + self.muted = NO; } -(void) dispose @@ -2039,7 +2051,8 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* OSSpinLockLock(&audioPlayer->pcmBufferSpinLock); BOOL waitForBuffer = NO; - STKQueueEntry* entry = audioPlayer->currentlyPlayingEntry; + BOOL muted = audioPlayer->muted; + STKQueueEntry* entry = audioPlayer->currentlyPlayingEntry; AudioBuffer* audioBuffer = audioPlayer->pcmAudioBuffer; UInt32 frameSizeInBytes = audioPlayer->pcmBufferFrameSizeInBytes; UInt32 used = audioPlayer->pcmBufferUsedFrameCount; @@ -2111,7 +2124,15 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* ioData->mBuffers[0].mNumberChannels = 2; ioData->mBuffers[0].mDataByteSize = frameSizeInBytes * framesToCopy; - memcpy(ioData->mBuffers[0].mData, audioBuffer->mData + (start * frameSizeInBytes), ioData->mBuffers[0].mDataByteSize); + + if (muted) + { + memset(ioData->mBuffers[0].mData, 0, ioData->mBuffers[0].mDataByteSize); + } + else + { + memcpy(ioData->mBuffers[0].mData, audioBuffer->mData + (start * frameSizeInBytes), ioData->mBuffers[0].mDataByteSize); + } totalFramesCopied = framesToCopy; @@ -2126,7 +2147,15 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* ioData->mBuffers[0].mNumberChannels = 2; ioData->mBuffers[0].mDataByteSize = frameSizeInBytes * framesToCopy; - memcpy(ioData->mBuffers[0].mData, audioBuffer->mData + (start * frameSizeInBytes), ioData->mBuffers[0].mDataByteSize); + + if (muted) + { + memset(ioData->mBuffers[0].mData, 0, ioData->mBuffers[0].mDataByteSize); + } + else + { + memcpy(ioData->mBuffers[0].mData, audioBuffer->mData + (start * frameSizeInBytes), ioData->mBuffers[0].mDataByteSize); + } UInt32 moreFramesToCopy = 0; UInt32 delta = inNumberFrames - framesToCopy; @@ -2137,7 +2166,15 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* ioData->mBuffers[0].mNumberChannels = 2; ioData->mBuffers[0].mDataByteSize += frameSizeInBytes * moreFramesToCopy; - memcpy(ioData->mBuffers[0].mData + (framesToCopy * frameSizeInBytes), audioBuffer->mData, frameSizeInBytes * moreFramesToCopy); + + if (muted) + { + memset(ioData->mBuffers[0].mData + (framesToCopy * frameSizeInBytes), 0, frameSizeInBytes * moreFramesToCopy); + } + else + { + memcpy(ioData->mBuffers[0].mData + (framesToCopy * frameSizeInBytes), audioBuffer->mData, frameSizeInBytes * moreFramesToCopy); + } } totalFramesCopied = framesToCopy + moreFramesToCopy; From 018da7a2eb2f2c2b994f46157ccc9ba68067eb27 Mon Sep 17 00:00:00 2001 From: Thong Nguyen Date: Sat, 1 Feb 2014 23:29:39 +0000 Subject: [PATCH 30/33] Slight layout fix for AudioPlayerView --- ExampleApp/ExampleApp/AudioPlayerView.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ExampleApp/ExampleApp/AudioPlayerView.m b/ExampleApp/ExampleApp/AudioPlayerView.m index ac3b71e..6ac44ac 100644 --- a/ExampleApp/ExampleApp/AudioPlayerView.m +++ b/ExampleApp/ExampleApp/AudioPlayerView.m @@ -99,7 +99,7 @@ repeatSwitch = [[UISwitch alloc] initWithFrame:CGRectMake((320 - size.width) / 2, frame.size.height * 0.15 + 180, size.width, size.height)]; - label = [[UILabel alloc] initWithFrame:CGRectMake(0, slider.frame.origin.y + slider.frame.size.height, frame.size.width, 25)]; + label = [[UILabel alloc] initWithFrame:CGRectMake(0, slider.frame.origin.y + slider.frame.size.height + 10, frame.size.width, 25)]; label.textAlignment = NSTextAlignmentCenter; From 70368a09ba22ce669040661298c0af088c5f4341 Mon Sep 17 00:00:00 2001 From: Thong Nguyen Date: Sun, 2 Feb 2014 12:48:24 +0000 Subject: [PATCH 31/33] Added pendingQueue, pendingQueueCount, mostRecentlyQueuedStillPendingItem. Added more documentation comments. Changed STKAudioPlayerInternalState to be private/hidden --- ExampleApp/ExampleApp/AppDelegate.m | 2 +- ExampleApp/ExampleApp/AudioPlayerView.m | 2 +- .../StreamingKit.xcodeproj/project.pbxproj | 294 ++++++++++++++++++ StreamingKit/StreamingKit/STKAudioPlayer.h | 104 ++++--- StreamingKit/StreamingKit/STKAudioPlayer.m | 158 +++++++--- .../STKAutoRecoveringHTTPDataSource.h | 2 +- .../STKAutoRecoveringHTTPDataSource.m | 2 +- .../StreamingKitMac-Prefix.pch | 9 + .../StreamingKitMac/StreamingKitMac.h | 13 + .../StreamingKitMac/StreamingKitMac.m | 13 + .../StreamingKitMacTests-Info.plist | 22 ++ .../StreamingKitMacTests.m | 34 ++ .../en.lproj/InfoPlist.strings | 2 + 13 files changed, 573 insertions(+), 84 deletions(-) create mode 100644 StreamingKit/StreamingKitMac/StreamingKitMac-Prefix.pch create mode 100644 StreamingKit/StreamingKitMac/StreamingKitMac.h create mode 100644 StreamingKit/StreamingKitMac/StreamingKitMac.m create mode 100644 StreamingKit/StreamingKitMacTests/StreamingKitMacTests-Info.plist create mode 100644 StreamingKit/StreamingKitMacTests/StreamingKitMacTests.m create mode 100644 StreamingKit/StreamingKitMacTests/en.lproj/InfoPlist.strings diff --git a/ExampleApp/ExampleApp/AppDelegate.m b/ExampleApp/ExampleApp/AppDelegate.m index 1bba755..495753b 100644 --- a/ExampleApp/ExampleApp/AppDelegate.m +++ b/ExampleApp/ExampleApp/AppDelegate.m @@ -32,7 +32,7 @@ self.window.backgroundColor = [UIColor whiteColor]; - audioPlayer = [[STKAudioPlayer alloc] init]; + audioPlayer = [[STKAudioPlayer alloc] initWithOptions:STKAudioPlayerOptionFlushQueueOnSeek]; audioPlayer.meteringEnabled = YES; AudioPlayerView* audioPlayerView = [[AudioPlayerView alloc] initWithFrame:self.window.bounds]; diff --git a/ExampleApp/ExampleApp/AudioPlayerView.m b/ExampleApp/ExampleApp/AudioPlayerView.m index 6ac44ac..28ab1a7 100644 --- a/ExampleApp/ExampleApp/AudioPlayerView.m +++ b/ExampleApp/ExampleApp/AudioPlayerView.m @@ -292,7 +292,7 @@ return audioPlayer; } --(void) audioPlayer:(STKAudioPlayer*)audioPlayer stateChanged:(STKAudioPlayerState)state +-(void) audioPlayer:(STKAudioPlayer*)audioPlayer stateChanged:(STKAudioPlayerState)state previousState:(STKAudioPlayerState)previousState { [self updateControls]; } diff --git a/StreamingKit/StreamingKit.xcodeproj/project.pbxproj b/StreamingKit/StreamingKit.xcodeproj/project.pbxproj index 0fabf5d..5b1c441 100644 --- a/StreamingKit/StreamingKit.xcodeproj/project.pbxproj +++ b/StreamingKit/StreamingKit.xcodeproj/project.pbxproj @@ -7,6 +7,22 @@ objects = { /* Begin PBXBuildFile section */ + A1A4996B189E744400E2A2E2 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A4996A189E744400E2A2E2 /* Cocoa.framework */; }; + A1A49975189E744500E2A2E2 /* StreamingKitMac.m in Sources */ = {isa = PBXBuildFile; fileRef = A1A49974189E744500E2A2E2 /* StreamingKitMac.m */; }; + A1A4997B189E744500E2A2E2 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1E7C4D9188D57F60010896F /* XCTest.framework */; }; + A1A4997C189E744500E2A2E2 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A4996A189E744400E2A2E2 /* Cocoa.framework */; }; + A1A4997F189E744500E2A2E2 /* libStreamingKitMac.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A49969189E744400E2A2E2 /* libStreamingKitMac.a */; }; + A1A49985189E744500E2A2E2 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = A1A49983189E744500E2A2E2 /* InfoPlist.strings */; }; + A1A49987189E744500E2A2E2 /* StreamingKitMacTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A1A49986189E744500E2A2E2 /* StreamingKitMacTests.m */; }; + A1A4998E189E745900E2A2E2 /* STKAudioPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4F2188D5E550010896F /* STKAudioPlayer.m */; }; + A1A4998F189E745C00E2A2E2 /* STKAutoRecoveringHTTPDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4F4188D5E550010896F /* STKAutoRecoveringHTTPDataSource.m */; }; + A1A49991189E746000E2A2E2 /* STKCoreFoundationDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4F6188D5E550010896F /* STKCoreFoundationDataSource.m */; }; + A1A49992189E746300E2A2E2 /* STKDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4F8188D5E550010896F /* STKDataSource.m */; }; + A1A49993189E746500E2A2E2 /* STKDataSourceWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4FA188D5E550010896F /* STKDataSourceWrapper.m */; }; + A1A49994189E746900E2A2E2 /* STKHTTPDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4FC188D5E550010896F /* STKHTTPDataSource.m */; }; + A1A49995189E746B00E2A2E2 /* STKLocalFileDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4FE188D5E550010896F /* STKLocalFileDataSource.m */; }; + A1A49996189E746E00E2A2E2 /* STKQueueEntry.m in Sources */ = {isa = PBXBuildFile; fileRef = A1BF65D1189A6582004DD08C /* STKQueueEntry.m */; }; + A1A49997189E747000E2A2E2 /* NSMutableArray+STKAudioPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = A1BF65D4189A65C6004DD08C /* NSMutableArray+STKAudioPlayer.m */; }; A1BF65D2189A6582004DD08C /* STKQueueEntry.m in Sources */ = {isa = PBXBuildFile; fileRef = A1BF65D1189A6582004DD08C /* STKQueueEntry.m */; }; A1BF65D5189A65C6004DD08C /* NSMutableArray+STKAudioPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = A1BF65D4189A65C6004DD08C /* NSMutableArray+STKAudioPlayer.m */; }; A1C9767718981BFE0057F881 /* AudioUnit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1C9767618981BFE0057F881 /* AudioUnit.framework */; }; @@ -27,6 +43,13 @@ /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + A1A4997D189E744500E2A2E2 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = A1E7C4C0188D57F50010896F /* Project object */; + proxyType = 1; + remoteGlobalIDString = A1A49968189E744400E2A2E2; + remoteInfo = StreamingKitMac; + }; A1E7C4DE188D57F60010896F /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = A1E7C4C0188D57F50010896F /* Project object */; @@ -49,6 +72,18 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + A1A49969189E744400E2A2E2 /* libStreamingKitMac.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libStreamingKitMac.a; sourceTree = BUILT_PRODUCTS_DIR; }; + A1A4996A189E744400E2A2E2 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = Library/Frameworks/Cocoa.framework; sourceTree = DEVELOPER_DIR; }; + A1A4996D189E744500E2A2E2 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + A1A4996E189E744500E2A2E2 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; + A1A4996F189E744500E2A2E2 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; + A1A49972189E744500E2A2E2 /* StreamingKitMac-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "StreamingKitMac-Prefix.pch"; sourceTree = ""; }; + A1A49973189E744500E2A2E2 /* StreamingKitMac.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = StreamingKitMac.h; sourceTree = ""; }; + A1A49974189E744500E2A2E2 /* StreamingKitMac.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = StreamingKitMac.m; sourceTree = ""; }; + A1A4997A189E744500E2A2E2 /* StreamingKitMacTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = StreamingKitMacTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + A1A49982189E744500E2A2E2 /* StreamingKitMacTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "StreamingKitMacTests-Info.plist"; sourceTree = ""; }; + A1A49984189E744500E2A2E2 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + A1A49986189E744500E2A2E2 /* StreamingKitMacTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = StreamingKitMacTests.m; sourceTree = ""; }; A1BF65D0189A6582004DD08C /* STKQueueEntry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKQueueEntry.h; sourceTree = ""; }; A1BF65D1189A6582004DD08C /* STKQueueEntry.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKQueueEntry.m; sourceTree = ""; }; A1BF65D3189A65C6004DD08C /* NSMutableArray+STKAudioPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSMutableArray+STKAudioPlayer.h"; sourceTree = ""; }; @@ -80,6 +115,24 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + A1A49966189E744400E2A2E2 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A1A4996B189E744400E2A2E2 /* Cocoa.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A1A49977189E744500E2A2E2 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A1A4997C189E744500E2A2E2 /* Cocoa.framework in Frameworks */, + A1A4997B189E744500E2A2E2 /* XCTest.framework in Frameworks */, + A1A4997F189E744500E2A2E2 /* libStreamingKitMac.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; A1E7C4C5188D57F50010896F /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -103,11 +156,59 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + A1A4996C189E744500E2A2E2 /* Other Frameworks */ = { + isa = PBXGroup; + children = ( + A1A4996D189E744500E2A2E2 /* Foundation.framework */, + A1A4996E189E744500E2A2E2 /* CoreData.framework */, + A1A4996F189E744500E2A2E2 /* AppKit.framework */, + ); + name = "Other Frameworks"; + sourceTree = ""; + }; + A1A49970189E744500E2A2E2 /* StreamingKitMac */ = { + isa = PBXGroup; + children = ( + A1A49973189E744500E2A2E2 /* StreamingKitMac.h */, + A1A49974189E744500E2A2E2 /* StreamingKitMac.m */, + A1A49971189E744500E2A2E2 /* Supporting Files */, + ); + path = StreamingKitMac; + sourceTree = ""; + }; + A1A49971189E744500E2A2E2 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + A1A49972189E744500E2A2E2 /* StreamingKitMac-Prefix.pch */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + A1A49980189E744500E2A2E2 /* StreamingKitMacTests */ = { + isa = PBXGroup; + children = ( + A1A49986189E744500E2A2E2 /* StreamingKitMacTests.m */, + A1A49981189E744500E2A2E2 /* Supporting Files */, + ); + path = StreamingKitMacTests; + sourceTree = ""; + }; + A1A49981189E744500E2A2E2 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + A1A49982189E744500E2A2E2 /* StreamingKitMacTests-Info.plist */, + A1A49983189E744500E2A2E2 /* InfoPlist.strings */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; A1E7C4BF188D57F50010896F = { isa = PBXGroup; children = ( A1E7C4CD188D57F50010896F /* StreamingKit */, A1E7C4E1188D57F60010896F /* StreamingKitTests */, + A1A49970189E744500E2A2E2 /* StreamingKitMac */, + A1A49980189E744500E2A2E2 /* StreamingKitMacTests */, A1E7C4CA188D57F50010896F /* Frameworks */, A1E7C4C9188D57F50010896F /* Products */, ); @@ -118,6 +219,8 @@ children = ( A1E7C4C8188D57F50010896F /* libStreamingKit.a */, A1E7C4D8188D57F60010896F /* StreamingKitTests.xctest */, + A1A49969189E744400E2A2E2 /* libStreamingKitMac.a */, + A1A4997A189E744500E2A2E2 /* StreamingKitMacTests.xctest */, ); name = Products; sourceTree = ""; @@ -129,6 +232,8 @@ A1E7C507188D62D20010896F /* UIKit.framework */, A1E7C4CB188D57F50010896F /* Foundation.framework */, A1E7C4D9188D57F60010896F /* XCTest.framework */, + A1A4996A189E744400E2A2E2 /* Cocoa.framework */, + A1A4996C189E744500E2A2E2 /* Other Frameworks */, ); name = Frameworks; sourceTree = ""; @@ -187,7 +292,52 @@ }; /* End PBXGroup section */ +/* Begin PBXHeadersBuildPhase section */ + A1A49967189E744400E2A2E2 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + /* Begin PBXNativeTarget section */ + A1A49968189E744400E2A2E2 /* StreamingKitMac */ = { + isa = PBXNativeTarget; + buildConfigurationList = A1A4998C189E744500E2A2E2 /* Build configuration list for PBXNativeTarget "StreamingKitMac" */; + buildPhases = ( + A1A49965189E744400E2A2E2 /* Sources */, + A1A49966189E744400E2A2E2 /* Frameworks */, + A1A49967189E744400E2A2E2 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = StreamingKitMac; + productName = StreamingKitMac; + productReference = A1A49969189E744400E2A2E2 /* libStreamingKitMac.a */; + productType = "com.apple.product-type.library.static"; + }; + A1A49979189E744500E2A2E2 /* StreamingKitMacTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = A1A4998D189E744500E2A2E2 /* Build configuration list for PBXNativeTarget "StreamingKitMacTests" */; + buildPhases = ( + A1A49976189E744500E2A2E2 /* Sources */, + A1A49977189E744500E2A2E2 /* Frameworks */, + A1A49978189E744500E2A2E2 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + A1A4997E189E744500E2A2E2 /* PBXTargetDependency */, + ); + name = StreamingKitMacTests; + productName = StreamingKitMacTests; + productReference = A1A4997A189E744500E2A2E2 /* StreamingKitMacTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; A1E7C4C7188D57F50010896F /* StreamingKit */ = { isa = PBXNativeTarget; buildConfigurationList = A1E7C4EB188D57F60010896F /* Build configuration list for PBXNativeTarget "StreamingKit" */; @@ -247,11 +397,21 @@ targets = ( A1E7C4C7188D57F50010896F /* StreamingKit */, A1E7C4D7188D57F60010896F /* StreamingKitTests */, + A1A49968189E744400E2A2E2 /* StreamingKitMac */, + A1A49979189E744500E2A2E2 /* StreamingKitMacTests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + A1A49978189E744500E2A2E2 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A1A49985189E744500E2A2E2 /* InfoPlist.strings in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; A1E7C4D6188D57F60010896F /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -263,6 +423,31 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + A1A49965189E744400E2A2E2 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A1A4998F189E745C00E2A2E2 /* STKAutoRecoveringHTTPDataSource.m in Sources */, + A1A49994189E746900E2A2E2 /* STKHTTPDataSource.m in Sources */, + A1A49991189E746000E2A2E2 /* STKCoreFoundationDataSource.m in Sources */, + A1A49995189E746B00E2A2E2 /* STKLocalFileDataSource.m in Sources */, + A1A49997189E747000E2A2E2 /* NSMutableArray+STKAudioPlayer.m in Sources */, + A1A4998E189E745900E2A2E2 /* STKAudioPlayer.m in Sources */, + A1A49993189E746500E2A2E2 /* STKDataSourceWrapper.m in Sources */, + A1A49992189E746300E2A2E2 /* STKDataSource.m in Sources */, + A1A49975189E744500E2A2E2 /* StreamingKitMac.m in Sources */, + A1A49996189E746E00E2A2E2 /* STKQueueEntry.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A1A49976189E744500E2A2E2 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A1A49987189E744500E2A2E2 /* StreamingKitMacTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; A1E7C4C4188D57F50010896F /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -290,6 +475,11 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + A1A4997E189E744500E2A2E2 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = A1A49968189E744400E2A2E2 /* StreamingKitMac */; + targetProxy = A1A4997D189E744500E2A2E2 /* PBXContainerItemProxy */; + }; A1E7C4DF188D57F60010896F /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = A1E7C4C7188D57F50010896F /* StreamingKit */; @@ -298,6 +488,14 @@ /* End PBXTargetDependency section */ /* Begin PBXVariantGroup section */ + A1A49983189E744500E2A2E2 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + A1A49984189E744500E2A2E2 /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; A1E7C4E4188D57F60010896F /* InfoPlist.strings */ = { isa = PBXVariantGroup; children = ( @@ -309,6 +507,86 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + A1A49988189E744500E2A2E2 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(DEVELOPER_FRAMEWORKS_DIR)", + ); + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "StreamingKitMac/StreamingKitMac-Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + MACOSX_DEPLOYMENT_TARGET = 10.8; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + }; + name = Debug; + }; + A1A49989189E744500E2A2E2 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(DEVELOPER_FRAMEWORKS_DIR)", + ); + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "StreamingKitMac/StreamingKitMac-Prefix.pch"; + MACOSX_DEPLOYMENT_TARGET = 10.8; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + }; + name = Release; + }; + A1A4998A189E744500E2A2E2 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(inherited)", + ); + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "StreamingKitMac/StreamingKitMac-Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = "StreamingKitMacTests/StreamingKitMacTests-Info.plist"; + MACOSX_DEPLOYMENT_TARGET = 10.8; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + WRAPPER_EXTENSION = xctest; + }; + name = Debug; + }; + A1A4998B189E744500E2A2E2 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + FRAMEWORK_SEARCH_PATHS = ( + "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(inherited)", + ); + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "StreamingKitMac/StreamingKitMac-Prefix.pch"; + INFOPLIST_FILE = "StreamingKitMacTests/StreamingKitMacTests-Info.plist"; + MACOSX_DEPLOYMENT_TARGET = 10.8; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + WRAPPER_EXTENSION = xctest; + }; + name = Release; + }; A1E7C4E9188D57F60010896F /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -454,6 +732,22 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + A1A4998C189E744500E2A2E2 /* Build configuration list for PBXNativeTarget "StreamingKitMac" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A1A49988189E744500E2A2E2 /* Debug */, + A1A49989189E744500E2A2E2 /* Release */, + ); + defaultConfigurationIsVisible = 0; + }; + A1A4998D189E744500E2A2E2 /* Build configuration list for PBXNativeTarget "StreamingKitMacTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A1A4998A189E744500E2A2E2 /* Debug */, + A1A4998B189E744500E2A2E2 /* Release */, + ); + defaultConfigurationIsVisible = 0; + }; A1E7C4C3188D57F50010896F /* Build configuration list for PBXProject "StreamingKit" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.h b/StreamingKit/StreamingKit/STKAudioPlayer.h index 2f7a43c..a09a63f 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.h +++ b/StreamingKit/StreamingKit/STKAudioPlayer.h @@ -7,7 +7,7 @@ Inspired by Matt Gallagher's AudioStreamer: https://github.com/mattgallagher/AudioStreamer - Copyright (c) 2012 Thong Nguyen (tumtumtum@gmail.com). All rights reserved. + Copyright (c) 2012-2014 Thong Nguyen (tumtumtum@gmail.com). All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -44,30 +44,12 @@ #include "UIKit/UIApplication.h" #endif -typedef enum -{ - STKAudioPlayerInternalStateInitialised = 0, - STKAudioPlayerInternalStateRunning = 1, - STKAudioPlayerInternalStatePlaying = (1 << 1) | STKAudioPlayerInternalStateRunning, - STKAudioPlayerInternalStateRebuffering = (1 << 2) | STKAudioPlayerInternalStateRunning, - STKAudioPlayerInternalStateStartingThread = (1 << 3) | STKAudioPlayerInternalStateRunning, - STKAudioPlayerInternalStateWaitingForData = (1 << 4) | STKAudioPlayerInternalStateRunning, - /* Same as STKAudioPlayerInternalStateWaitingForData but isn't immediately raised as a buffering event */ - STKAudioPlayerInternalStateWaitingForDataAfterSeek = (1 << 5) | STKAudioPlayerInternalStateRunning, - STKAudioPlayerInternalStatePaused = (1 << 6) | STKAudioPlayerInternalStateRunning, - STKAudioPlayerInternalStateStopped = (1 << 9), - STKAudioPlayerInternalStatePendingNext = (1 << 10), - STKAudioPlayerInternalStateDisposed = (1 << 30), - STKAudioPlayerInternalStateError = (1 << 31) -} -STKAudioPlayerInternalState; - typedef enum { STKAudioPlayerStateReady, STKAudioPlayerStateRunning = 1, STKAudioPlayerStatePlaying = (1 << 1) | STKAudioPlayerStateRunning, - STKAudioPlayerStateBuffering = (1 << 2) | STKAudioPlayerStatePlaying, + STKAudioPlayerStateBuffering = (1 << 2) | STKAudioPlayerStateRunning, STKAudioPlayerStatePaused = (1 << 3) | STKAudioPlayerStateRunning, STKAudioPlayerStateStopped = (1 << 4), STKAudioPlayerStateError = (1 << 5), @@ -77,9 +59,10 @@ STKAudioPlayerState; typedef enum { - STKAudioPlayerStopReasonNoStop = 0, + STKAudioPlayerStopReasonNone = 0, STKAudioPlayerStopReasonEof, - STKAudioPlayerStopReasonUserAction + STKAudioPlayerStopReasonUserAction, + STKAudioPlayerStopReasonError = 0xffff } STKAudioPlayerStopReason; @@ -91,14 +74,14 @@ typedef enum STKAudioPlayerErrorAudioSystemError, STKAudioPlayerErrorCodecError, STKAudioPlayerErrorDataNotFound, - STKAudioPlayerErrorOther = -1 + STKAudioPlayerErrorOther = 0xffff } STKAudioPlayerErrorCode; typedef enum { STKAudioPlayerOptionNone = 0, - STKAudioPlayerOptionDontFlushQueueOnSeek = 1 + STKAudioPlayerOptionFlushQueueOnSeek = 1 } STKAudioPlayerOptions; @@ -106,14 +89,21 @@ STKAudioPlayerOptions; @protocol STKAudioPlayerDelegate --(void) audioPlayer:(STKAudioPlayer*)audioPlayer stateChanged:(STKAudioPlayerState)state; --(void) audioPlayer:(STKAudioPlayer*)audioPlayer unexpectedError:(STKAudioPlayerErrorCode)errorCode; +/// Raised when an item has started playing -(void) audioPlayer:(STKAudioPlayer*)audioPlayer didStartPlayingQueueItemId:(NSObject*)queueItemId; +/// Raised when an item has finished buffering (may or may not be the currently playing item) +/// This event may be raised multiple times for the same item if seek is invoked on the player -(void) audioPlayer:(STKAudioPlayer*)audioPlayer didFinishBufferingSourceWithQueueItemId:(NSObject*)queueItemId; +/// Raised when the state of the player has changed +-(void) audioPlayer:(STKAudioPlayer*)audioPlayer stateChanged:(STKAudioPlayerState)state previousState:(STKAudioPlayerState)previousState; +/// Raised when an item has finished playing -(void) audioPlayer:(STKAudioPlayer*)audioPlayer didFinishPlayingQueueItemId:(NSObject*)queueItemId withReason:(STKAudioPlayerStopReason)stopReason andProgress:(double)progress andDuration:(double)duration; +/// Raised when an unexpected and possibly unrecoverable error has occured (usually best to recreate the STKAudioPlauyer) +-(void) audioPlayer:(STKAudioPlayer*)audioPlayer unexpectedError:(STKAudioPlayerErrorCode)errorCode; @optional +/// Optionally implemented to get logging information from the STKAudioPlayer (used internally for debugging) -(void) audioPlayer:(STKAudioPlayer*)audioPlayer logInfo:(NSString*)line; --(void) audioPlayer:(STKAudioPlayer*)audioPlayer internalStateChanged:(STKAudioPlayerInternalState)state; +/// Raised when items queued items are cleared (usually because of a call to play, setDataSource or stop) -(void) audioPlayer:(STKAudioPlayer*)audioPlayer didCancelQueuedItems:(NSArray*)queuedItems; @end @@ -122,58 +112,100 @@ typedef void(^STKFrameFilter)(UInt32 channelsPerFrame, UInt32 bytesPerFrame, UIn @interface STKAudioPlayer : NSObject +/// Gets or sets the player muted state @property (readwrite) BOOL muted; +/// Gets the current item duration in seconds @property (readonly) double duration; +/// Gets the current item progress in seconds @property (readonly) double progress; +/// Enables or disables peak and average decibel meteting @property (readwrite) BOOL meteringEnabled; +/// 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) +@property (readonly) NSArray* pendingQueue; +/// The number of items pending to be played (includes buffering and upcoming items but does not include the current item) +@property (readonly) NSUInteger pendingQueueCount; +/// Gets the most recently queued item that is still pending to play +@property (readonly) NSObject* mostRecentlyQueuedStillPendingItem; +/// Gets the current state of the player @property (readwrite) STKAudioPlayerState state; +/// Gets the options provided to the player on startup @property (readonly) STKAudioPlayerOptions options; +/// Gets the reason why the player is stopped (if any) @property (readonly) STKAudioPlayerStopReason stopReason; +/// Gets and sets the delegate used for receiving events from the STKAudioPlayer @property (readwrite, unsafe_unretained) id delegate; --(id) init; --(id) initWithReadBufferSize:(int)readBufferSizeIn andOptions:(STKAudioPlayerOptions)options; - -/// Creates a datasource from a given URL -/// URLs with FILE schemes will return an STKLocalFileDataSource -/// URLs with HTTP schemes will return an STKHTTPDataSource wrapped within an STKAutoRecoveringHTTPDataSource -/// URLs with unrecognised schemes will return nil +/// Creates a datasource from a given URL. +/// URLs with FILE schemes will return an STKLocalFileDataSource. +/// URLs with HTTP schemes will return an STKHTTPDataSource wrapped within an STKAutoRecoveringHTTPDataSource. +/// URLs with unrecognised schemes will return nil. +(STKDataSource*) dataSourceFromURL:(NSURL*)url; + +/// Initializes a new STKAudioPlayer with the default options +-(id) init; + +/// Initializes a new STKAudioPlayer with the given options +-(id) initWithOptions:(STKAudioPlayerOptions)optionsIn; + /// Plays an item from the given URL (all pending queued items are removed) -(void) play:(NSString*)urlString; + /// Plays an item from the given URL (all pending queued items are removed) -(void) playWithURL:(NSURL*)url; + /// Plays the given item (all pending queued items are removed) -(void) playWithDataSource:(STKDataSource*)dataSource; -/// Queues a DataSource with the given Item ID for playback + +/// Queues a DataSource with te given Item ID for playback -(void) queueDataSource:(STKDataSource*)dataSource withQueueItemId:(NSObject*)queueItemId; + /// Plays the given item (all pending queued items are removed) -(void) setDataSource:(STKDataSource*)dataSourceIn withQueueItemId:(NSObject*)queueItemId; + /// Seeks to a specific time (in seconds) -(void) seekToTime:(double)value; -/// Clears any upcoming items already queued for playback (does not stop the current item) + +/// Clears any upcoming items already queued for playback (does not stop the current item). +/// The didCancelItems event will be raised for the items removed from the queue. -(void) clearQueue; + /// Pauses playback -(void) pause; + /// Resumes playback from pause -(void) resume; + /// Stops playback of the current file, flushes all the buffers and removes any pending queued items -(void) stop; + /// Mutes playback -(void) mute; + /// Unmutes playback -(void) unmute; + /// Disposes the STKAudioPlayer and frees up all resources before returning -(void) dispose; + /// The QueueItemId of the currently playing item -(NSObject*) currentlyPlayingQueueItemId; +/// Removes a frame filter with the given name -(void) removeFrameFilterWithName:(NSString*)name; + +/// Appends a frame filter with the given name and filter block to the end of the filter chain -(void) appendFrameFilterWithName:(NSString*)name block:(STKFrameFilter)block; + +/// Appends a frame filter with the given name and filter block just after the filter with the given name. +/// 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) -(float) peakPowerInDecibelsForChannel:(NSUInteger)channelNumber; + +/// Reads the average power in decibals for the given channel (0 or 1) -(float) averagePowerInDecibelsForChannel:(NSUInteger)channelNumber; @end diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index 201449e..e72af96 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -41,6 +41,9 @@ #import "NSMutableArray+STKAudioPlayer.h" #import "libkern/OSAtomic.h" +#define kOutputBus 0 +#define kInputBus 1 + #define STK_DBMIN (-60) #define STK_DBOFFSET (-74.0) #define STK_LOWPASSFILTERTIMESLICE (0.0005) @@ -48,19 +51,33 @@ #define STK_DEFAULT_PCM_BUFFER_SIZE_IN_SECONDS (10) #define STK_DEFAULT_SECONDS_REQUIRED_TO_START_PLAYING (0.1) #define STK_MAX_COMPRESSED_PACKETS_FOR_BITRATE_CALCULATION (2048) - -#define STK_BUFFERS_NEEDED_TO_START (32) -#define STK_BUFFERS_NEEDED_WHEN_UNDERUNNING (128) #define STK_DEFAULT_READ_BUFFER_SIZE (64 * 1024) #define STK_DEFAULT_PACKET_BUFFER_SIZE (2048) -#define STK_FRAMES_MISSED_BEFORE_CONSIDERED_UNDERRUN (1024) #define LOGINFO(x) [self logInfo:[NSString stringWithFormat:@"%s %@", sel_getName(_cmd), x]]; +typedef enum +{ + STKAudioPlayerInternalStateInitialised = 0, + STKAudioPlayerInternalStateRunning = 1, + STKAudioPlayerInternalStatePlaying = (1 << 1) | STKAudioPlayerInternalStateRunning, + STKAudioPlayerInternalStateRebuffering = (1 << 2) | STKAudioPlayerInternalStateRunning, + STKAudioPlayerInternalStateStartingThread = (1 << 3) | STKAudioPlayerInternalStateRunning, + STKAudioPlayerInternalStateWaitingForData = (1 << 4) | STKAudioPlayerInternalStateRunning, + /* Same as STKAudioPlayerInternalStateWaitingForData but isn't immediately raised as a buffering event */ + STKAudioPlayerInternalStateWaitingForDataAfterSeek = (1 << 5) | STKAudioPlayerInternalStateRunning, + STKAudioPlayerInternalStatePaused = (1 << 6) | STKAudioPlayerInternalStateRunning, + STKAudioPlayerInternalStateStopped = (1 << 9), + STKAudioPlayerInternalStatePendingNext = (1 << 10), + STKAudioPlayerInternalStateDisposed = (1 << 30), + STKAudioPlayerInternalStateError = (1 << 31) +} +STKAudioPlayerInternalState; + @interface STKFrameFilterEntry : NSObject { @public - NSString* name; + const NSString* name; STKFrameFilter filter; } @end @@ -186,20 +203,13 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn internalState = value; - if ([self.delegate respondsToSelector:@selector(audioPlayer:internalStateChanged:)]) - { - dispatch_async(dispatch_get_main_queue(), ^ - { - [self.delegate audioPlayer:self internalStateChanged:internalState]; - }); - } - STKAudioPlayerState newState; switch (internalState) { case STKAudioPlayerInternalStateInitialised: newState = STKAudioPlayerStateReady; + stopReason = STKAudioPlayerStopReasonNone; break; case STKAudioPlayerInternalStateRunning: case STKAudioPlayerInternalStatePendingNext: @@ -207,32 +217,39 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn case STKAudioPlayerInternalStatePlaying: case STKAudioPlayerInternalStateWaitingForDataAfterSeek: newState = STKAudioPlayerStatePlaying; + stopReason = STKAudioPlayerStopReasonNone; break; case STKAudioPlayerInternalStateRebuffering: case STKAudioPlayerInternalStateWaitingForData: newState = STKAudioPlayerStateBuffering; + stopReason = STKAudioPlayerStopReasonNone; break; case STKAudioPlayerInternalStateStopped: newState = STKAudioPlayerStateStopped; break; case STKAudioPlayerInternalStatePaused: newState = STKAudioPlayerStatePaused; + stopReason = STKAudioPlayerStopReasonNone; break; case STKAudioPlayerInternalStateDisposed: newState = STKAudioPlayerStateDisposed; + stopReason = STKAudioPlayerStopReasonUserAction; break; case STKAudioPlayerInternalStateError: newState = STKAudioPlayerStateError; + stopReason = STKAudioPlayerStopReasonError; break; } - if (newState != self.state) + STKAudioPlayerState previousState = self.state; + + if (newState != previousState) { self.state = newState; dispatch_async(dispatch_get_main_queue(), ^ { - [self.delegate audioPlayer:self stateChanged:self.state]; + [self.delegate audioPlayer:self stateChanged:self.state previousState:previousState]; }); } } @@ -265,6 +282,11 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn return [self initWithReadBufferSize:STK_DEFAULT_READ_BUFFER_SIZE andOptions:STKAudioPlayerOptionNone]; } +-(id) initWithOptions:(STKAudioPlayerOptions)optionsIn +{ + return [self initWithReadBufferSize:STK_DEFAULT_READ_BUFFER_SIZE andOptions:optionsIn]; +} + -(id) initWithReadBufferSize:(int)readBufferSizeIn andOptions:(STKAudioPlayerOptions)optionsIn { if (self = [super init]) @@ -313,9 +335,8 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn upcomingQueue = [[NSMutableArray alloc] init]; bufferingQueue = [[NSMutableArray alloc] init]; - + [self resetPcmBuffers]; - [self createAudioUnit]; [self createPlaybackThread]; } @@ -423,29 +444,18 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn { NSMutableArray* array = [[NSMutableArray alloc] initWithCapacity:bufferingQueue.count + upcomingQueue.count]; - STKQueueEntry* entry = [bufferingQueue dequeue]; - - if (entry && entry != currentlyPlayingEntry) - { - [array addObject:[entry queueItemId]]; - } - - while (bufferingQueue.count > 0) - { - id queueItemId = [[bufferingQueue dequeue] queueItemId]; - - if (queueItemId != nil) - { - [array addObject:queueItemId]; - } - } - for (STKQueueEntry* entry in upcomingQueue) { [array addObject:entry.queueItemId]; } + + for (STKQueueEntry* entry in bufferingQueue) + { + [array addObject:entry.queueItemId]; + } [upcomingQueue removeAllObjects]; + [bufferingQueue removeAllObjects]; if (array.count > 0) { @@ -507,6 +517,8 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn { pthread_mutex_lock(&playerMutex); { + [self startSystemBackgroundTask]; + [upcomingQueue enqueue:[[STKQueueEntry alloc] initWithDataSource:dataSourceIn andQueueItemId:queueItemId]]; } pthread_mutex_unlock(&playerMutex); @@ -1046,18 +1058,18 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn [currentlyReadingEntry.dataSource unregisterForEvents]; } - if (self->options & STKAudioPlayerOptionDontFlushQueueOnSeek) + if (self->options & STKAudioPlayerOptionFlushQueueOnSeek) + { + self.internalState = STKAudioPlayerInternalStateWaitingForDataAfterSeek; + [self setCurrentlyReadingEntry:currentlyPlayingEntry andStartPlaying:YES clearQueue:YES]; + } + else { [self requeueBufferingEntries]; self.internalState = STKAudioPlayerInternalStateWaitingForDataAfterSeek; [self setCurrentlyReadingEntry:currentlyPlayingEntry andStartPlaying:YES clearQueue:NO]; } - else - { - self.internalState = STKAudioPlayerInternalStateWaitingForDataAfterSeek; - [self setCurrentlyReadingEntry:currentlyPlayingEntry andStartPlaying:YES clearQueue:YES]; - } } else if (currentlyReadingEntry == nil) { @@ -1557,13 +1569,11 @@ static void AudioFileStreamPacketsProc(void* clientData, UInt32 numberBytes, UIn return retval; } -#define kOutputBus 0 -#define kInputBus 1 - static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* classDesc) { +#if TARGET_OS_IPHONE UInt32 size; - + if (AudioFormatGetPropertyInfo(kAudioFormatProperty_Decoders, sizeof(formatId), &formatId, &size) != 0) { return NO; @@ -1586,6 +1596,7 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl return YES; } } +#endif return NO; } @@ -1666,7 +1677,11 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl AudioComponentDescription desc; desc.componentType = kAudioUnitType_Output; +#if TARGET_OS_IPHONE desc.componentSubType = kAudioUnitSubType_RemoteIO; +#else + desc.componentSubType = kAudioUnitSubType_DefaultOutput; +#endif desc.componentFlags = 0; desc.componentFlagsMask = 0; desc.componentManufacturer = kAudioUnitManufacturer_Apple; @@ -2289,6 +2304,61 @@ static OSStatus OutputRenderCallback(void* inRefCon, AudioUnitRenderActionFlags* return 0; } +-(NSArray*) pendingQueue +{ + pthread_mutex_lock(&playerMutex); + + NSArray* retval; + NSMutableArray* mutableArray = [[NSMutableArray alloc] initWithCapacity:upcomingQueue.count + bufferingQueue.count]; + + [mutableArray skipQueueWithQueue:upcomingQueue]; + [mutableArray skipQueueWithQueue:bufferingQueue]; + + retval = [NSArray arrayWithArray:mutableArray]; + + pthread_mutex_unlock(&playerMutex); + + return retval; +} + +-(NSUInteger) pendingQueueCount +{ + pthread_mutex_lock(&playerMutex); + + NSUInteger retval = upcomingQueue.count + bufferingQueue.count; + + pthread_mutex_unlock(&playerMutex); + + return retval; +} + +-(NSObject*) mostRecentlyQueuedStillPendingItem +{ + pthread_mutex_lock(&playerMutex); + + if (upcomingQueue.count > 0) + { + NSObject* retval = [upcomingQueue objectAtIndex:0]; + + pthread_mutex_unlock(&playerMutex); + + return retval; + } + + if (bufferingQueue.count > 0) + { + NSObject* retval = [bufferingQueue objectAtIndex:0]; + + pthread_mutex_unlock(&playerMutex); + + return retval; + } + + pthread_mutex_unlock(&playerMutex); + + return nil; +} + -(float) peakPowerInDecibelsForChannel:(NSUInteger)channelNumber { if (channelNumber >= canonicalAudioStreamBasicDescription.mChannelsPerFrame) diff --git a/StreamingKit/StreamingKit/STKAutoRecoveringHTTPDataSource.h b/StreamingKit/StreamingKit/STKAutoRecoveringHTTPDataSource.h index e08f767..45cc2bc 100644 --- a/StreamingKit/StreamingKit/STKAutoRecoveringHTTPDataSource.h +++ b/StreamingKit/StreamingKit/STKAutoRecoveringHTTPDataSource.h @@ -4,7 +4,7 @@ Created by Thong Nguyen on 16/10/2012. https://github.com/tumtumtum/audjustable - Copyright (c) 2012 Thong Nguyen (tumtumtum@gmail.com). All rights reserved. + Copyright (c) 2012-2014 Thong Nguyen (tumtumtum@gmail.com). All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/StreamingKit/StreamingKit/STKAutoRecoveringHTTPDataSource.m b/StreamingKit/StreamingKit/STKAutoRecoveringHTTPDataSource.m index 87db858..d8c11a0 100644 --- a/StreamingKit/StreamingKit/STKAutoRecoveringHTTPDataSource.m +++ b/StreamingKit/StreamingKit/STKAutoRecoveringHTTPDataSource.m @@ -4,7 +4,7 @@ Created by Thong Nguyen on 16/10/2012. https://github.com/tumtumtum/audjustable - Copyright (c) 2012 Thong Nguyen (tumtumtum@gmail.com). All rights reserved. + Copyright (c) 2012-2014 Thong Nguyen (tumtumtum@gmail.com). All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/StreamingKit/StreamingKitMac/StreamingKitMac-Prefix.pch b/StreamingKit/StreamingKitMac/StreamingKitMac-Prefix.pch new file mode 100644 index 0000000..926a42b --- /dev/null +++ b/StreamingKit/StreamingKitMac/StreamingKitMac-Prefix.pch @@ -0,0 +1,9 @@ +// +// Prefix header +// +// The contents of this file are implicitly included at the beginning of every source file. +// + +#ifdef __OBJC__ + #import +#endif diff --git a/StreamingKit/StreamingKitMac/StreamingKitMac.h b/StreamingKit/StreamingKitMac/StreamingKitMac.h new file mode 100644 index 0000000..5fd9917 --- /dev/null +++ b/StreamingKit/StreamingKitMac/StreamingKitMac.h @@ -0,0 +1,13 @@ +// +// StreamingKitMac.h +// StreamingKitMac +// +// Created by Thong Nguyen on 02/02/2014. +// Copyright (c) 2014 Thong Nguyen. All rights reserved. +// + +#import + +@interface StreamingKitMac : NSObject + +@end diff --git a/StreamingKit/StreamingKitMac/StreamingKitMac.m b/StreamingKit/StreamingKitMac/StreamingKitMac.m new file mode 100644 index 0000000..9247514 --- /dev/null +++ b/StreamingKit/StreamingKitMac/StreamingKitMac.m @@ -0,0 +1,13 @@ +// +// StreamingKitMac.m +// StreamingKitMac +// +// Created by Thong Nguyen on 02/02/2014. +// Copyright (c) 2014 Thong Nguyen. All rights reserved. +// + +#import "StreamingKitMac.h" + +@implementation StreamingKitMac + +@end diff --git a/StreamingKit/StreamingKitMacTests/StreamingKitMacTests-Info.plist b/StreamingKit/StreamingKitMacTests/StreamingKitMacTests-Info.plist new file mode 100644 index 0000000..552d5b1 --- /dev/null +++ b/StreamingKit/StreamingKitMacTests/StreamingKitMacTests-Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + com.abstractpath.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/StreamingKit/StreamingKitMacTests/StreamingKitMacTests.m b/StreamingKit/StreamingKitMacTests/StreamingKitMacTests.m new file mode 100644 index 0000000..f0656ae --- /dev/null +++ b/StreamingKit/StreamingKitMacTests/StreamingKitMacTests.m @@ -0,0 +1,34 @@ +// +// StreamingKitMacTests.m +// StreamingKitMacTests +// +// Created by Thong Nguyen on 02/02/2014. +// Copyright (c) 2014 Thong Nguyen. All rights reserved. +// + +#import + +@interface StreamingKitMacTests : XCTestCase + +@end + +@implementation StreamingKitMacTests + +- (void)setUp +{ + [super setUp]; + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown +{ + // Put teardown code here. This method is called after the invocation of each test method in the class. + [super tearDown]; +} + +- (void)testExample +{ + XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__); +} + +@end diff --git a/StreamingKit/StreamingKitMacTests/en.lproj/InfoPlist.strings b/StreamingKit/StreamingKitMacTests/en.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/StreamingKit/StreamingKitMacTests/en.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + From 4df5a84569ec384be6e32f142b4af6cab915298c Mon Sep 17 00:00:00 2001 From: Thong Nguyen Date: Sun, 2 Feb 2014 13:14:37 +0000 Subject: [PATCH 32/33] Added OSX build targets and ExampleAppMac. Added AudioUnit as a dependent framework in podspec --- .../ExampleAppMac.xcodeproj/project.pbxproj | 529 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/ExampleAppMac.xccheckout | 41 ++ ExampleAppMac/ExampleAppMac/AppDelegate.h | 15 + ExampleAppMac/ExampleAppMac/AppDelegate.m | 21 + .../ExampleAppMac/Base.lproj/MainMenu.xib | 467 ++++++++++++++++ .../ExampleAppMac/ExampleAppMac-Info.plist | 34 ++ .../ExampleAppMac/ExampleAppMac-Prefix.pch | 9 + .../AppIcon.appiconset/Contents.json | 58 ++ .../ExampleAppMac/en.lproj/Credits.rtf | 29 + .../ExampleAppMac/en.lproj/InfoPlist.strings | 2 + ExampleAppMac/ExampleAppMac/main.m | 14 + .../ExampleAppMacTests-Info.plist | 22 + .../ExampleAppMacTests/ExampleAppMacTests.m | 34 ++ .../en.lproj/InfoPlist.strings | 2 + StreamingKit-head.podspec | 2 +- StreamingKit.podspec | 2 +- .../contents.xcworkspacedata | 3 + .../StreamingKit.xcodeproj/project.pbxproj | 56 +- StreamingKit/StreamingKit/STKAudioPlayer.m | 3 +- 20 files changed, 1341 insertions(+), 9 deletions(-) create mode 100644 ExampleAppMac/ExampleAppMac.xcodeproj/project.pbxproj create mode 100644 ExampleAppMac/ExampleAppMac.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 ExampleAppMac/ExampleAppMac.xcodeproj/project.xcworkspace/xcshareddata/ExampleAppMac.xccheckout create mode 100644 ExampleAppMac/ExampleAppMac/AppDelegate.h create mode 100644 ExampleAppMac/ExampleAppMac/AppDelegate.m create mode 100644 ExampleAppMac/ExampleAppMac/Base.lproj/MainMenu.xib create mode 100644 ExampleAppMac/ExampleAppMac/ExampleAppMac-Info.plist create mode 100644 ExampleAppMac/ExampleAppMac/ExampleAppMac-Prefix.pch create mode 100644 ExampleAppMac/ExampleAppMac/Images.xcassets/AppIcon.appiconset/Contents.json create mode 100644 ExampleAppMac/ExampleAppMac/en.lproj/Credits.rtf create mode 100644 ExampleAppMac/ExampleAppMac/en.lproj/InfoPlist.strings create mode 100644 ExampleAppMac/ExampleAppMac/main.m create mode 100644 ExampleAppMac/ExampleAppMacTests/ExampleAppMacTests-Info.plist create mode 100644 ExampleAppMac/ExampleAppMacTests/ExampleAppMacTests.m create mode 100644 ExampleAppMac/ExampleAppMacTests/en.lproj/InfoPlist.strings diff --git a/ExampleAppMac/ExampleAppMac.xcodeproj/project.pbxproj b/ExampleAppMac/ExampleAppMac.xcodeproj/project.pbxproj new file mode 100644 index 0000000..05afba7 --- /dev/null +++ b/ExampleAppMac/ExampleAppMac.xcodeproj/project.pbxproj @@ -0,0 +1,529 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + A1A499A5189E765800E2A2E2 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A499A4189E765800E2A2E2 /* Cocoa.framework */; }; + A1A499AF189E765800E2A2E2 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = A1A499AD189E765800E2A2E2 /* InfoPlist.strings */; }; + A1A499B1189E765800E2A2E2 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = A1A499B0189E765800E2A2E2 /* main.m */; }; + A1A499B5189E765800E2A2E2 /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = A1A499B3189E765800E2A2E2 /* Credits.rtf */; }; + A1A499B8189E765800E2A2E2 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = A1A499B7189E765800E2A2E2 /* AppDelegate.m */; }; + A1A499BB189E765800E2A2E2 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = A1A499B9189E765800E2A2E2 /* MainMenu.xib */; }; + A1A499BD189E765800E2A2E2 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A1A499BC189E765800E2A2E2 /* Images.xcassets */; }; + A1A499C4189E765800E2A2E2 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A499C3189E765800E2A2E2 /* XCTest.framework */; }; + A1A499C5189E765800E2A2E2 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A499A4189E765800E2A2E2 /* Cocoa.framework */; }; + A1A499CD189E765800E2A2E2 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = A1A499CB189E765800E2A2E2 /* InfoPlist.strings */; }; + A1A499CF189E765800E2A2E2 /* ExampleAppMacTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A1A499CE189E765800E2A2E2 /* ExampleAppMacTests.m */; }; + A1A499EC189E793300E2A2E2 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A499A9189E765800E2A2E2 /* Foundation.framework */; }; + A1A499F0189E793D00E2A2E2 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A499EF189E793D00E2A2E2 /* CoreAudio.framework */; }; + A1A499F2189E799400E2A2E2 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A499F1189E799400E2A2E2 /* AudioToolbox.framework */; }; + A1A499F3189E799F00E2A2E2 /* AudioUnit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A499ED189E793700E2A2E2 /* AudioUnit.framework */; }; + A1A499F5189E79CB00E2A2E2 /* CoreAudioKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A499F4189E79CB00E2A2E2 /* CoreAudioKit.framework */; }; + A1A499F9189E7A3500E2A2E2 /* libStreamingKitMac.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A1A499F8189E7A3500E2A2E2 /* libStreamingKitMac.a */; }; + 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 */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + A1A499C6189E765800E2A2E2 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = A1A49999189E765800E2A2E2 /* Project object */; + proxyType = 1; + remoteGlobalIDString = A1A499A0189E765800E2A2E2; + remoteInfo = ExampleAppMac; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + A1A499A1189E765800E2A2E2 /* ExampleAppMac.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ExampleAppMac.app; sourceTree = BUILT_PRODUCTS_DIR; }; + A1A499A4189E765800E2A2E2 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; + A1A499A7189E765800E2A2E2 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; + A1A499A8189E765800E2A2E2 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; }; + A1A499A9189E765800E2A2E2 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + A1A499AC189E765800E2A2E2 /* ExampleAppMac-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "ExampleAppMac-Info.plist"; sourceTree = ""; }; + A1A499AE189E765800E2A2E2 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + A1A499B0189E765800E2A2E2 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + A1A499B2189E765800E2A2E2 /* ExampleAppMac-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ExampleAppMac-Prefix.pch"; sourceTree = ""; }; + A1A499B4189E765800E2A2E2 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = en; path = en.lproj/Credits.rtf; sourceTree = ""; }; + A1A499B6189E765800E2A2E2 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + A1A499B7189E765800E2A2E2 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + A1A499BA189E765800E2A2E2 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + A1A499BC189E765800E2A2E2 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; + A1A499C2189E765800E2A2E2 /* ExampleAppMacTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ExampleAppMacTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + A1A499C3189E765800E2A2E2 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; + A1A499CA189E765800E2A2E2 /* ExampleAppMacTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "ExampleAppMacTests-Info.plist"; sourceTree = ""; }; + A1A499CC189E765800E2A2E2 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + A1A499CE189E765800E2A2E2 /* ExampleAppMacTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ExampleAppMacTests.m; sourceTree = ""; }; + A1A499EA189E76BD00E2A2E2 /* libStreamingKitMac.a */ = {isa = PBXFileReference; lastKnownFileType = file; name = libStreamingKitMac.a; path = ../StreamingKit/build/Debug/libStreamingKitMac.a; sourceTree = ""; }; + A1A499ED189E793700E2A2E2 /* AudioUnit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioUnit.framework; path = System/Library/Frameworks/AudioUnit.framework; sourceTree = SDKROOT; }; + A1A499EF189E793D00E2A2E2 /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; }; + A1A499F1189E799400E2A2E2 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; }; + 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; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + A1A4999E189E765800E2A2E2 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A1A499FD189E7BFC00E2A2E2 /* AudioUnit.framework in Frameworks */, + A1A499FC189E7A6D00E2A2E2 /* SystemConfiguration.framework in Frameworks */, + A1A499FA189E7A5600E2A2E2 /* AudioToolbox.framework in Frameworks */, + A1A499F9189E7A3500E2A2E2 /* libStreamingKitMac.a in Frameworks */, + A1A499F0189E793D00E2A2E2 /* CoreAudio.framework in Frameworks */, + A1A499EC189E793300E2A2E2 /* Foundation.framework in Frameworks */, + A1A499A5189E765800E2A2E2 /* Cocoa.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A1A499BF189E765800E2A2E2 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A1A499F5189E79CB00E2A2E2 /* CoreAudioKit.framework in Frameworks */, + A1A499F3189E799F00E2A2E2 /* AudioUnit.framework in Frameworks */, + A1A499F2189E799400E2A2E2 /* AudioToolbox.framework in Frameworks */, + A1A499C5189E765800E2A2E2 /* Cocoa.framework in Frameworks */, + A1A499C4189E765800E2A2E2 /* XCTest.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + A1A49998189E765800E2A2E2 = { + isa = PBXGroup; + children = ( + A1A499AA189E765800E2A2E2 /* ExampleAppMac */, + A1A499C8189E765800E2A2E2 /* ExampleAppMacTests */, + A1A499A3189E765800E2A2E2 /* Frameworks */, + A1A499A2189E765800E2A2E2 /* Products */, + ); + sourceTree = ""; + }; + A1A499A2189E765800E2A2E2 /* Products */ = { + isa = PBXGroup; + children = ( + A1A499A1189E765800E2A2E2 /* ExampleAppMac.app */, + A1A499C2189E765800E2A2E2 /* ExampleAppMacTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + A1A499A3189E765800E2A2E2 /* Frameworks */ = { + isa = PBXGroup; + children = ( + A1A499FB189E7A6D00E2A2E2 /* SystemConfiguration.framework */, + A1A499F8189E7A3500E2A2E2 /* libStreamingKitMac.a */, + A1A499F4189E79CB00E2A2E2 /* CoreAudioKit.framework */, + A1A499F1189E799400E2A2E2 /* AudioToolbox.framework */, + A1A499EF189E793D00E2A2E2 /* CoreAudio.framework */, + A1A499ED189E793700E2A2E2 /* AudioUnit.framework */, + A1A499EA189E76BD00E2A2E2 /* libStreamingKitMac.a */, + A1A499A4189E765800E2A2E2 /* Cocoa.framework */, + A1A499C3189E765800E2A2E2 /* XCTest.framework */, + A1A499A6189E765800E2A2E2 /* Other Frameworks */, + ); + name = Frameworks; + sourceTree = ""; + }; + A1A499A6189E765800E2A2E2 /* Other Frameworks */ = { + isa = PBXGroup; + children = ( + A1A499A7189E765800E2A2E2 /* AppKit.framework */, + A1A499A8189E765800E2A2E2 /* CoreData.framework */, + A1A499A9189E765800E2A2E2 /* Foundation.framework */, + ); + name = "Other Frameworks"; + sourceTree = ""; + }; + A1A499AA189E765800E2A2E2 /* ExampleAppMac */ = { + isa = PBXGroup; + children = ( + A1A499B6189E765800E2A2E2 /* AppDelegate.h */, + A1A499B7189E765800E2A2E2 /* AppDelegate.m */, + A1A499B9189E765800E2A2E2 /* MainMenu.xib */, + A1A499BC189E765800E2A2E2 /* Images.xcassets */, + A1A499AB189E765800E2A2E2 /* Supporting Files */, + ); + path = ExampleAppMac; + sourceTree = ""; + }; + A1A499AB189E765800E2A2E2 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + A1A499AC189E765800E2A2E2 /* ExampleAppMac-Info.plist */, + A1A499AD189E765800E2A2E2 /* InfoPlist.strings */, + A1A499B0189E765800E2A2E2 /* main.m */, + A1A499B2189E765800E2A2E2 /* ExampleAppMac-Prefix.pch */, + A1A499B3189E765800E2A2E2 /* Credits.rtf */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + A1A499C8189E765800E2A2E2 /* ExampleAppMacTests */ = { + isa = PBXGroup; + children = ( + A1A499CE189E765800E2A2E2 /* ExampleAppMacTests.m */, + A1A499C9189E765800E2A2E2 /* Supporting Files */, + ); + path = ExampleAppMacTests; + sourceTree = ""; + }; + A1A499C9189E765800E2A2E2 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + A1A499CA189E765800E2A2E2 /* ExampleAppMacTests-Info.plist */, + A1A499CB189E765800E2A2E2 /* InfoPlist.strings */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + A1A499A0189E765800E2A2E2 /* ExampleAppMac */ = { + isa = PBXNativeTarget; + buildConfigurationList = A1A499D2189E765800E2A2E2 /* Build configuration list for PBXNativeTarget "ExampleAppMac" */; + buildPhases = ( + A1A4999D189E765800E2A2E2 /* Sources */, + A1A4999E189E765800E2A2E2 /* Frameworks */, + A1A4999F189E765800E2A2E2 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ExampleAppMac; + productName = ExampleAppMac; + productReference = A1A499A1189E765800E2A2E2 /* ExampleAppMac.app */; + productType = "com.apple.product-type.application"; + }; + A1A499C1189E765800E2A2E2 /* ExampleAppMacTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = A1A499D5189E765800E2A2E2 /* Build configuration list for PBXNativeTarget "ExampleAppMacTests" */; + buildPhases = ( + A1A499BE189E765800E2A2E2 /* Sources */, + A1A499BF189E765800E2A2E2 /* Frameworks */, + A1A499C0189E765800E2A2E2 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + A1A499C7189E765800E2A2E2 /* PBXTargetDependency */, + ); + name = ExampleAppMacTests; + productName = ExampleAppMacTests; + productReference = A1A499C2189E765800E2A2E2 /* ExampleAppMacTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + A1A49999189E765800E2A2E2 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0500; + ORGANIZATIONNAME = "Thong Nguyen"; + TargetAttributes = { + A1A499C1189E765800E2A2E2 = { + TestTargetID = A1A499A0189E765800E2A2E2; + }; + }; + }; + buildConfigurationList = A1A4999C189E765800E2A2E2 /* Build configuration list for PBXProject "ExampleAppMac" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = A1A49998189E765800E2A2E2; + productRefGroup = A1A499A2189E765800E2A2E2 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + A1A499A0189E765800E2A2E2 /* ExampleAppMac */, + A1A499C1189E765800E2A2E2 /* ExampleAppMacTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + A1A4999F189E765800E2A2E2 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A1A499AF189E765800E2A2E2 /* InfoPlist.strings in Resources */, + A1A499BD189E765800E2A2E2 /* Images.xcassets in Resources */, + A1A499B5189E765800E2A2E2 /* Credits.rtf in Resources */, + A1A499BB189E765800E2A2E2 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A1A499C0189E765800E2A2E2 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A1A499CD189E765800E2A2E2 /* InfoPlist.strings in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + A1A4999D189E765800E2A2E2 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A1A499B8189E765800E2A2E2 /* AppDelegate.m in Sources */, + A1A499B1189E765800E2A2E2 /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A1A499BE189E765800E2A2E2 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A1A499CF189E765800E2A2E2 /* ExampleAppMacTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + A1A499C7189E765800E2A2E2 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = A1A499A0189E765800E2A2E2 /* ExampleAppMac */; + targetProxy = A1A499C6189E765800E2A2E2 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + A1A499AD189E765800E2A2E2 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + A1A499AE189E765800E2A2E2 /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + A1A499B3189E765800E2A2E2 /* Credits.rtf */ = { + isa = PBXVariantGroup; + children = ( + A1A499B4189E765800E2A2E2 /* en */, + ); + name = Credits.rtf; + sourceTree = ""; + }; + A1A499B9189E765800E2A2E2 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + A1A499BA189E765800E2A2E2 /* Base */, + ); + name = MainMenu.xib; + sourceTree = ""; + }; + A1A499CB189E765800E2A2E2 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + A1A499CC189E765800E2A2E2 /* en */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + A1A499D0189E765800E2A2E2 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.9; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + }; + name = Debug; + }; + A1A499D1189E765800E2A2E2 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.9; + SDKROOT = macosx; + }; + name = Release; + }; + A1A499D3189E765800E2A2E2 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + COMBINE_HIDPI_IMAGES = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "ExampleAppMac/ExampleAppMac-Prefix.pch"; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "$(SRCROOT)/../StreamingKit/StreamingKit", + ); + INFOPLIST_FILE = "ExampleAppMac/ExampleAppMac-Info.plist"; + MACOSX_DEPLOYMENT_TARGET = 10.8; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + WRAPPER_EXTENSION = app; + }; + name = Debug; + }; + A1A499D4189E765800E2A2E2 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + COMBINE_HIDPI_IMAGES = YES; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "ExampleAppMac/ExampleAppMac-Prefix.pch"; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "$(SRCROOT)/../StreamingKit/StreamingKit", + ); + INFOPLIST_FILE = "ExampleAppMac/ExampleAppMac-Info.plist"; + MACOSX_DEPLOYMENT_TARGET = 10.8; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + WRAPPER_EXTENSION = app; + }; + name = Release; + }; + A1A499D6189E765800E2A2E2 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/ExampleAppMac.app/Contents/MacOS/ExampleAppMac"; + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(inherited)", + ); + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "ExampleAppMac/ExampleAppMac-Prefix.pch"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = "ExampleAppMacTests/ExampleAppMacTests-Info.plist"; + MACOSX_DEPLOYMENT_TARGET = 10.8; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUNDLE_LOADER)"; + WRAPPER_EXTENSION = xctest; + }; + name = Debug; + }; + A1A499D7189E765800E2A2E2 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/ExampleAppMac.app/Contents/MacOS/ExampleAppMac"; + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(inherited)", + ); + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "ExampleAppMac/ExampleAppMac-Prefix.pch"; + INFOPLIST_FILE = "ExampleAppMacTests/ExampleAppMacTests-Info.plist"; + MACOSX_DEPLOYMENT_TARGET = 10.8; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUNDLE_LOADER)"; + WRAPPER_EXTENSION = xctest; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + A1A4999C189E765800E2A2E2 /* Build configuration list for PBXProject "ExampleAppMac" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A1A499D0189E765800E2A2E2 /* Debug */, + A1A499D1189E765800E2A2E2 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + A1A499D2189E765800E2A2E2 /* Build configuration list for PBXNativeTarget "ExampleAppMac" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A1A499D3189E765800E2A2E2 /* Debug */, + A1A499D4189E765800E2A2E2 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + A1A499D5189E765800E2A2E2 /* Build configuration list for PBXNativeTarget "ExampleAppMacTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A1A499D6189E765800E2A2E2 /* Debug */, + A1A499D7189E765800E2A2E2 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = A1A49999189E765800E2A2E2 /* Project object */; +} diff --git a/ExampleAppMac/ExampleAppMac.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/ExampleAppMac/ExampleAppMac.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..436fba6 --- /dev/null +++ b/ExampleAppMac/ExampleAppMac.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/ExampleAppMac/ExampleAppMac.xcodeproj/project.xcworkspace/xcshareddata/ExampleAppMac.xccheckout b/ExampleAppMac/ExampleAppMac.xcodeproj/project.xcworkspace/xcshareddata/ExampleAppMac.xccheckout new file mode 100644 index 0000000..0eeebd3 --- /dev/null +++ b/ExampleAppMac/ExampleAppMac.xcodeproj/project.xcworkspace/xcshareddata/ExampleAppMac.xccheckout @@ -0,0 +1,41 @@ + + + + + IDESourceControlProjectFavoriteDictionaryKey + + IDESourceControlProjectIdentifier + B1180E29-F9F8-4232-A985-F8E21716EF14 + IDESourceControlProjectName + ExampleAppMac + IDESourceControlProjectOriginsDictionary + + DD310C30-B3D0-4BD7-9565-9F29F09CC4F8 + https://github.com/tumtumtum/StreamingKit.git + + IDESourceControlProjectPath + ExampleAppMac/ExampleAppMac.xcodeproj/project.xcworkspace + IDESourceControlProjectRelativeInstallPathDictionary + + DD310C30-B3D0-4BD7-9565-9F29F09CC4F8 + ../../.. + + IDESourceControlProjectURL + https://github.com/tumtumtum/StreamingKit.git + IDESourceControlProjectVersion + 110 + IDESourceControlProjectWCCIdentifier + DD310C30-B3D0-4BD7-9565-9F29F09CC4F8 + IDESourceControlProjectWCConfigurations + + + IDESourceControlRepositoryExtensionIdentifierKey + public.vcs.git + IDESourceControlWCCIdentifierKey + DD310C30-B3D0-4BD7-9565-9F29F09CC4F8 + IDESourceControlWCCName + StreamingKit + + + + diff --git a/ExampleAppMac/ExampleAppMac/AppDelegate.h b/ExampleAppMac/ExampleAppMac/AppDelegate.h new file mode 100644 index 0000000..32f2314 --- /dev/null +++ b/ExampleAppMac/ExampleAppMac/AppDelegate.h @@ -0,0 +1,15 @@ +// +// AppDelegate.h +// ExampleAppMac +// +// Created by Thong Nguyen on 02/02/2014. +// Copyright (c) 2014 Thong Nguyen. All rights reserved. +// + +#import + +@interface AppDelegate : NSObject + +@property (assign) IBOutlet NSWindow *window; + +@end diff --git a/ExampleAppMac/ExampleAppMac/AppDelegate.m b/ExampleAppMac/ExampleAppMac/AppDelegate.m new file mode 100644 index 0000000..ab2a7c5 --- /dev/null +++ b/ExampleAppMac/ExampleAppMac/AppDelegate.m @@ -0,0 +1,21 @@ +// +// AppDelegate.m +// ExampleAppMac +// +// Created by Thong Nguyen on 02/02/2014. +// Copyright (c) 2014 Thong Nguyen. All rights reserved. +// + +#import "AppDelegate.h" +#import "STKAudioPlayer.h" + +@implementation AppDelegate + +-(void) applicationDidFinishLaunching:(NSNotification *)aNotification +{ + STKAudioPlayer* player = [[STKAudioPlayer alloc] init]; + + [player play:@"http://fs.bloom.fm/oss/audiosamples/sample.mp3"]; +} + +@end diff --git a/ExampleAppMac/ExampleAppMac/Base.lproj/MainMenu.xib b/ExampleAppMac/ExampleAppMac/Base.lproj/MainMenu.xib new file mode 100644 index 0000000..eefd534 --- /dev/null +++ b/ExampleAppMac/ExampleAppMac/Base.lproj/MainMenu.xib @@ -0,0 +1,467 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Default + + + + Left to Right + + + + Right to Left + + + + + + + + Default + + + + Left to Right + + + + Right to Left + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ExampleAppMac/ExampleAppMac/ExampleAppMac-Info.plist b/ExampleAppMac/ExampleAppMac/ExampleAppMac-Info.plist new file mode 100644 index 0000000..9248f20 --- /dev/null +++ b/ExampleAppMac/ExampleAppMac/ExampleAppMac-Info.plist @@ -0,0 +1,34 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + + CFBundleIdentifier + com.abstractpath.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSMinimumSystemVersion + ${MACOSX_DEPLOYMENT_TARGET} + NSHumanReadableCopyright + Copyright © 2014 Thong Nguyen. All rights reserved. + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/ExampleAppMac/ExampleAppMac/ExampleAppMac-Prefix.pch b/ExampleAppMac/ExampleAppMac/ExampleAppMac-Prefix.pch new file mode 100644 index 0000000..926a42b --- /dev/null +++ b/ExampleAppMac/ExampleAppMac/ExampleAppMac-Prefix.pch @@ -0,0 +1,9 @@ +// +// Prefix header +// +// The contents of this file are implicitly included at the beginning of every source file. +// + +#ifdef __OBJC__ + #import +#endif diff --git a/ExampleAppMac/ExampleAppMac/Images.xcassets/AppIcon.appiconset/Contents.json b/ExampleAppMac/ExampleAppMac/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..2db2b1c --- /dev/null +++ b/ExampleAppMac/ExampleAppMac/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,58 @@ +{ + "images" : [ + { + "idiom" : "mac", + "size" : "16x16", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "16x16", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "32x32", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "32x32", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "128x128", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "128x128", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "256x256", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "256x256", + "scale" : "2x" + }, + { + "idiom" : "mac", + "size" : "512x512", + "scale" : "1x" + }, + { + "idiom" : "mac", + "size" : "512x512", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/ExampleAppMac/ExampleAppMac/en.lproj/Credits.rtf b/ExampleAppMac/ExampleAppMac/en.lproj/Credits.rtf new file mode 100644 index 0000000..46576ef --- /dev/null +++ b/ExampleAppMac/ExampleAppMac/en.lproj/Credits.rtf @@ -0,0 +1,29 @@ +{\rtf0\ansi{\fonttbl\f0\fswiss Helvetica;} +{\colortbl;\red255\green255\blue255;} +\paperw9840\paperh8400 +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural + +\f0\b\fs24 \cf0 Engineering: +\b0 \ + Some people\ +\ + +\b Human Interface Design: +\b0 \ + Some other people\ +\ + +\b Testing: +\b0 \ + Hopefully not nobody\ +\ + +\b Documentation: +\b0 \ + Whoever\ +\ + +\b With special thanks to: +\b0 \ + Mom\ +} diff --git a/ExampleAppMac/ExampleAppMac/en.lproj/InfoPlist.strings b/ExampleAppMac/ExampleAppMac/en.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/ExampleAppMac/ExampleAppMac/en.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/ExampleAppMac/ExampleAppMac/main.m b/ExampleAppMac/ExampleAppMac/main.m new file mode 100644 index 0000000..9322aa7 --- /dev/null +++ b/ExampleAppMac/ExampleAppMac/main.m @@ -0,0 +1,14 @@ +// +// main.m +// ExampleAppMac +// +// Created by Thong Nguyen on 02/02/2014. +// Copyright (c) 2014 Thong Nguyen. All rights reserved. +// + +#import + +int main(int argc, const char * argv[]) +{ + return NSApplicationMain(argc, argv); +} diff --git a/ExampleAppMac/ExampleAppMacTests/ExampleAppMacTests-Info.plist b/ExampleAppMac/ExampleAppMacTests/ExampleAppMacTests-Info.plist new file mode 100644 index 0000000..552d5b1 --- /dev/null +++ b/ExampleAppMac/ExampleAppMacTests/ExampleAppMacTests-Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + com.abstractpath.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/ExampleAppMac/ExampleAppMacTests/ExampleAppMacTests.m b/ExampleAppMac/ExampleAppMacTests/ExampleAppMacTests.m new file mode 100644 index 0000000..3ae839a --- /dev/null +++ b/ExampleAppMac/ExampleAppMacTests/ExampleAppMacTests.m @@ -0,0 +1,34 @@ +// +// ExampleAppMacTests.m +// ExampleAppMacTests +// +// Created by Thong Nguyen on 02/02/2014. +// Copyright (c) 2014 Thong Nguyen. All rights reserved. +// + +#import + +@interface ExampleAppMacTests : XCTestCase + +@end + +@implementation ExampleAppMacTests + +- (void)setUp +{ + [super setUp]; + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown +{ + // Put teardown code here. This method is called after the invocation of each test method in the class. + [super tearDown]; +} + +- (void)testExample +{ + XCTFail(@"No implementation for \"%s\"", __PRETTY_FUNCTION__); +} + +@end diff --git a/ExampleAppMac/ExampleAppMacTests/en.lproj/InfoPlist.strings b/ExampleAppMac/ExampleAppMacTests/en.lproj/InfoPlist.strings new file mode 100644 index 0000000..477b28f --- /dev/null +++ b/ExampleAppMac/ExampleAppMacTests/en.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/StreamingKit-head.podspec b/StreamingKit-head.podspec index c55bdd7..5eae266 100644 --- a/StreamingKit-head.podspec +++ b/StreamingKit-head.podspec @@ -9,5 +9,5 @@ Pod::Spec.new do |s| s.platform = :ios s.requires_arc = true s.source_files = 'StreamingKit/StreamingKit/*.{h,m}' - s.frameworks = 'AVFoundation', 'SystemConfiguration', 'CFNetwork', 'CoreFoundation', 'AudioToolbox' + s.frameworks = 'AVFoundation', 'SystemConfiguration', 'CFNetwork', 'CoreFoundation', 'AudioToolbox', 'AudioUnit' end diff --git a/StreamingKit.podspec b/StreamingKit.podspec index 57a76ab..1a18544 100644 --- a/StreamingKit.podspec +++ b/StreamingKit.podspec @@ -9,5 +9,5 @@ Pod::Spec.new do |s| s.platform = :ios s.requires_arc = true s.source_files = 'StreamingKit/StreamingKit/*.{h,m}' - s.frameworks = 'AVFoundation', 'SystemConfiguration', 'CFNetwork', 'CoreFoundation', 'AudioToolbox' + s.frameworks = 'AVFoundation', 'SystemConfiguration', 'CFNetwork', 'CoreFoundation', 'AudioToolbox', 'AudioUnit' end diff --git a/StreamingKit.xcworkspace/contents.xcworkspacedata b/StreamingKit.xcworkspace/contents.xcworkspacedata index eb695ed..1155645 100644 --- a/StreamingKit.xcworkspace/contents.xcworkspacedata +++ b/StreamingKit.xcworkspace/contents.xcworkspacedata @@ -4,6 +4,9 @@ + + diff --git a/StreamingKit/StreamingKit.xcodeproj/project.pbxproj b/StreamingKit/StreamingKit.xcodeproj/project.pbxproj index 5b1c441..bdb68cc 100644 --- a/StreamingKit/StreamingKit.xcodeproj/project.pbxproj +++ b/StreamingKit/StreamingKit.xcodeproj/project.pbxproj @@ -25,8 +25,6 @@ A1A49997189E747000E2A2E2 /* NSMutableArray+STKAudioPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = A1BF65D4189A65C6004DD08C /* NSMutableArray+STKAudioPlayer.m */; }; A1BF65D2189A6582004DD08C /* STKQueueEntry.m in Sources */ = {isa = PBXBuildFile; fileRef = A1BF65D1189A6582004DD08C /* STKQueueEntry.m */; }; A1BF65D5189A65C6004DD08C /* NSMutableArray+STKAudioPlayer.m in Sources */ = {isa = PBXBuildFile; fileRef = A1BF65D4189A65C6004DD08C /* NSMutableArray+STKAudioPlayer.m */; }; - A1C9767718981BFE0057F881 /* AudioUnit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1C9767618981BFE0057F881 /* AudioUnit.framework */; }; - A1E7C4CC188D57F50010896F /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1E7C4CB188D57F50010896F /* Foundation.framework */; }; A1E7C4DA188D57F60010896F /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1E7C4D9188D57F60010896F /* XCTest.framework */; }; A1E7C4DB188D57F60010896F /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1E7C4CB188D57F50010896F /* Foundation.framework */; }; A1E7C4E0188D57F60010896F /* libStreamingKit.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A1E7C4C8188D57F50010896F /* libStreamingKit.a */; }; @@ -39,7 +37,6 @@ A1E7C503188D5E550010896F /* STKDataSourceWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4FA188D5E550010896F /* STKDataSourceWrapper.m */; }; A1E7C504188D5E550010896F /* STKHTTPDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4FC188D5E550010896F /* STKHTTPDataSource.m */; }; A1E7C505188D5E550010896F /* STKLocalFileDataSource.m in Sources */ = {isa = PBXBuildFile; fileRef = A1E7C4FE188D5E550010896F /* STKLocalFileDataSource.m */; }; - A1E7C508188D62D20010896F /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1E7C507188D62D20010896F /* UIKit.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -50,6 +47,20 @@ remoteGlobalIDString = A1A49968189E744400E2A2E2; remoteInfo = StreamingKitMac; }; + A1A499E6189E769A00E2A2E2 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = A1A499E1189E769A00E2A2E2 /* ExampleAppMac.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = A1A499A1189E765800E2A2E2; + remoteInfo = ExampleAppMac; + }; + A1A499E8189E769A00E2A2E2 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = A1A499E1189E769A00E2A2E2 /* ExampleAppMac.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = A1A499C2189E765800E2A2E2; + remoteInfo = ExampleAppMacTests; + }; A1E7C4DE188D57F60010896F /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = A1E7C4C0188D57F50010896F /* Project object */; @@ -84,6 +95,8 @@ A1A49982189E744500E2A2E2 /* StreamingKitMacTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "StreamingKitMacTests-Info.plist"; sourceTree = ""; }; A1A49984189E744500E2A2E2 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; A1A49986189E744500E2A2E2 /* StreamingKitMacTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = StreamingKitMacTests.m; sourceTree = ""; }; + A1A499E1189E769A00E2A2E2 /* ExampleAppMac.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ExampleAppMac.xcodeproj; path = ../ExampleAppMac/ExampleAppMac.xcodeproj; sourceTree = ""; }; + A1A499F6189E79EA00E2A2E2 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; }; A1BF65D0189A6582004DD08C /* STKQueueEntry.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = STKQueueEntry.h; sourceTree = ""; }; A1BF65D1189A6582004DD08C /* STKQueueEntry.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = STKQueueEntry.m; sourceTree = ""; }; A1BF65D3189A65C6004DD08C /* NSMutableArray+STKAudioPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSMutableArray+STKAudioPlayer.h"; sourceTree = ""; }; @@ -137,9 +150,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - A1C9767718981BFE0057F881 /* AudioUnit.framework in Frameworks */, - A1E7C508188D62D20010896F /* UIKit.framework in Frameworks */, - A1E7C4CC188D57F50010896F /* Foundation.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -202,6 +212,15 @@ name = "Supporting Files"; sourceTree = ""; }; + A1A499E2189E769A00E2A2E2 /* Products */ = { + isa = PBXGroup; + children = ( + A1A499E7189E769A00E2A2E2 /* ExampleAppMac.app */, + A1A499E9189E769A00E2A2E2 /* ExampleAppMacTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; A1E7C4BF188D57F50010896F = { isa = PBXGroup; children = ( @@ -211,6 +230,7 @@ A1A49980189E744500E2A2E2 /* StreamingKitMacTests */, A1E7C4CA188D57F50010896F /* Frameworks */, A1E7C4C9188D57F50010896F /* Products */, + A1A499E1189E769A00E2A2E2 /* ExampleAppMac.xcodeproj */, ); sourceTree = ""; }; @@ -228,6 +248,7 @@ A1E7C4CA188D57F50010896F /* Frameworks */ = { isa = PBXGroup; children = ( + A1A499F6189E79EA00E2A2E2 /* AudioToolbox.framework */, A1C9767618981BFE0057F881 /* AudioUnit.framework */, A1E7C507188D62D20010896F /* UIKit.framework */, A1E7C4CB188D57F50010896F /* Foundation.framework */, @@ -393,6 +414,12 @@ mainGroup = A1E7C4BF188D57F50010896F; productRefGroup = A1E7C4C9188D57F50010896F /* Products */; projectDirPath = ""; + projectReferences = ( + { + ProductGroup = A1A499E2189E769A00E2A2E2 /* Products */; + ProjectRef = A1A499E1189E769A00E2A2E2 /* ExampleAppMac.xcodeproj */; + }, + ); projectRoot = ""; targets = ( A1E7C4C7188D57F50010896F /* StreamingKit */, @@ -403,6 +430,23 @@ }; /* End PBXProject section */ +/* Begin PBXReferenceProxy section */ + A1A499E7189E769A00E2A2E2 /* ExampleAppMac.app */ = { + isa = PBXReferenceProxy; + fileType = wrapper.application; + path = ExampleAppMac.app; + remoteRef = A1A499E6189E769A00E2A2E2 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + A1A499E9189E769A00E2A2E2 /* ExampleAppMacTests.xctest */ = { + isa = PBXReferenceProxy; + fileType = wrapper.cfbundle; + path = ExampleAppMacTests.xctest; + remoteRef = A1A499E8189E769A00E2A2E2 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + /* Begin PBXResourcesBuildPhase section */ A1A49978189E744500E2A2E2 /* Resources */ = { isa = PBXResourcesBuildPhase; diff --git a/StreamingKit/StreamingKit/STKAudioPlayer.m b/StreamingKit/StreamingKit/STKAudioPlayer.m index e72af96..0cbde99 100644 --- a/StreamingKit/StreamingKit/STKAudioPlayer.m +++ b/StreamingKit/StreamingKit/STKAudioPlayer.m @@ -1697,8 +1697,8 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl return; } +#if TARGET_OS_IPHONE UInt32 flag = 1; - status = AudioUnitSetProperty(audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, kOutputBus, &flag, sizeof(flag)); if (status) @@ -1707,6 +1707,7 @@ static BOOL GetHardwareCodecClassDesc(UInt32 formatId, AudioClassDescription* cl return; } +#endif status = AudioUnitSetProperty(audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, kOutputBus, &canonicalAudioStreamBasicDescription, sizeof(canonicalAudioStreamBasicDescription)); From 1c78dc58678da44118612f4c22787b758ae640f2 Mon Sep 17 00:00:00 2001 From: Thong Nguyen Date: Sun, 2 Feb 2014 14:19:12 +0000 Subject: [PATCH 33/33] 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); + } }]; } }