// // AudioController.m // Cog // // Created by Vincent Spader on 8/7/05. // Copyright 2005 Vincent Spader. All rights reserved. // #import "AudioPlayer.h" #import "BufferChain.h" #import "OutputNode.h" #import "Status.h" @implementation AudioPlayer - (id)init { self = [super init]; if (self) { output = NULL; bufferChain = NULL; chainQueue = [[NSMutableArray alloc] init]; } return self; } - (void)setDelegate:(id)d { delegate = d; } - (id)delegate { return delegate; } - (void)play:(NSURL *)url { [self play:url withUserInfo:nil]; } - (void)play:(NSURL *)url withUserInfo:(id)userInfo { if (output) { [output release]; } output = [[OutputNode alloc] initWithController:self previous:nil]; [output setup]; NSEnumerator *enumerator = [chainQueue objectEnumerator]; id anObject; while (anObject = [enumerator nextObject]) { [anObject setShouldContinue:NO]; } [chainQueue removeAllObjects]; if (bufferChain) { [bufferChain setShouldContinue:NO]; [bufferChain release]; } bufferChain = [[BufferChain alloc] initWithController:self]; while (![bufferChain open:url withOutputFormat:[output format]]) { [bufferChain release]; [self requestNextStream: userInfo]; url = nextStream; if (url == nil) { return; } userInfo = nextStreamUserInfo; [self notifyStreamChanged:userInfo]; bufferChain = [[BufferChain alloc] initWithController:self]; } [bufferChain setUserInfo:userInfo]; [self setShouldContinue:YES]; DBLog(@"DETACHING THREADS"); [output launchThread]; [bufferChain launchThreads]; [self setPlaybackStatus:kCogStatusPlaying]; } - (void)stop { //Set shouldoContinue to NO on allll things [self setShouldContinue:NO]; [self setPlaybackStatus:kCogStatusStopped]; } - (void)pause { [output pause]; [self setPlaybackStatus:kCogStatusPaused]; } - (void)resume { [output resume]; [self setPlaybackStatus:kCogStatusPlaying]; } - (void)seekToTime:(double)time { //Need to reset everything's buffers, and then seek? /*HACK TO TEST HOW WELL THIS WOULD WORK*/ [bufferChain seek:time]; [output seek:time]; /*END HACK*/ } - (void)setVolume:(double)v { [output setVolume:v]; } - (void)setNextStream:(NSURL *)url { [self setNextStream:url withUserInfo:nil]; } - (void)setNextStream:(NSURL *)url withUserInfo:(id)userInfo { [url retain]; [nextStream release]; nextStream = url; [userInfo retain]; [nextStreamUserInfo release]; nextStreamUserInfo = userInfo; } - (void)setShouldContinue:(BOOL)s { [bufferChain setShouldContinue:s]; [output setShouldContinue:s]; } - (double)amountPlayed { return [output amountPlayed]; } - (void)requestNextStream:(id)userInfo { [self sendDelegateMethod:@selector(audioPlayer:requestNextStream:) withObject:userInfo waitUntilDone:YES]; } - (void)notifyStreamChanged:(id)userInfo { [self sendDelegateMethod:@selector(audioPlayer:streamChanged:) withObject:userInfo waitUntilDone:NO]; } - (void)endOfInputReached:(BufferChain *)sender //Sender is a BufferChain { BufferChain *newChain = nil; nextStreamUserInfo = [sender userInfo]; [nextStreamUserInfo retain]; //Retained because when setNextStream is called, it will be released!!! do { [newChain release]; [self requestNextStream: nextStreamUserInfo]; if (nextStream == nil) { return; } newChain = [[BufferChain alloc] initWithController:self]; } while (![newChain open:nextStream withOutputFormat:[output format]]); [newChain setUserInfo: nextStreamUserInfo]; [newChain setShouldContinue:YES]; [newChain launchThreads]; [chainQueue insertObject:newChain atIndex:[chainQueue count]]; [newChain release]; } - (void)endOfInputPlayed { if ([chainQueue count] <= 0) { //End of playlist DBLog(@"STOPPED"); [self stop]; return; } // NSLog(@"SWAPPING BUFFERS"); [bufferChain release]; DBLog(@"END OF INPUT PLAYED"); bufferChain = [chainQueue objectAtIndex:0]; [bufferChain retain]; [chainQueue removeObjectAtIndex:0]; [self notifyStreamChanged:[bufferChain userInfo]]; [output setEndOfStream:NO]; } - (void)sendDelegateMethod:(SEL)selector withObject:(id)obj waitUntilDone:(BOOL)wait { NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[delegate methodSignatureForSelector:selector]]; [invocation setSelector:selector]; [invocation setArgument:&self atIndex:2]; //Indexes start at 2, the first being self, the second being command. [invocation setArgument:&obj atIndex:3]; [self performSelectorOnMainThread:@selector(sendDelegateMethodMainThread:) withObject:invocation waitUntilDone:wait]; } - (void)sendDelegateMethodMainThread:(id)invocation { [invocation invokeWithTarget:delegate]; } - (void)setPlaybackStatus:(int)status { [self sendDelegateMethod:@selector(audioPlayer:statusChanged:) withObject:[NSNumber numberWithInt:status] waitUntilDone:NO]; } - (BufferChain *)bufferChain { return bufferChain; } - (OutputNode *) output { return output; } + (NSArray *)fileTypes { PluginController *pluginController = [PluginController sharedPluginController]; NSArray *decoderTypes = [[pluginController decoders] allKeys]; NSArray *metdataReaderTypes = [[pluginController metadataReaders] allKeys]; NSArray *propertiesReaderTypes = [[pluginController propertiesReaders] allKeys]; NSMutableSet *types = [NSMutableSet set]; [types addObjectsFromArray:decoderTypes]; [types addObjectsFromArray:metdataReaderTypes]; [types addObjectsFromArray:propertiesReaderTypes]; return [types allObjects]; } @end