diff --git a/PlaybackController.h b/PlaybackController.h index 2a0e233f5..ce27aac0b 100644 --- a/PlaybackController.h +++ b/PlaybackController.h @@ -20,6 +20,8 @@ IBOutlet NSButton *playButton; + NSTimer *positionTimer; + BOOL waitingForPlay; //No sneaky changing on us SoundController *soundController; @@ -52,9 +54,8 @@ //Methods since this is SoundController's delegate - (void)delegateNotifyStatusUpdate:(NSNumber *)status; -- (void)delegateNotifyPositionUpdate:(double)pos; - (void)delegateNotifyBitrateUpdate:(float)bitrate; -- (void)delegateNotifySongChanged:(double)length; +- (void)delegateNotifySongChanged; - (void)delegateRequestNextSong:(int)queueSize; @end diff --git a/PlaybackController.m b/PlaybackController.m index cdf155bf3..714ea26b4 100644 --- a/PlaybackController.m +++ b/PlaybackController.m @@ -86,6 +86,13 @@ // DBLog(@"PlayEntry: %@ Sent!", [pe filename]); if (playbackStatus != kCogStatusStopped) [self stop:self]; + + NSLog(@"LENGTH: %lf", [pe length]); + [positionSlider setMaxValue:[pe length]]; + [positionSlider setDoubleValue:0.0f]; + + [self updateTimeField:0.0f]; + [soundController play:[pe filename]]; } @@ -180,23 +187,26 @@ - (void)delegateRequestNextSong:(int)queueSize { PlaylistEntry *pe; - pe = [playlistController entryAtOffset:(queueSize+1)]; if (pe == nil) [soundController setNextSong:nil]; else + { + NSLog(@"NEXT SONG: %@", [pe filename]); [soundController setNextSong:[pe filename]]; + } } -- (void)delegateNotifySongChanged:(double)length +- (void)delegateNotifySongChanged { [playlistController next]; + PlaylistEntry *pe = [playlistController currentEntry];; -// [positionSlider setMaxValue:length]; -// [positionSlider setDoubleValue:0]; + [positionSlider setMaxValue:[pe length]]; + [positionSlider setDoubleValue:0.0f]; -// [self updateTimeField:0.0f]; + [self updateTimeField:0.0f]; } @@ -205,8 +215,10 @@ // [bitrateField setIntValue:bitrate]; } -- (void)delegateNotifyPositionUpdate:(double)pos +- (void)updatePosition:(id)sender { + double pos = [soundController amountPlayed]; + if ([positionSlider tracking] == NO) { // DBLog(@"Received pos update: %f", pos); @@ -221,11 +233,27 @@ int status = [s intValue]; if (status == kCogStatusStopped || status == kCogStatusPaused) { + NSLog(@"INVALIDATING"); + if (positionTimer) + { + [positionTimer invalidate]; + positionTimer = NULL; + } + if (status == kCogStatusStopped) + { + [positionSlider setDoubleValue:0.0f]; + + [self updateTimeField:0.0f]; + } + //Show play image [self changePlayButtonImage:@"play"]; } else if (status == kCogStatusPlaying) { + if (!positionTimer) + positionTimer = [NSTimer scheduledTimerWithTimeInterval:1.00 target:self selector:@selector(updatePosition:) userInfo:nil repeats:YES]; + //Show pause [self changePlayButtonImage:@"pause"]; } diff --git a/Playlist/DNDArrayController.m b/Playlist/DNDArrayController.m index 1bc74f3fb..7b9fd8d79 100755 --- a/Playlist/DNDArrayController.m +++ b/Playlist/DNDArrayController.m @@ -121,6 +121,7 @@ NSString *MovedRowsType = @"MOVED_ROWS_TYPE"; - (NSIndexSet *)indexSetFromRows:(NSArray *)rows { + NSLog(@"HELLO"); NSMutableIndexSet *indexSet = [NSMutableIndexSet indexSet]; NSEnumerator *rowEnumerator = [rows objectEnumerator]; NSNumber *idx; diff --git a/Sound/BufferChain.h b/Sound/BufferChain.h index a465153f8..12159c199 100644 --- a/Sound/BufferChain.h +++ b/Sound/BufferChain.h @@ -1,5 +1,5 @@ // -// InputChain.h +// BufferChain.h // CogNew // // Created by Zaphod Beeblebrox on 1/4/06. diff --git a/Sound/BufferChain.m b/Sound/BufferChain.m index 9c22c7cd6..c0a36e1dc 100644 --- a/Sound/BufferChain.m +++ b/Sound/BufferChain.m @@ -1,5 +1,5 @@ // -// InputChain.m +// BufferChain.m // CogNew // // Created by Zaphod Beeblebrox on 1/4/06. @@ -24,6 +24,9 @@ - (void)buildChain { + [inputNode release]; + [converterNode release]; + inputNode = [[InputNode alloc] initWithController:soundController previous:nil]; converterNode = [[ConverterNode alloc] initWithController:soundController previous:inputNode]; @@ -48,6 +51,14 @@ [converterNode launchThread]; } +- (void)dealloc +{ + [inputNode release]; + [converterNode release]; + + [super dealloc]; +} + - (id)finalNode { diff --git a/Sound/ConverterNode.h b/Sound/ConverterNode.h index f878666c4..f2b55fe97 100644 --- a/Sound/ConverterNode.h +++ b/Sound/ConverterNode.h @@ -1,5 +1,5 @@ // -// Converter.h +// ConverterNode.h // Cog // // Created by Zaphod Beeblebrox on 8/2/05. diff --git a/Sound/ConverterNode.m b/Sound/ConverterNode.m index 554dcad08..143fea662 100644 --- a/Sound/ConverterNode.m +++ b/Sound/ConverterNode.m @@ -1,5 +1,5 @@ // -// Converter.m +// ConverterNode.m // Cog // // Created by Zaphod Beeblebrox on 8/2/05. @@ -41,9 +41,11 @@ static OSStatus ACInputProc(AudioConverterRef inAudioConverter, UInt32* ioNumber void *readPtr; int amountToWrite; int availInput; + int amountRead; - if ([converter shouldContinue] == NO) + if ([converter shouldContinue] == NO || [converter endOfStream] == YES) { +// NSLog(@"END OF STREAM IN CONV"); ioData->mBuffers[0].mDataByteSize = 0; *ioNumberDataPackets = 0; @@ -51,7 +53,28 @@ 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]; +/* if ([converter endOfStream] == YES) + { + ioData->mBuffers[0].mDataByteSize = 0; + *ioNumberDataPackets = 0; + + return noErr; + } +*/ if (amountRead == 0) + { + ioData->mBuffers[0].mDataByteSize = 0; + *ioNumberDataPackets = 0; + + return 100; //Keep asking for data + } + + /* availInput = [[previousNode buffer] lengthAvailableToReadReturningPointer:&readPtr]; if (availInput == 0 ) { @@ -59,10 +82,10 @@ static OSStatus ACInputProc(AudioConverterRef inAudioConverter, UInt32* ioNumber ioData->mBuffers[0].mDataByteSize = 0; *ioNumberDataPackets = 0; - if ([previousNode endOfInput] == YES) + if ([previousNode endOfStream] == YES) { NSLog(@"END OF CONVERTER INPUT"); - [converter setEndOfInput:YES]; + [converter setEndOfStream:YES]; [converter setShouldContinue:NO]; return noErr; @@ -86,9 +109,10 @@ static OSStatus ACInputProc(AudioConverterRef inAudioConverter, UInt32* ioNumber [[previousNode buffer] didReadLength:amountToWrite]; [[previousNode semaphore] signal]; } - +*/ +// NSLog(@"Amount read: %@ %i", converter, amountRead); ioData->mBuffers[0].mData = converter->callbackBuffer; - ioData->mBuffers[0].mDataByteSize = amountToWrite; + ioData->mBuffers[0].mDataByteSize = amountRead; ioData->mBuffers[0].mNumberChannels = (converter->inputFormat.mChannelsPerFrame); ioData->mNumberBuffers = 1; @@ -97,12 +121,31 @@ static OSStatus ACInputProc(AudioConverterRef inAudioConverter, UInt32* ioNumber -(void)process { - void *writePtr; + char writeBuf[CHUNK_SIZE]; + int amountConverted; + + + while ([self shouldContinue] == YES) //Need to watch EOS somehow.... + { + amountConverted = [self convert:writeBuf amount:CHUNK_SIZE]; + +// NSLog(@"Amount converted %@: %i %i", self, amountConverted, [self endOfStream]); + if (amountConverted == 0 && [self endOfStream] == YES) + { +// NSLog(@"END OF STREAM FOR ZINE DINNER!!!!"); + return; + } + + [self writeData:writeBuf amount:amountConverted]; + } + +/* void *writePtr; int availOutput; int amountConverted; while ([self shouldContinue] == YES) { + availOutput = [buffer lengthAvailableToWriteReturningPointer:&writePtr]; while (availOutput == 0) @@ -122,6 +165,7 @@ static OSStatus ACInputProc(AudioConverterRef inAudioConverter, UInt32* ioNumber if (amountConverted > 0) [buffer didWriteLength:amountConverted]; } + */ } - (int)convert:(void *)dest amount:(int)amount @@ -137,8 +181,12 @@ static OSStatus ACInputProc(AudioConverterRef inAudioConverter, UInt32* ioNumber ioData.mNumberBuffers = 1; err = AudioConverterFillComplexBuffer(converter, ACInputProc, self, &ioNumberFrames, &ioData, NULL); -// if (err != noErr) -// DBLog(@"Converter error: %i", err); + if (err == kAudioConverterErr_InvalidInputSize) //It returns insz at EOS at times...so run it again to make sure all data is converted + { + return [self convert:dest amount:amount]; + } + if (err != noErr) + NSLog(@"Converter error: %i", err); return ioData.mBuffers[0].mDataByteSize; /* diff --git a/Sound/InputNode.h b/Sound/InputNode.h index cc1e3f232..5586754b7 100644 --- a/Sound/InputNode.h +++ b/Sound/InputNode.h @@ -1,5 +1,5 @@ // -// InputController.h +// InputNode.h // Cog // // Created by Zaphod Beeblebrox on 8/2/05. diff --git a/Sound/InputNode.m b/Sound/InputNode.m index 2c8167681..8bf757467 100644 --- a/Sound/InputNode.m +++ b/Sound/InputNode.m @@ -1,5 +1,5 @@ // -// InputController.m +// InputNode.m // Cog // // Created by Zaphod Beeblebrox on 8/2/05. @@ -17,7 +17,7 @@ [soundFile getFormat:&format]; - endOfInput = NO; + shouldContinue = YES; } - (void)process @@ -28,21 +28,20 @@ DBLog(@"Playing file.\n"); - while ([self shouldContinue] == YES) + while ([self shouldContinue] == YES && [self endOfStream] == NO) { amountRead = [soundFile fillBuffer:buf ofSize: chunk_size]; if (amountRead <= 0) { - endOfInput = YES; + endOfStream = YES; NSLog(@"END OF INPUT WAS REACHED"); [controller endOfInputReached]; - shouldContinue = NO; [soundFile close]; return; //eof } [self writeData:buf amount:amountRead]; } - + [soundFile close]; } diff --git a/Sound/Node.h b/Sound/Node.h index 50d0f2d28..01434c041 100644 --- a/Sound/Node.h +++ b/Sound/Node.h @@ -1,5 +1,5 @@ // -// InputChainLink.h +// Node.h // CogNew // // Created by Zaphod Beeblebrox on 1/4/06. @@ -21,7 +21,7 @@ id controller; BOOL shouldContinue; - BOOL endOfInput; //All data is now in buffer + BOOL endOfStream; //All data is now in buffer } - (id)initWithPrevious:(id)p; @@ -42,7 +42,7 @@ - (Semaphore *)semaphore; -- (BOOL)endOfInput; -- (void)setEndOfInput:(BOOL)e; +- (BOOL)endOfStream; +- (void)setEndOfStream:(BOOL)e; @end diff --git a/Sound/Node.m b/Sound/Node.m index 28e6add8a..71e7f4f53 100644 --- a/Sound/Node.m +++ b/Sound/Node.m @@ -1,5 +1,5 @@ // -// InputChainLink.m +// Node.m // CogNew // // Created by Zaphod Beeblebrox on 1/4/06. @@ -20,7 +20,8 @@ controller = c; previousNode = p; - endOfInput = NO; + endOfStream = NO; + shouldContinue = YES; } return self; @@ -31,33 +32,30 @@ void *writePtr; int amountToCopy, availOutput; int amountLeft = amount; - - do + + while (shouldContinue == YES && amountLeft > 0) { availOutput = [buffer lengthAvailableToWriteReturningPointer:&writePtr]; - while (availOutput < CHUNK_SIZE) + + if (availOutput == 0) { [semaphore wait]; + } + else + { + amountToCopy = availOutput; + if (amountToCopy > amountLeft) + amountToCopy = amountLeft; - if (shouldContinue == NO) + memcpy(writePtr, &((char *)ptr)[amount - amountLeft], amountToCopy); + if (amountToCopy > 0) { - return (amount - amountLeft); + [buffer didWriteLength:amountToCopy]; } - availOutput = [buffer lengthAvailableToWriteReturningPointer:&writePtr]; + amountLeft -= amountToCopy; } - amountToCopy = availOutput; - if (amountToCopy > amountLeft) - amountToCopy = amountLeft; - - memcpy(writePtr, &((char *)ptr)[amount - amountLeft], amountToCopy); - if (amountToCopy > 0) - { - [buffer didWriteLength:amountToCopy]; - } - - amountLeft -= amountToCopy; - } while (amountLeft > 0); + } return (amount - amountLeft); } @@ -72,8 +70,12 @@ { NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; DBLog(@"In thread entry"); + [self retain]; + [self process]; - + + [self release]; + [pool release]; } @@ -85,8 +87,17 @@ availInput = [[previousNode buffer] lengthAvailableToReadReturningPointer:&readPtr]; + if (availInput <= amount && [previousNode endOfStream] == YES) + { +// NSLog(@"RELEASING: %i %i %i", availInput, [previousNode endOfStream], shouldContinue); +// [previousNode release]; + //If it is the outputNode, [soundController newInputChain]; + //else + endOfStream = YES; + } + amountToCopy = availInput; - if (availInput > amount) + if (amountToCopy > amount) { amountToCopy = amount; } @@ -96,14 +107,9 @@ if (amountToCopy > 0) { [[previousNode buffer] didReadLength:amountToCopy]; + [[previousNode semaphore] signal]; } - //Do endOfInput fun now... - if ((amountToCopy <= 0) && ([previousNode endOfInput] == YES)) - { - endOfInput = YES; - shouldContinue = NO; - } return amountToCopy; } @@ -139,14 +145,14 @@ return semaphore; } -- (BOOL)endOfInput +- (BOOL)endOfStream { - return endOfInput; + return endOfStream; } -- (void)setEndOfInput:(BOOL)e +- (void)setEndOfStream:(BOOL)e { - endOfInput = e; + endOfStream = e; } @end diff --git a/Sound/OutputCoreAudio.m b/Sound/OutputCoreAudio.m index da5d09bbe..bd6b05c66 100644 --- a/Sound/OutputCoreAudio.m +++ b/Sound/OutputCoreAudio.m @@ -17,6 +17,7 @@ if (self) { outputController = c; + outputUnit = NULL; } return self; @@ -32,14 +33,24 @@ static OSStatus Sound_Renderer(void *inRefCon, AudioUnitRenderActionFlags *ioAc if ([output->outputController shouldContinue] == NO) { + NSLog(@"STOPPING"); AudioOutputUnitStop(output->outputUnit); - +// [output stop]; + return err; } amountToRead = inNumberFrames*(output->deviceFormat.mBytesPerPacket); amountRead = [output->outputController readData:(readPointer) amount:amountToRead]; +// NSLog(@"Amount read: %i %i", amountRead, [output->outputController endOfStream]); + if ((amountRead < amountToRead) && [output->outputController endOfStream] == NO) //Try one more time! for track changes! + { + int amountRead2; //Use this since return type of readdata isnt known...may want to fix then can do a simple += to readdata + amountRead2 = [output->outputController readData:(readPointer+amountRead) amount:amountToRead-amountRead]; + amountRead += amountRead2; + } +// NSLog(@"Amount read: %i", amountRead); ioData->mBuffers[0].mDataByteSize = amountRead; return err; @@ -48,6 +59,9 @@ static OSStatus Sound_Renderer(void *inRefCon, AudioUnitRenderActionFlags *ioAc - (BOOL)setup { NSLog(@"SETUP"); + if (outputUnit) + [self stop]; + ComponentDescription desc; OSStatus err; @@ -154,6 +168,11 @@ static OSStatus Sound_Renderer(void *inRefCon, AudioUnitRenderActionFlags *ioAc } } +- (void)dealloc +{ + [self stop]; +} + - (void)pause { AudioOutputUnitStop(outputUnit); diff --git a/Sound/OutputNode.h b/Sound/OutputNode.h index 2f45fc9d7..50c96b318 100644 --- a/Sound/OutputNode.h +++ b/Sound/OutputNode.h @@ -1,5 +1,5 @@ // -// OutputController.h +// OutputNode.h // Cog // // Created by Zaphod Beeblebrox on 8/2/05. @@ -18,13 +18,17 @@ @interface OutputNode : Node { AudioStreamBasicDescription format; + int amountPlayed; OutputCoreAudio *output; } - (id)initWithController:(id)c previousLink:p; +- (double)amountPlayed; + - (void)setup; - (void)process; +- (void)close; - (int)readData:(void *)ptr amount:(int)amount; diff --git a/Sound/OutputNode.m b/Sound/OutputNode.m index 687153183..3e8a94734 100644 --- a/Sound/OutputNode.m +++ b/Sound/OutputNode.m @@ -1,5 +1,5 @@ // -// OutputController.m +// OutputNode.m // Cog // // Created by Zaphod Beeblebrox on 8/2/05. @@ -13,6 +13,8 @@ - (void)setup { + amountPlayed = 0; + output = [[OutputCoreAudio alloc] initWithController:self]; [output setup]; @@ -35,39 +37,37 @@ - (int)readData:(void *)ptr amount:(int)amount { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + int n; - previousNode = [[controller bufferChain] finalNode]; n = [super readData:ptr amount:amount]; - if ((n == 0) && (endOfInput == YES)) +// NSLog(@"N: %i %i", n, endOfStream); + if (endOfStream == YES) { - endOfInput = NO; - shouldContinue = YES; - NSLog(@"DONE IN"); - return 0; + NSLog(@"End of stream reached: %i", endOfStream); + + amountPlayed = 0; + [controller endOfInputPlayed]; //Updates shouldContinue appropriately? + NSLog(@"End of stream reached: %i", endOfStream); +// return (n + [self readData:ptr amount:(amount-n)]); } - void *tempPtr; - - if (([[[[controller bufferChain] finalNode] buffer] lengthAvailableToReadReturningPointer:&tempPtr] == 0) && ([[[controller bufferChain] finalNode] endOfInput] == YES)) - { - NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; - - NSLog(@"END OF OUTPUT INPUT?!"); - [controller endOfInputPlayed]; - endOfInput = YES; + amountPlayed += n; - [pool release]; - - return n + [self readData:&ptr[n] amount:(amount-n)]; - - - } + [pool release]; return n; } + +- (double)amountPlayed +{ + + return (amountPlayed/format.mBytesPerFrame)/(format.mSampleRate/1000.0); +} + - (AudioStreamBasicDescription) format { return format; @@ -78,6 +78,16 @@ format = *f; } +- (void)close +{ + [output stop]; +} + +- (void)dealloc +{ + [output release]; +} + - (void)setVolume:(double) v { [output setVolume:v]; @@ -87,7 +97,7 @@ { [super setShouldContinue:s]; - if (s == NO) - [output stop]; +// if (s == NO) +// [output stop]; } @end diff --git a/Sound/SoundController.h b/Sound/SoundController.h index 08a8b58b4..7573f68ff 100644 --- a/Sound/SoundController.h +++ b/Sound/SoundController.h @@ -37,6 +37,9 @@ - (void)seekToTime:(double)time; - (void)setVolume:(double)v; +- (double)amountPlayed; + + - (void)setNextSong:(NSString *)s; - (void)setPlaybackStatus:(int)s; diff --git a/Sound/SoundController.m b/Sound/SoundController.m index 39a3b44ec..cee7db894 100644 --- a/Sound/SoundController.m +++ b/Sound/SoundController.m @@ -1,5 +1,5 @@ // -// Controller.m +// SoundController.m // Cog // // Created by Zaphod Beeblebrox on 8/7/05. @@ -19,8 +19,8 @@ if (self) { //things - output = [[OutputNode alloc] initWithController:self previous:nil]; - bufferChain = [[BufferChain alloc] initWithController:self]; + output = NULL; + bufferChain = NULL; chainQueue = [[NSMutableArray alloc] init]; @@ -33,7 +33,28 @@ - (void)play:(NSString *)filename { DBLog(@"OPENING FILE: %s\n", filename); + + 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]; [bufferChain open:filename]; [self setShouldContinue:YES]; @@ -91,6 +112,11 @@ [output setShouldContinue:s]; } +- (double)amountPlayed +{ + return [output amountPlayed]; +} + - (void)endOfInputReached { [delegate delegateRequestNextSong:[chainQueue count]]; @@ -117,7 +143,8 @@ if ([chainQueue count] <= 0) { //End of playlist - [self setPlaybackStatus:kCogStatusStopped]; + NSLog(@"STOPPED"); + [self stop]; return; } @@ -130,14 +157,12 @@ [chainQueue removeObjectAtIndex:0]; - NSLog(@"SONG CHANGED"); - [delegate delegateNotifySongChanged:0.0]; - - // NSLog(@"SWAPPED"); + [delegate delegateNotifySongChanged]; + [output setEndOfStream:NO]; } - (void)setPlaybackStatus:(int)s -{ +{ [delegate performSelectorOnMainThread:@selector(delegateNotifyStatusUpdate:) withObject:[NSNumber numberWithInt:s] waitUntilDone:NO]; }