Seeking now takes the true nuclear approach to output, and should no longer have glitches
parent
d22ee14a36
commit
dfeca7aa97
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
@class BufferChain;
|
@class BufferChain;
|
||||||
@class OutputNode;
|
@class OutputNode;
|
||||||
|
@protocol CogDecoder;
|
||||||
|
|
||||||
@interface AudioPlayer : NSObject
|
@interface AudioPlayer : NSObject
|
||||||
{
|
{
|
||||||
|
@ -19,6 +20,10 @@
|
||||||
double volume;
|
double volume;
|
||||||
|
|
||||||
NSMutableArray *chainQueue;
|
NSMutableArray *chainQueue;
|
||||||
|
|
||||||
|
NSURL *currentStream;
|
||||||
|
id currentUserInfo;
|
||||||
|
NSDictionary *currentRGInfo;
|
||||||
|
|
||||||
NSURL *nextStream;
|
NSURL *nextStream;
|
||||||
id nextStreamUserInfo;
|
id nextStreamUserInfo;
|
||||||
|
@ -30,6 +35,7 @@
|
||||||
BOOL endOfInputReached;
|
BOOL endOfInputReached;
|
||||||
BOOL startedPaused;
|
BOOL startedPaused;
|
||||||
BOOL initialBufferFilled;
|
BOOL initialBufferFilled;
|
||||||
|
BOOL paused;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (id)init;
|
- (id)init;
|
||||||
|
@ -40,6 +46,7 @@
|
||||||
- (void)play:(NSURL *)url;
|
- (void)play:(NSURL *)url;
|
||||||
- (void)play:(NSURL *)url withUserInfo:(id)userInfo withRGInfo:(NSDictionary*)rgi;
|
- (void)play:(NSURL *)url withUserInfo:(id)userInfo withRGInfo:(NSDictionary*)rgi;
|
||||||
- (void)play:(NSURL *)url withUserInfo:(id)userInfo withRGInfo:(NSDictionary*)rgi startPaused:(BOOL)paused;
|
- (void)play:(NSURL *)url withUserInfo:(id)userInfo withRGInfo:(NSDictionary*)rgi startPaused:(BOOL)paused;
|
||||||
|
- (void)play:(id<CogDecoder>)decoder startPaused:(BOOL)paused;
|
||||||
|
|
||||||
- (void)stop;
|
- (void)stop;
|
||||||
- (void)pause;
|
- (void)pause;
|
||||||
|
|
|
@ -55,9 +55,9 @@
|
||||||
|
|
||||||
- (void)play:(NSURL *)url withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi startPaused:(BOOL)paused
|
- (void)play:(NSURL *)url withUserInfo:(id)userInfo withRGInfo:(NSDictionary *)rgi startPaused:(BOOL)paused
|
||||||
{
|
{
|
||||||
output = [[OutputNode alloc] initWithController:self previous:nil];
|
if (output) {
|
||||||
[output setup];
|
[output close];
|
||||||
[output setVolume: volume];
|
}
|
||||||
@synchronized(chainQueue) {
|
@synchronized(chainQueue) {
|
||||||
for (id anObject in chainQueue)
|
for (id anObject in chainQueue)
|
||||||
{
|
{
|
||||||
|
@ -72,9 +72,16 @@
|
||||||
bufferChain = nil;
|
bufferChain = nil;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
output = [[OutputNode alloc] initWithController:self previous:nil];
|
||||||
|
[output setup];
|
||||||
|
[output setVolume: volume];
|
||||||
|
|
||||||
bufferChain = [[BufferChain alloc] initWithController:self];
|
bufferChain = [[BufferChain alloc] initWithController:self];
|
||||||
[self notifyStreamChanged:userInfo];
|
[self notifyStreamChanged:userInfo];
|
||||||
|
|
||||||
|
currentStream = url;
|
||||||
|
currentUserInfo = userInfo;
|
||||||
|
currentRGInfo = rgi;
|
||||||
|
|
||||||
while (![bufferChain open:url withOutputFormat:[output format] withRGInfo:rgi])
|
while (![bufferChain open:url withOutputFormat:[output format] withRGInfo:rgi])
|
||||||
{
|
{
|
||||||
|
@ -87,10 +94,14 @@
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
userInfo = nextStreamUserInfo;
|
userInfo = nextStreamUserInfo;
|
||||||
rgi = nextStreamRGInfo;
|
rgi = nextStreamRGInfo;
|
||||||
|
|
||||||
|
currentStream = url;
|
||||||
|
currentUserInfo = userInfo;
|
||||||
|
currentRGInfo = rgi;
|
||||||
|
|
||||||
[self notifyStreamChanged:userInfo];
|
[self notifyStreamChanged:userInfo];
|
||||||
|
|
||||||
bufferChain = [[BufferChain alloc] initWithController:self];
|
bufferChain = [[BufferChain alloc] initWithController:self];
|
||||||
|
@ -108,6 +119,78 @@
|
||||||
|
|
||||||
if (paused)
|
if (paused)
|
||||||
[self setPlaybackStatus:CogStatusPaused waitUntilDone:YES];
|
[self setPlaybackStatus:CogStatusPaused waitUntilDone:YES];
|
||||||
|
|
||||||
|
self->paused = paused;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)play:(id<CogDecoder>)decoder startPaused:(BOOL)paused
|
||||||
|
{
|
||||||
|
if (output) {
|
||||||
|
[output close];
|
||||||
|
}
|
||||||
|
@synchronized(chainQueue) {
|
||||||
|
for (id anObject in chainQueue)
|
||||||
|
{
|
||||||
|
[anObject setShouldContinue:NO];
|
||||||
|
}
|
||||||
|
[chainQueue removeAllObjects];
|
||||||
|
endOfInputReached = NO;
|
||||||
|
if (bufferChain)
|
||||||
|
{
|
||||||
|
[bufferChain setShouldContinue:NO];
|
||||||
|
|
||||||
|
bufferChain = nil;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output = [[OutputNode alloc] initWithController:self previous:nil];
|
||||||
|
[output setup];
|
||||||
|
[output setVolume: volume];
|
||||||
|
|
||||||
|
bufferChain = [[BufferChain alloc] initWithController:self];
|
||||||
|
[self notifyStreamChanged:currentUserInfo];
|
||||||
|
|
||||||
|
NSURL *url = currentStream;
|
||||||
|
id userInfo = currentUserInfo;
|
||||||
|
NSDictionary *rgi = currentRGInfo;
|
||||||
|
|
||||||
|
while (![bufferChain openWithDecoder:decoder withOutputFormat:[output format] withRGInfo:currentRGInfo])
|
||||||
|
{
|
||||||
|
bufferChain = nil;
|
||||||
|
|
||||||
|
[self requestNextStream: userInfo];
|
||||||
|
|
||||||
|
url = nextStream;
|
||||||
|
if (url == nil)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
userInfo = nextStreamUserInfo;
|
||||||
|
rgi = nextStreamRGInfo;
|
||||||
|
|
||||||
|
currentStream = url;
|
||||||
|
currentUserInfo = userInfo;
|
||||||
|
currentRGInfo = rgi;
|
||||||
|
|
||||||
|
[self notifyStreamChanged:userInfo];
|
||||||
|
|
||||||
|
bufferChain = [[BufferChain alloc] initWithController:self];
|
||||||
|
}
|
||||||
|
|
||||||
|
[bufferChain setUserInfo:userInfo];
|
||||||
|
|
||||||
|
[self setShouldContinue:YES];
|
||||||
|
|
||||||
|
outputLaunched = NO;
|
||||||
|
startedPaused = paused;
|
||||||
|
initialBufferFilled = NO;
|
||||||
|
|
||||||
|
[bufferChain launchThreads];
|
||||||
|
|
||||||
|
if (paused)
|
||||||
|
[self setPlaybackStatus:CogStatusPaused waitUntilDone:YES];
|
||||||
|
|
||||||
|
self->paused = paused;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)stop
|
- (void)stop
|
||||||
|
@ -122,6 +205,8 @@
|
||||||
[output pause];
|
[output pause];
|
||||||
|
|
||||||
[self setPlaybackStatus:CogStatusPaused waitUntilDone:YES];
|
[self setPlaybackStatus:CogStatusPaused waitUntilDone:YES];
|
||||||
|
|
||||||
|
paused = YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)resume
|
- (void)resume
|
||||||
|
@ -136,12 +221,15 @@
|
||||||
[output resume];
|
[output resume];
|
||||||
|
|
||||||
[self setPlaybackStatus:CogStatusPlaying waitUntilDone:YES];
|
[self setPlaybackStatus:CogStatusPlaying waitUntilDone:YES];
|
||||||
|
|
||||||
|
paused = NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)seekToTime:(double)time
|
- (void)seekToTime:(double)time
|
||||||
{
|
{
|
||||||
//Need to reset everything's buffers, and then seek?
|
//Need to reset everything's buffers, and then seek?
|
||||||
/*HACK TO TEST HOW WELL THIS WOULD WORK*/
|
/*HACK TO TEST HOW WELL THIS WOULD WORK*/
|
||||||
|
[self play:[[bufferChain inputNode] stealDecoder] startPaused:paused];
|
||||||
[output seek:time];
|
[output seek:time];
|
||||||
[bufferChain seek:time];
|
[bufferChain seek:time];
|
||||||
/*END HACK*/
|
/*END HACK*/
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
InputNode *inputNode;
|
InputNode *inputNode;
|
||||||
ConverterNode *converterNode;
|
ConverterNode *converterNode;
|
||||||
|
|
||||||
AudioStreamBasicDescription _inputFormat;
|
AudioStreamBasicDescription inputFormat;
|
||||||
|
|
||||||
NSURL *streamURL;
|
NSURL *streamURL;
|
||||||
id userInfo;
|
id userInfo;
|
||||||
|
@ -35,11 +35,17 @@
|
||||||
//Used when changing tracks to reuse the same decoder
|
//Used when changing tracks to reuse the same decoder
|
||||||
- (BOOL)openWithInput:(InputNode *)i withOutputFormat:(AudioStreamBasicDescription)outputFormat withRGInfo:(NSDictionary*)rgi;
|
- (BOOL)openWithInput:(InputNode *)i withOutputFormat:(AudioStreamBasicDescription)outputFormat withRGInfo:(NSDictionary*)rgi;
|
||||||
|
|
||||||
|
//Used when resetting the decoder on seek
|
||||||
|
- (BOOL)openWithDecoder:(id<CogDecoder>)decoder
|
||||||
|
withOutputFormat:(AudioStreamBasicDescription)outputFormat
|
||||||
|
withRGInfo:(NSDictionary*)rgi;
|
||||||
|
|
||||||
- (void)seek:(double)time;
|
- (void)seek:(double)time;
|
||||||
|
|
||||||
- (void)launchThreads;
|
- (void)launchThreads;
|
||||||
|
|
||||||
- (InputNode *)inputNode;
|
- (InputNode *)inputNode;
|
||||||
|
- (InputNode *)stealInputNode;
|
||||||
|
|
||||||
- (id)finalNode;
|
- (id)finalNode;
|
||||||
|
|
||||||
|
|
|
@ -63,7 +63,7 @@
|
||||||
if (![inputNode openWithSource:source])
|
if (![inputNode openWithSource:source])
|
||||||
return NO;
|
return NO;
|
||||||
|
|
||||||
if (![converterNode setupWithInputFormat:(_inputFormat = propertiesToASBD([inputNode properties])) outputFormat:outputFormat])
|
if (![converterNode setupWithInputFormat:(inputFormat = propertiesToASBD([inputNode properties])) outputFormat:outputFormat])
|
||||||
return NO;
|
return NO;
|
||||||
|
|
||||||
[self setRGInfo:rgi];
|
[self setRGInfo:rgi];
|
||||||
|
@ -82,7 +82,7 @@
|
||||||
return NO;
|
return NO;
|
||||||
|
|
||||||
DLog(@"Input Properties: %@", [inputNode properties]);
|
DLog(@"Input Properties: %@", [inputNode properties]);
|
||||||
if (![converterNode setupWithInputFormat:(_inputFormat = propertiesToASBD([inputNode properties])) outputFormat:outputFormat])
|
if (![converterNode setupWithInputFormat:(inputFormat = propertiesToASBD([inputNode properties])) outputFormat:outputFormat])
|
||||||
return NO;
|
return NO;
|
||||||
|
|
||||||
[self setRGInfo:rgi];
|
[self setRGInfo:rgi];
|
||||||
|
@ -90,6 +90,25 @@
|
||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (BOOL)openWithDecoder:(id<CogDecoder>)decoder
|
||||||
|
withOutputFormat:(AudioStreamBasicDescription)outputFormat
|
||||||
|
withRGInfo:(NSDictionary*)rgi;
|
||||||
|
{
|
||||||
|
DLog(@"New buffer chain!");
|
||||||
|
[self buildChain];
|
||||||
|
|
||||||
|
if (![inputNode openWithDecoder:decoder])
|
||||||
|
return NO;
|
||||||
|
|
||||||
|
DLog(@"Input Properties: %@", [inputNode properties]);
|
||||||
|
if (![converterNode setupWithInputFormat:(inputFormat = propertiesToASBD([inputNode properties])) outputFormat:outputFormat])
|
||||||
|
return NO;
|
||||||
|
|
||||||
|
[self setRGInfo:rgi];
|
||||||
|
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
- (void)launchThreads
|
- (void)launchThreads
|
||||||
{
|
{
|
||||||
DLog(@"Properties: %@", [inputNode properties]);
|
DLog(@"Properties: %@", [inputNode properties]);
|
||||||
|
@ -206,7 +225,7 @@
|
||||||
|
|
||||||
- (AudioStreamBasicDescription)inputFormat
|
- (AudioStreamBasicDescription)inputFormat
|
||||||
{
|
{
|
||||||
return _inputFormat;
|
return inputFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -36,6 +36,8 @@
|
||||||
- (BOOL)openWithSource:(id<CogSource>)source;
|
- (BOOL)openWithSource:(id<CogSource>)source;
|
||||||
- (BOOL)openWithDecoder:(id<CogDecoder>) d;
|
- (BOOL)openWithDecoder:(id<CogDecoder>) d;
|
||||||
|
|
||||||
|
- (id<CogDecoder>)stealDecoder;
|
||||||
|
|
||||||
- (void)process;
|
- (void)process;
|
||||||
- (NSDictionary *) properties;
|
- (NSDictionary *) properties;
|
||||||
- (void)seek:(long)frame;
|
- (void)seek:(long)frame;
|
||||||
|
|
|
@ -215,11 +215,16 @@
|
||||||
return NO;
|
return NO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)removeObservers
|
||||||
|
{
|
||||||
|
[decoder removeObserver:self forKeyPath:@"properties"];
|
||||||
|
[decoder removeObserver:self forKeyPath:@"metadata"];
|
||||||
|
}
|
||||||
|
|
||||||
- (void)dealloc
|
- (void)dealloc
|
||||||
{
|
{
|
||||||
DLog(@"Input Node dealloc");
|
DLog(@"Input Node dealloc");
|
||||||
[decoder removeObserver:self forKeyPath:@"properties"];
|
[self removeObservers];
|
||||||
[decoder removeObserver:self forKeyPath:@"metadata"];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSDictionary *) properties
|
- (NSDictionary *) properties
|
||||||
|
@ -232,4 +237,13 @@
|
||||||
return decoder;
|
return decoder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (id<CogDecoder>) stealDecoder
|
||||||
|
{
|
||||||
|
[self removeObservers];
|
||||||
|
id<CogDecoder> decoder = self->decoder;
|
||||||
|
self->decoder = [[NSClassFromString(@"SilenceDecoder") alloc] init];
|
||||||
|
[self registerObservers];
|
||||||
|
return decoder;
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
Loading…
Reference in New Issue