Compare commits

..

23 Commits

Author SHA1 Message Date
Thong Nguyen d5e7f986ac Updated Audjustable.podspec to 0.0.9 2013-10-23 11:48:15 +13:00
Thong Nguyen 6ac8851b9e Tidied up code 2013-10-23 11:24:59 +13:00
Thong Nguyen 062a320de6 Merge pull request #37 from johnboiles/master
Adding level metering APIs. Adding casts to get rid of Xcode warnings.
2013-10-22 15:17:43 -07:00
John Boiles 05f50e11d0 Adding level metering APIs, similar to those in AVAudioPlayer. 2013-10-15 18:26:01 -07:00
Thong Nguyen c213f8809a Merge pull request #36 from kevinrenskers/master
Added mute and unmute methods, increased version to 0.0.8
2013-10-15 18:06:54 -07:00
John Boiles 5baa7a07c2 Adding some casts to get rid of Xcode warnings. 2013-10-15 13:49:40 -07:00
Kevin Renskers 01629e7fdf Update podspec version 0.0.8 2013-10-10 13:10:27 +00:00
Kevin Renskers 1283cb31cc Added mute and unmute methods 2013-10-10 13:09:18 +00:00
Thong Nguyen 1a0b463a32 Udpated podspec to 0.0.7 2013-08-30 19:43:53 +02:00
Thong Nguyen aeb1ee1b7c Merge pull request #34 from ablinov/master
Removed test code
2013-08-30 10:40:10 -07:00
Alexey Blinov ec8b82b28f Removed test code 2013-08-30 18:34:57 +01:00
Thong Nguyen c84db3bcab Merge pull request #32 from terryso/master
Fixed AudioPlayer not calling the didEncounterError: method when an error occured
2013-08-04 16:03:26 -07:00
terryso cf14a035f6 Fixed AudioPlayer not calling the didEncounterError: method when an error occured 2013-08-01 10:12:19 +08:00
Thong Nguyen 8da9ea9545 Updated podspec to 0.0.6 2013-07-17 15:59:59 +01:00
Thong Nguyen ee387dc098 Fixed deadlock 2013-07-17 15:48:18 +01:00
Thong Nguyen c2d5d4000d Fixed playerMutex not being released sometimes (bug from last commit) 2013-07-16 16:31:45 +01:00
Thong Nguyen 65c1715269 Fix potential threading crash in [AudioPlayer duration] (I think). Moved to Apple provided spinlock implementation 2013-07-16 16:09:52 +01:00
Thong Nguyen 05965388cc Merge pull request #29 from steveluscher/patch-1
Readme: spelling, etc.
2013-07-10 07:17:54 -07:00
Steven Luscher ba4e44b8a2 Readme: spelling, etc. 2013-07-06 11:59:32 -07:00
Thong Nguyen 08287bc6a7 Merge pull request #24 from gangverk/master
Add support for HE-AAC
2013-05-17 11:26:45 -07:00
Piers dd3bd913b5 Update podspec to version 0.0.5 2013-05-17 17:22:32 +00:00
Piers f8887b7ff1 Add support for HE-AAC
The code for handling HE-AAC has been copied from Matt Gallagher's
AudioStreamer library.

