diff --git a/Audio/AudioPlayer.m b/Audio/AudioPlayer.m index 33db458f7..433936c8b 100644 --- a/Audio/AudioPlayer.m +++ b/Audio/AudioPlayer.m @@ -232,6 +232,16 @@ - (BOOL)endOfInputReached:(BufferChain *)sender //Sender is a BufferChain { @synchronized (chainQueue) { + // No point in constructing new chain for the next playlist entry + // if there's already one at the head of chainQueue... r-r-right? + for (BufferChain *chain in chainQueue) + { + if ([chain isRunning]) + { + return YES; + } + } + BufferChain *newChain = nil; nextStreamUserInfo = [sender userInfo]; @@ -286,6 +296,17 @@ [self addChainToQueue:newChain]; [newChain release]; + + // I'm stupid and can't hold too much stuff in my head all at once, so writing it here. + // + // Once we get here: + // - buffer chain for previous stream finished reading + // - there are (probably) some bytes of the previous stream in the output buffer which haven't been played + // (by output node) yet + // - self.bufferChain == previous playlist entry's buffer chain + // - self.nextStream == next playlist entry's URL + // - self.nextStreamUserInfo == next playlist entry + // - head of chainQueue is the buffer chain for the next entry (which has launched its threads already) } return YES; @@ -293,6 +314,11 @@ - (void)endOfInputPlayed { + // Once we get here: + // - the buffer chain for the next playlist entry (started in endOfInputReached) have been working for some time + // already, so that there is some decoded and converted data to play + // - the buffer chain for the next entry is the first item in chainQueue + @synchronized(chainQueue) { endOfInputReached = NO; @@ -307,11 +333,13 @@ return; } - [bufferChain release]; - - bufferChain = [chainQueue objectAtIndex:0]; + + BufferChain *oldChain = bufferChain; + bufferChain = [chainQueue objectAtIndex:0]; + [oldChain release]; [bufferChain retain]; - + + [chainQueue removeObjectAtIndex:0]; DLog(@"New!!! %@ %@", bufferChain, [[bufferChain inputNode] decoder]); [chainQueue removeObjectAtIndex:0]; diff --git a/Audio/Chain/BufferChain.h b/Audio/Chain/BufferChain.h index fbdea021a..f24b6f513 100644 --- a/Audio/Chain/BufferChain.h +++ b/Audio/Chain/BufferChain.h @@ -59,4 +59,6 @@ - (void)inputFormatDidChange:(AudioStreamBasicDescription)format; +- (BOOL)isRunning; + @end diff --git a/Audio/Chain/BufferChain.m b/Audio/Chain/BufferChain.m index b72f8cbad..0f2b3e002 100644 --- a/Audio/Chain/BufferChain.m +++ b/Audio/Chain/BufferChain.m @@ -125,7 +125,7 @@ [rgInfo release]; [userInfo release]; [streamURL release]; - + [inputNode release]; [converterNode release]; @@ -136,7 +136,7 @@ - (void)seek:(double)time { - long frame = time * [[[inputNode properties] objectForKey:@"sampleRate"] floatValue]; + long frame = (long) round(time * [[[inputNode properties] objectForKey:@"sampleRate"] floatValue]); [inputNode seek:frame]; } @@ -192,4 +192,14 @@ [converterNode setShouldContinue:s]; } +- (BOOL)isRunning +{ + InputNode *theInputNode = [self inputNode]; + if (nil != theInputNode && [theInputNode shouldContinue] && ![theInputNode endOfStream]) + { + return YES; + } + return NO; +} + @end diff --git a/Audio/Chain/ConverterNode.m b/Audio/Chain/ConverterNode.m index 8eddae539..9e3b3430f 100644 --- a/Audio/Chain/ConverterNode.m +++ b/Audio/Chain/ConverterNode.m @@ -98,7 +98,11 @@ static void scale_by_volume(float * buffer, int count, float volume) } //called from the complexfill when the audio is converted...good clean fun -static OSStatus ACInputProc(AudioConverterRef inAudioConverter, UInt32* ioNumberDataPackets, AudioBufferList* ioData, AudioStreamPacketDescription** outDataPacketDescription, void* inUserData) +static OSStatus ACInputProc(AudioConverterRef inAudioConverter, + UInt32* ioNumberDataPackets, + AudioBufferList* ioData, + AudioStreamPacketDescription** outDataPacketDescription, + void* inUserData) { ConverterNode *converter = (ConverterNode *)inUserData; OSStatus err = noErr; @@ -116,7 +120,9 @@ static OSStatus ACInputProc(AudioConverterRef inAudioConverter, UInt32* ioNumber amountToWrite = (*ioNumberDataPackets)*(converter->inputFormat.mBytesPerPacket); if (converter->callbackBuffer != NULL) + { free(converter->callbackBuffer); + } converter->callbackBuffer = malloc(amountToWrite); amountRead = [converter readData:converter->callbackBuffer amount:amountToWrite]; @@ -136,7 +142,11 @@ static OSStatus ACInputProc(AudioConverterRef inAudioConverter, UInt32* ioNumber return err; } -static OSStatus ACFloatProc(AudioConverterRef inAudioConverter, UInt32* ioNumberDataPackets, AudioBufferList* ioData, AudioStreamPacketDescription** outDataPacketDescription, void* inUserData) +static OSStatus ACFloatProc(AudioConverterRef inAudioConverter, + UInt32* ioNumberDataPackets, + AudioBufferList* ioData, + AudioStreamPacketDescription** outDataPacketDescription, + void* inUserData) { ConverterNode *converter = (ConverterNode *)inUserData; OSStatus err = noErr; diff --git a/Audio/Chain/InputNode.h b/Audio/Chain/InputNode.h index be52a684e..c0b440faa 100644 --- a/Audio/Chain/InputNode.h +++ b/Audio/Chain/InputNode.h @@ -16,6 +16,8 @@ #import "Node.h" #import "Plugin.h" +#define INPUT_NODE_SEEK + @interface InputNode : Node { id decoder; @@ -26,6 +28,8 @@ BOOL shouldSeek; long seekFrame; + + Semaphore *exitAtTheEndOfTheStream; } - (BOOL)openWithSource:(id)source; diff --git a/Audio/Chain/InputNode.m b/Audio/Chain/InputNode.m index e04042072..08d1362e4 100644 --- a/Audio/Chain/InputNode.m +++ b/Audio/Chain/InputNode.m @@ -16,6 +16,16 @@ @implementation InputNode +- (id)initWithController:(id)c previous:(id)p { + self = [super initWithController:c previous:p]; + if (self) { + exitAtTheEndOfTheStream = [[Semaphore alloc] init]; + } + + return self; +} + + - (BOOL)openWithSource:(id)source { decoder = [AudioDecoder audioDecoderForSource:source]; @@ -131,20 +141,39 @@ } DLog(@"End of stream? %@", [self properties]); + endOfStream = YES; shouldClose = [controller endOfInputReached]; //Lets us know if we should keep going or not (occassionally, for track changes within a file) DLog(@"closing? is %i", shouldClose); - break; + + // wait before exiting, as we might still get seeking request + DLog("InputNode: Before wait") + [exitAtTheEndOfTheStream waitIndefinitely]; + DLog("InputNode: After wait, should seek = %d", shouldSeek) + if (shouldSeek) + { + endOfStream = NO; + shouldClose = NO; + continue; + } + else + { + break; + } } [self writeData:inputBuffer amount:amountInBuffer]; amountInBuffer = 0; } } + if (shouldClose) [decoder close]; - free(inputBuffer); + + free(inputBuffer); + + [exitAtTheEndOfTheStream signal]; } - (void)seek:(long)frame @@ -153,6 +182,11 @@ shouldSeek = YES; DLog(@"Should seek!"); [semaphore signal]; + + if (endOfStream) + { + [exitAtTheEndOfTheStream signal]; + } } - (BOOL)setTrack:(NSURL *)track @@ -169,6 +203,9 @@ - (void)dealloc { DLog(@"Input Node dealloc"); + [exitAtTheEndOfTheStream signal]; + [exitAtTheEndOfTheStream wait]; // wait for decoder to be closed (see -(void)process ) + [exitAtTheEndOfTheStream release]; [decoder removeObserver:self forKeyPath:@"properties"]; [decoder removeObserver:self forKeyPath:@"metadata"]; diff --git a/Audio/Output/OutputCoreAudio.m b/Audio/Output/OutputCoreAudio.m index 85ac63e19..4a04b354a 100644 --- a/Audio/Output/OutputCoreAudio.m +++ b/Audio/Output/OutputCoreAudio.m @@ -108,7 +108,7 @@ static OSStatus Sound_Renderer(void *inRefCon, AudioUnitRenderActionFlags *ioAc sizeof(AudioDeviceID)); if (err != noErr) { - ALog(@"No output device could be found, your random error code is %i. Have a nice day!", err); + ALog(@"No output device could be found, your random error code is %d. Have a nice day!", err); return NO; } diff --git a/Audio/Utils/Semaphore.h b/Audio/Utils/Semaphore.h index a5c231666..39f98d16a 100644 --- a/Audio/Utils/Semaphore.h +++ b/Audio/Utils/Semaphore.h @@ -17,5 +17,6 @@ -(void)signal; -(void)timedWait:(int)seconds; -(void)wait; +-(void)waitIndefinitely; @end diff --git a/Audio/Utils/Semaphore.m b/Audio/Utils/Semaphore.m index b21e12b3d..620ff50da 100644 --- a/Audio/Utils/Semaphore.m +++ b/Audio/Utils/Semaphore.m @@ -40,4 +40,9 @@ semaphore_timedwait(semaphore, t); } +-(void)waitIndefinitely +{ + semaphore_wait(semaphore); +} + @end