https://github.com/mattgallagher/AudioStreamer/blob/master/Classes/AudioStreamer.m#L1605
2013-05-15 16:29:24 +00:00
Thong Nguyen 6d2b656a58 Updated README 2013-05-03 18:46:34 +01:00
9 changed files with 328 additions and 125 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "Audjustable"
s.version = "0.0.4"
s.version = "0.0.9"
s.summary = "A fast and extensible audio streamer for iOS and OSX with support for gapless playback and custom (non-HTTP) sources."
s.homepage = "http://tumtumtum.github.com/audjustable/"
s.license = 'MIT'
+13 -4
View File
@@ -67,7 +67,7 @@
A186B4F2157F80E700BD0084 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
A186B4F3157F80E700BD0084 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
A186B500157F813100BD0084 /* AudioPlayer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioPlayer.h; sourceTree = "<group>"; };
A186B501157F813100BD0084 /* AudioPlayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AudioPlayer.m; sourceTree = "<group>"; };
A186B501157F813100BD0084 /* AudioPlayer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AudioPlayer.m; sourceTree = "<group>"; wrapsLines = 1; };
A186B502157F813100BD0084 /* CoreFoundationDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CoreFoundationDataSource.h; sourceTree = "<group>"; };
A186B503157F813100BD0084 /* CoreFoundationDataSource.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CoreFoundationDataSource.m; sourceTree = "<group>"; };
A186B504157F813100BD0084 /* DataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DataSource.h; sourceTree = "<group>"; };
@@ -247,7 +247,7 @@
A186B4D6157F80E600BD0084 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0460;
LastUpgradeCheck = 0500;
};
buildConfigurationList = A186B4D9157F80E600BD0084 /* Build configuration list for PBXProject "Audjustable" */;
compatibilityVersion = "Xcode 3.2";
@@ -371,9 +371,10 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
@@ -388,10 +389,14 @@
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 4.3;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
@@ -401,9 +406,10 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_32_BIT)";
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
@@ -411,8 +417,11 @@
COPY_PHASE_STRIP = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 4.3;
OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1";
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDESourceControlProjectFavoriteDictionaryKey</key>
<false/>
<key>IDESourceControlProjectIdentifier</key>
<string>0082CB9A-521A-4A1D-A18C-AEBEDF2DF227</string>
<key>IDESourceControlProjectName</key>
<string>Audjustable</string>
<key>IDESourceControlProjectOriginsDictionary</key>
<dict>
<key>39F64986-79A8-40D3-9B3C-3C7711576969</key>
<string>https://github.com/tumtumtum/audjustable.git</string>
</dict>
<key>IDESourceControlProjectPath</key>
<string>Audjustable.xcodeproj/project.xcworkspace</string>
<key>IDESourceControlProjectRelativeInstallPathDictionary</key>
<dict>
<key>39F64986-79A8-40D3-9B3C-3C7711576969</key>
<string>../..</string>
</dict>
<key>IDESourceControlProjectURL</key>
<string>https://github.com/tumtumtum/audjustable.git</string>
<key>IDESourceControlProjectVersion</key>
<integer>110</integer>
<key>IDESourceControlProjectWCCIdentifier</key>
<string>39F64986-79A8-40D3-9B3C-3C7711576969</string>
<key>IDESourceControlProjectWCConfigurations</key>
<array>
<dict>
<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
<string>public.vcs.git</string>
<key>IDESourceControlWCCIdentifierKey</key>
<string>39F64986-79A8-40D3-9B3C-3C7711576969</string>
<key>IDESourceControlWCCName</key>
<string>audjustable</string>
</dict>
</array>
</dict>
</plist>
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0460"
LastUpgradeVersion = "0500"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0460"
LastUpgradeVersion = "0500"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -172,6 +172,10 @@ AudioQueueBufferRefLookupEntry;
volatile BOOL audioQueueFlushing;
volatile SInt64 audioPacketsReadCount;
volatile SInt64 audioPacketsPlayedCount;
BOOL meteringEnabled;
AudioQueueLevelMeterState* levelMeterState;
NSInteger numberOfChannels;
}
@property (readonly) double duration;
@@ -179,6 +183,7 @@ AudioQueueBufferRefLookupEntry;
@property (readwrite) AudioPlayerState state;
@property (readonly) AudioPlayerStopReason stopReason;
@property (readwrite, unsafe_unretained) id<AudioPlayerDelegate> delegate;
@property (readwrite) BOOL meteringEnabled;
-(id) init;
-(id) initWithNumberOfAudioQueueBuffers:(int)numberOfAudioQueueBuffers andReadBufferSize:(int)readBufferSizeIn;
@@ -191,7 +196,12 @@ AudioQueueBufferRefLookupEntry;
-(void) resume;
-(void) stop;
-(void) flushStop;
-(void) mute;
-(void) unmute;
-(void) dispose;
-(NSObject*) currentlyPlayingQueueItemId;
-(void) updateMeters;
-(float) peakPowerInDecibelsForChannel:(NSUInteger)channelNumber;
-(float) averagePowerInDecibelsForChannel:(NSUInteger)channelNumber;
@end
+254 -113
View File
@@ -48,12 +48,6 @@
#define OSSTATUS_PARAM_ERROR (-50)
#define SPIN_LOCK_LOCK(x) \
while(OSAtomicCompareAndSwapInt(0, 1, x) == false);
#define SPIN_LOCK_UNLOCK(x) \
*x = 0;
@interface NSMutableArray(AudioPlayerExtensions)
-(void) enqueue:(id)obj;
-(id) dequeue;
@@ -219,7 +213,7 @@
double calculatedBitRate = [self calculatedBitRate];
if (calculatedBitRate == 0 || self->dataSource.length == 0)
if (calculatedBitRate == 0 || dataSource.length == 0)
{
return 0;
}
@@ -321,9 +315,9 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
if ([self.delegate respondsToSelector:@selector(audioPlayer:internalStateChanged:)])
{
dispatch_async(dispatch_get_main_queue(), ^
{
[self.delegate audioPlayer:self internalStateChanged:internalState];
});
{
[self.delegate audioPlayer:self internalStateChanged:internalState];
});
}
AudioPlayerState newState;
@@ -393,7 +387,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
{
fastApiQueue = [[NSOperationQueue alloc] init];
[fastApiQueue setMaxConcurrentOperationCount:1];
readBufferSize = readBufferSizeIn;
readBuffer = calloc(sizeof(UInt8), readBufferSize);
@@ -457,6 +451,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
free(packetDescs);
free(audioQueueBuffer);
free(audioQueueBufferLookup);
free(levelMeterState);
}
-(void) startSystemBackgroundTask
@@ -534,12 +529,12 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
[upcomingQueue removeAllObjects];
dispatch_async(dispatch_get_main_queue(), ^
{
if ([self.delegate respondsToSelector:@selector(audioPlayer:didCancelQueuedItems:)])
{
[self.delegate audioPlayer:self didCancelQueuedItems:array];
}
});
{
if ([self.delegate respondsToSelector:@selector(audioPlayer:didCancelQueuedItems:)])
{
[self.delegate audioPlayer:self didCancelQueuedItems:array];
}
});
}
pthread_mutex_unlock(&playerMutex);
}
@@ -554,34 +549,34 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
[fastApiQueue cancelAllOperations];
[fastApiQueue addOperationWithBlock:^
{
pthread_mutex_lock(&playerMutex);
{
[self startSystemBackgroundTask];
[self clearQueue];
[upcomingQueue enqueue:[[QueueEntry alloc] initWithDataSource:dataSourceIn andQueueItemId:queueItemId]];
self.internalState = AudioPlayerInternalStateRunning;
[self processQueue:YES];
}
pthread_mutex_unlock(&playerMutex);
}];
{
pthread_mutex_lock(&playerMutex);
{
[self startSystemBackgroundTask];
[self clearQueue];
[upcomingQueue enqueue:[[QueueEntry alloc] initWithDataSource:dataSourceIn andQueueItemId:queueItemId]];
self.internalState = AudioPlayerInternalStateRunning;
[self processQueue:YES];
}
pthread_mutex_unlock(&playerMutex);
}];
}
-(void) queueDataSource:(DataSource*)dataSourceIn withQueueItemId:(NSObject*)queueItemId
{
[fastApiQueue addOperationWithBlock:^
{
pthread_mutex_lock(&playerMutex);
{
[upcomingQueue enqueue:[[QueueEntry alloc] initWithDataSource:dataSourceIn andQueueItemId:queueItemId]];
[self processQueue:NO];
}
pthread_mutex_unlock(&playerMutex);
}];
{
pthread_mutex_lock(&playerMutex);
{
[upcomingQueue enqueue:[[QueueEntry alloc] initWithDataSource:dataSourceIn andQueueItemId:queueItemId]];
[self processQueue:NO];
}
pthread_mutex_unlock(&playerMutex);
}];
}
-(void) handlePropertyChangeForFileStream:(AudioFileStreamID)inAudioFileStream fileStreamPropertyID:(AudioFileStreamPropertyID)inPropertyID ioFlags:(UInt32*)ioFlags
@@ -605,12 +600,13 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
break;
case kAudioFileStreamProperty_DataFormat:
{
AudioStreamBasicDescription newBasicDescription;
UInt32 size = sizeof(newBasicDescription);
AudioFileStreamGetProperty(inAudioFileStream, kAudioFileStreamProperty_DataFormat, &size, &newBasicDescription);
currentlyReadingEntry->audioStreamBasicDescription = newBasicDescription;
if (currentlyReadingEntry->audioStreamBasicDescription.mSampleRate == 0) {
AudioStreamBasicDescription newBasicDescription;
UInt32 size = sizeof(newBasicDescription);
AudioFileStreamGetProperty(inAudioFileStream, kAudioFileStreamProperty_DataFormat, &size, &newBasicDescription);
currentlyReadingEntry->audioStreamBasicDescription = newBasicDescription;
}
currentlyReadingEntry->sampleRate = currentlyReadingEntry->audioStreamBasicDescription.mSampleRate;
currentlyReadingEntry->packetDuration = currentlyReadingEntry->audioStreamBasicDescription.mFramesPerPacket / currentlyReadingEntry->sampleRate;
@@ -652,6 +648,44 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
discontinuous = YES;
}
break;
case kAudioFileStreamProperty_FormatList:
{
Boolean outWriteable;
UInt32 formatListSize;
OSStatus err = AudioFileStreamGetPropertyInfo(inAudioFileStream, kAudioFileStreamProperty_FormatList, &formatListSize, &outWriteable);
if (err)
{
break;
}
AudioFormatListItem *formatList = malloc(formatListSize);
err = AudioFileStreamGetProperty(inAudioFileStream, kAudioFileStreamProperty_FormatList, &formatListSize, formatList);
if (err)
{
free(formatList);
break;
}
for (int i = 0; i * sizeof(AudioFormatListItem) < formatListSize; i += sizeof(AudioFormatListItem))
{
AudioStreamBasicDescription pasbd = formatList[i].mASBD;
if (pasbd.mFormatID == kAudioFormatMPEG4AAC_HE ||
pasbd.mFormatID == kAudioFormatMPEG4AAC_HE_V2)
{
//
// We've found HE-AAC, remember this to tell the audio queue
// when we construct it.
//
#if !TARGET_IPHONE_SIMULATOR
currentlyReadingEntry->audioStreamBasicDescription = pasbd;
#endif
break;
}
}
free(formatList);
}
break;
}
}
@@ -700,7 +734,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
if (currentlyReadingEntry->processedPacketsSizeTotal < 0xfffff)
{
OSAtomicAdd32(packetSize, &currentlyReadingEntry->processedPacketsSizeTotal);
OSAtomicAdd32((int32_t)packetSize, &currentlyReadingEntry->processedPacketsSizeTotal);
OSAtomicIncrement32(&currentlyReadingEntry->processedPacketsCount);
}
@@ -815,7 +849,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
if (currentlyPlayingEntry)
{
SPIN_LOCK_LOCK(&currentlyPlayingLock);
OSSpinLockLock(&currentlyPlayingLock);
{
if (currentlyPlayingEntry)
{
@@ -827,7 +861,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
}
}
}
SPIN_LOCK_UNLOCK(&currentlyPlayingLock);
OSSpinLockUnlock(&currentlyPlayingLock);
}
int index = (int)bufferIn % audioQueueBufferRefLookupCount;
@@ -876,7 +910,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
if (!audioQueueFlushing && [self progress] > 4.0 && numberOfBuffersUsed == 0 ) {
self.internalState = AudioPlayerInternalStateRebuffering;
}
if (!audioQueueFlushing)
{
@@ -889,9 +923,9 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
if (playbackThread)
{
CFRunLoopPerformBlock([playbackThreadRunLoop getCFRunLoop], NSDefaultRunLoopMode, ^
{
[self audioQueueFinishedPlaying:entry];
});
{
[self audioQueueFinishedPlaying:entry];
});
CFRunLoopWakeUp([playbackThreadRunLoop getCFRunLoop]);
@@ -900,7 +934,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
}
}
}
if (self.internalState == AudioPlayerInternalStateStopped
|| self.internalState == AudioPlayerInternalStateStopping
|| self.internalState == AudioPlayerInternalStateDisposed
@@ -928,7 +962,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
}
}
if (signal || YES)
if (signal)
{
pthread_cond_signal(&queueBufferReadyCondition);
}
@@ -1040,7 +1074,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
pthread_mutex_unlock(&playerMutex);
pthread_mutex_lock(&queueBuffersMutex);
waiting = YES;
while (bufferUsed[fillBufferIndex] && !(seekToTimeWasRequested || self.internalState == AudioPlayerInternalStateStopped || self.internalState == AudioPlayerInternalStateStopping || self.internalState == AudioPlayerInternalStateDisposed))
@@ -1064,6 +1098,11 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
{
errorCode = errorCodeIn;
self.internalState = AudioPlayerInternalStateError;
dispatch_async(dispatch_get_main_queue(), ^
{
[self.delegate audioPlayer:self didEncounterError:errorCode];
});
}
-(void) createAudioQueue
@@ -1080,9 +1119,9 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
audioQueue = nil;
}
SPIN_LOCK_LOCK(&currentlyPlayingLock);
OSSpinLockLock(&currentlyPlayingLock);
currentAudioStreamBasicDescription = currentlyPlayingEntry->audioStreamBasicDescription;
SPIN_LOCK_UNLOCK(&currentlyPlayingLock);
OSSpinLockUnlock(&currentlyPlayingLock);
error = AudioQueueNewOutput(&currentlyPlayingEntry->audioStreamBasicDescription, AudioQueueOutputCallbackProc, (__bridge void*)self, NULL, NULL, 0, &audioQueue);
@@ -1177,6 +1216,10 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
AudioQueueSetParameter(audioQueue, kAudioQueueParam_Volume, 1);
// Reset metering enabled in case the user set it before the queue was created
[self setMeteringEnabled:meteringEnabled];
free(cookieData);
}
@@ -1187,16 +1230,22 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
return 0;
}
SPIN_LOCK_LOCK(&currentlyPlayingLock);
OSSpinLockLock(&currentlyPlayingLock);
QueueEntry* entry = currentlyPlayingEntry;
SPIN_LOCK_UNLOCK(&currentlyPlayingLock);
if (entry == nil)
{
OSSpinLockUnlock(&currentlyPlayingLock);
return 0;
}
return [entry duration];
double retval = [entry duration];
OSSpinLockUnlock(&currentlyPlayingLock);
return retval;
}
-(double) progress
@@ -1211,11 +1260,22 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
return 0;
}
SPIN_LOCK_LOCK(&currentlyPlayingLock);
QueueEntry* entry = currentlyPlayingEntry;
SPIN_LOCK_UNLOCK(&currentlyPlayingLock);
OSSpinLockLock(&currentlyPlayingLock);
return [entry progress];
QueueEntry* entry = currentlyPlayingEntry;
if (entry == nil)
{
OSSpinLockUnlock(&currentlyPlayingLock);
return 0;
}
double retval = [entry progress];
OSSpinLockUnlock(&currentlyPlayingLock);
return retval;
}
-(void) wakeupPlaybackThread
@@ -1322,9 +1382,9 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
{
[currentlyReadingEntry.dataSource seekToOffset:0];
}
[currentlyReadingEntry.dataSource registerForEvents:[NSRunLoop currentRunLoop]];
if (startPlaying)
{
[bufferingQueue removeAllObjects];
@@ -1365,9 +1425,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
double progress = [entry progress];
double duration = [entry duration];
SPIN_LOCK_UNLOCK(&currentlyPlayingLock);
BOOL nextIsDifferent = currentlyPlayingEntry != next;
SPIN_LOCK_UNLOCK(&currentlyPlayingLock);
if (next)
{
@@ -1378,35 +1436,33 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
seekToTimeWasRequested = NO;
}
SPIN_LOCK_LOCK(&currentlyPlayingLock);
OSSpinLockLock(&currentlyPlayingLock);
currentlyPlayingEntry = next;
currentlyPlayingEntry->bytesPlayed = 0;
NSObject* playingQueueItemId = playingQueueItemId = currentlyPlayingEntry.queueItemId;
SPIN_LOCK_UNLOCK(&currentlyPlayingLock);
OSSpinLockUnlock(&currentlyPlayingLock);
if (nextIsDifferent && entry)
{
dispatch_async(dispatch_get_main_queue(), ^
{
[self.delegate audioPlayer:self didFinishPlayingQueueItemId:queueItemId withReason:stopReason andProgress:progress andDuration:duration];
});
{
[self.delegate audioPlayer:self didFinishPlayingQueueItemId:queueItemId withReason:stopReason andProgress:progress andDuration:duration];
});
}
if (nextIsDifferent)
{
dispatch_async(dispatch_get_main_queue(), ^
{
[self.delegate audioPlayer:self didStartPlayingQueueItemId:playingQueueItemId];
});
{
[self.delegate audioPlayer:self didStartPlayingQueueItemId:playingQueueItemId];
});
}
}
else
{
SPIN_LOCK_LOCK(&currentlyPlayingLock);
{
currentlyPlayingEntry = nil;
}
SPIN_LOCK_UNLOCK(&currentlyPlayingLock);
OSSpinLockLock(&currentlyPlayingLock);
currentlyPlayingEntry = nil;
OSSpinLockUnlock(&currentlyPlayingLock);
if (currentlyReadingEntry == nil)
{
@@ -1475,11 +1531,9 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
[bufferingQueue dequeue];
}
SPIN_LOCK_LOCK(&currentlyPlayingLock);
{
currentlyPlayingEntry = nil;
}
SPIN_LOCK_UNLOCK(&currentlyPlayingLock);
OSSpinLockLock(&currentlyPlayingLock);
currentlyPlayingEntry = nil;
OSSpinLockUnlock(&currentlyPlayingLock);
currentlyReadingEntry = nil;
seekToTimeWasRequested = NO;
@@ -1504,11 +1558,9 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
[bufferingQueue dequeue];
}
SPIN_LOCK_LOCK(&currentlyPlayingLock);
{
currentlyPlayingEntry = nil;
}
SPIN_LOCK_UNLOCK(&currentlyPlayingLock);
OSSpinLockLock(&currentlyPlayingLock);
currentlyPlayingEntry = nil;
OSSpinLockUnlock(&currentlyPlayingLock);
currentlyReadingEntry = nil;
pthread_mutex_unlock(&queueBuffersMutex);
@@ -1589,16 +1641,16 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
{
[bufferingQueue dequeue];
}
QueueEntry* newEntry = [[QueueEntry alloc] initWithDataSource:currentlyReadingEntry.dataSource andQueueItemId:currentlyReadingEntry.queueItemId];
newEntry->audioStreamBasicDescription = currentlyReadingEntry->audioStreamBasicDescription;
[upcomingQueue skipQueue:newEntry];
SPIN_LOCK_LOCK(&currentlyPlayingLock);
OSSpinLockLock(&currentlyPlayingLock);
currentlyReadingEntry = nil;
SPIN_LOCK_UNLOCK(&currentlyPlayingLock);
OSSpinLockUnlock(&currentlyPlayingLock);
}
}
}
@@ -1619,7 +1671,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
-(void) startInternal
{
@autoreleasepool
{
{
playbackThreadRunLoop = [NSRunLoop currentRunLoop];
NSThread.currentThread.threadPriority = 1;
@@ -1628,14 +1680,14 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
packetsFilled = 0;
[playbackThreadRunLoop addPort:[NSPort port] forMode:NSDefaultRunLoopMode];
while (true)
{
if (![self processRunloop])
{
break;
}
NSDate *date = [[NSDate alloc] initWithTimeIntervalSinceNow:10];
[playbackThreadRunLoop runMode:NSDefaultRunLoopMode beforeDate:date];
}
@@ -1648,11 +1700,11 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
currentlyReadingEntry = nil;
SPIN_LOCK_LOCK(&currentlyPlayingLock);
{
currentlyPlayingEntry = nil;
}
SPIN_LOCK_UNLOCK(&currentlyPlayingLock);
pthread_mutex_lock(&playerMutex);
OSSpinLockLock(&currentlyPlayingLock);
currentlyPlayingEntry = nil;
OSSpinLockUnlock(&currentlyPlayingLock);
pthread_mutex_unlock(&playerMutex);
self.internalState = AudioPlayerInternalStateDisposed;
@@ -1664,9 +1716,9 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
-(void) processSeekToTime
{
OSStatus error;
SPIN_LOCK_LOCK(&currentlyPlayingLock);
OSSpinLockLock(&currentlyPlayingLock);
QueueEntry* currentEntry = currentlyReadingEntry;
SPIN_LOCK_UNLOCK(&currentlyPlayingLock);
OSSpinLockUnlock(&currentlyPlayingLock);
NSAssert(currentEntry == currentlyPlayingEntry, @"playing and reading must be the same");
@@ -1817,14 +1869,14 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
if (error)
{
dispatch_async(dispatch_get_main_queue(), ^
{
[self didEncounterError:AudioPlayerErrorQueueStopFailed];;
});
{
[self didEncounterError:AudioPlayerErrorQueueStopFailed];;
});
}
}
}
pthread_mutex_unlock(&playerMutex);
pthread_mutex_lock(&queueBuffersMutex);
if (numberOfBuffersUsed != 0)
@@ -2031,7 +2083,7 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
[self wakeupPlaybackThread];
}
}
pthread_mutex_unlock(&playerMutex);
pthread_mutex_unlock(&playerMutex);
}
-(void) stop
@@ -2096,6 +2148,16 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
}
}
-(void) mute
{
AudioQueueSetParameter(audioQueue, kAudioQueueParam_Volume, 0);
}
-(void) unmute
{
AudioQueueSetParameter(audioQueue, kAudioQueueParam_Volume, 1);
}
-(void) dispose
{
[self stop];
@@ -2104,16 +2166,95 @@ static void AudioQueueIsRunningCallbackProc(void* userData, AudioQueueRef audioQ
-(NSObject*) currentlyPlayingQueueItemId
{
SPIN_LOCK_LOCK(&currentlyPlayingLock);
OSSpinLockLock(&currentlyPlayingLock);
QueueEntry* entry = currentlyPlayingEntry;
SPIN_LOCK_UNLOCK(&currentlyPlayingLock);
if (entry == nil)
{
OSSpinLockUnlock(&currentlyPlayingLock);
return nil;
}
return entry.queueItemId;
NSObject* retval = entry.queueItemId;
OSSpinLockUnlock(&currentlyPlayingLock);
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.");
}
NSInteger channels = currentAudioStreamBasicDescription.mChannelsPerFrame;
if (numberOfChannels != channels)
{
numberOfChannels = channels;
if (levelMeterState) free(levelMeterState);
{
levelMeterState = malloc(sizeof(AudioQueueLevelMeterState) * numberOfChannels);
}
}
UInt32 sizeofMeters = 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;
}
@end
@@ -153,7 +153,7 @@
stream = 0;
relativePosition = 0;
seekStart = offset;
seekStart = (int)offset;
[self open];
[self reregisterForEvents];
+6 -4
View File
@@ -2,7 +2,7 @@
[Homepage](http://tumtumtum.github.com/audjustable)
Audjustable is audio streaming class for iOS and OSX. Audjustable uses CoreAudio to decompress and playback audio whilst providing a clean and simple object-oriented API.
Audjustable is an audio streaming class for iOS and OSX. Audjustable uses CoreAudio to decompress and playback audio whilst providing a clean and simple object-oriented API.
The primary motivation of this project was to decouple the input (DataSource/InputStreams) from the actual player logic in order to allow advanced customizable input handling such as: HTTP streaming, encryption, auto-recovery, dynamic-buffering. Along the way other features such as gapless playback were added as the opportunity arose.
@@ -14,7 +14,7 @@ The primary motivation of this project was to decouple the input (DataSource/Inp
* Mostly asynchronous API
* Buffered and gapless playback
* Easy to implement audio data sources (HTTP and local file system DataSources provided)
* Easy to extend DataSource to support adaptive buffering, encryption etc
* Easy to extend DataSource to support adaptive buffering, encryption, etc.
* Optimised for low CPU/battery usage
## Usage
@@ -23,6 +23,8 @@ Download the [source](https://github.com/tumtumtum/audjustable/zipball/master) w
If you would like to integrate the AudioPlayer directly into your project you only need to copy the files inside the `/Audjustable/Classes/AudioPlayer` [directory](https://github.com/tumtumtum/audjustable/tree/master/Audjustable/Classes/AudioPlayer) into your project.
Audjustable is also available as a [Cocoapod](http://cocoapods.org/?q=audjustable).
## Code
There are two main classes. The `DataSource` class which is the abstract base class for the various compressed audio data sources (HTTP, local file are provided). The `AudioPlayer` class manages and renders audio from a queue DataSources.
@@ -34,7 +36,7 @@ There are two main classes. The `DataSource` class which is the abstract base c
AudioPlayer* audioPlayer = [[AudioPlayer alloc] init];
audioPlayer.delegate = self;
// Queue on a URL to play. Each queue item has a unique ID (item1) that to identify the related file in delegate callbacks
// Queue on a URL to play. Each queue item has a unique ID (item1) that to identify the related file in delegate callbacks
[audioPlayer setDataSource:[audioPlayer dataSourceFromURL:@"https://github.com/downloads/tumtumtum/audjustable/sample.m4a"] withQueueItemId:@"item1"];
@@ -45,4 +47,4 @@ audioPlayer.delegate = self;
Background playback on iOS is easily added to your application by calling the `AudioSessionInitialize` in your AppDelegate.
### Authors and Contributors
Copyright 2012, Thong Nguyen (@tumtumtum)
Copyright 2012, Thong Nguyen (@tumtumtum)