2006-01-20 15:34:02 +00:00
|
|
|
//
|
2006-04-02 15:44:08 +00:00
|
|
|
// InputNode.m
|
2006-01-20 15:34:02 +00:00
|
|
|
// Cog
|
|
|
|
//
|
2006-09-04 18:46:18 +00:00
|
|
|
// Created by Vincent Spader on 8/2/05.
|
|
|
|
// Copyright 2005 Vincent Spader. All rights reserved.
|
2006-01-20 15:34:02 +00:00
|
|
|
//
|
|
|
|
|
|
|
|
#import "InputNode.h"
|
2022-02-07 05:49:27 +00:00
|
|
|
#import "AudioPlayer.h"
|
2007-02-24 20:36:27 +00:00
|
|
|
#import "BufferChain.h"
|
2007-03-03 17:19:37 +00:00
|
|
|
#import "CoreAudioUtils.h"
|
2013-10-21 05:04:09 +00:00
|
|
|
#import "OutputNode.h"
|
2022-02-07 05:49:27 +00:00
|
|
|
#import "Plugin.h"
|
2013-10-02 06:00:16 +00:00
|
|
|
|
2022-02-15 04:02:18 +00:00
|
|
|
#import "NSDictionary+Merge.h"
|
|
|
|
|
2013-10-11 12:03:55 +00:00
|
|
|
#import "Logging.h"
|
|
|
|
|
2006-01-20 15:34:02 +00:00
|
|
|
@implementation InputNode
|
2022-06-15 23:47:43 +00:00
|
|
|
|
|
|
|
static void *kInputNodeContext = &kInputNodeContext;
|
|
|
|
|
2013-10-12 20:59:34 +00:00
|
|
|
@synthesize exitAtTheEndOfTheStream;
|
|
|
|
|
2013-10-12 20:52:58 +00:00
|
|
|
- (id)initWithController:(id)c previous:(id)p {
|
2022-02-07 05:49:27 +00:00
|
|
|
self = [super initWithController:c previous:p];
|
|
|
|
if(self) {
|
|
|
|
exitAtTheEndOfTheStream = [[Semaphore alloc] init];
|
|
|
|
}
|
2013-10-12 20:52:58 +00:00
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
return self;
|
2013-10-12 20:52:58 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
- (BOOL)openWithSource:(id<CogSource>)source {
|
2022-06-17 01:16:09 +00:00
|
|
|
[self removeObservers];
|
|
|
|
|
2007-10-14 18:12:15 +00:00
|
|
|
decoder = [AudioDecoder audioDecoderForSource:source];
|
2007-03-03 17:19:37 +00:00
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
if(decoder == nil)
|
2006-04-13 02:51:22 +00:00
|
|
|
return NO;
|
2007-02-24 20:36:27 +00:00
|
|
|
|
2007-10-11 02:08:29 +00:00
|
|
|
[self registerObservers];
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
if(![decoder open:source]) {
|
2013-10-11 12:03:55 +00:00
|
|
|
ALog(@"Couldn't open decoder...");
|
2007-02-24 20:36:27 +00:00
|
|
|
return NO;
|
2007-03-02 01:36:52 +00:00
|
|
|
}
|
2022-02-07 05:49:27 +00:00
|
|
|
|
2007-11-24 20:16:27 +00:00
|
|
|
NSDictionary *properties = [decoder properties];
|
|
|
|
int bitsPerSample = [[properties objectForKey:@"bitsPerSample"] intValue];
|
|
|
|
int channels = [[properties objectForKey:@"channels"] intValue];
|
2022-02-07 05:49:27 +00:00
|
|
|
|
2021-12-11 08:22:19 +00:00
|
|
|
bytesPerFrame = ((bitsPerSample + 7) / 8) * channels;
|
2022-02-07 05:49:27 +00:00
|
|
|
|
|
|
|
nodeFormat = propertiesToASBD(properties);
|
2022-02-07 08:56:05 +00:00
|
|
|
if([properties valueForKey:@"channelConfig"])
|
2022-02-08 03:18:45 +00:00
|
|
|
nodeChannelConfig = [[properties valueForKey:@"channelConfig"] unsignedIntValue];
|
2022-02-07 05:49:27 +00:00
|
|
|
nodeLossless = [[properties valueForKey:@"encoding"] isEqualToString:@"lossless"];
|
|
|
|
|
2006-04-02 15:44:08 +00:00
|
|
|
shouldContinue = YES;
|
2006-04-02 20:03:12 +00:00
|
|
|
shouldSeek = NO;
|
2007-11-24 20:16:27 +00:00
|
|
|
|
2006-04-13 02:51:22 +00:00
|
|
|
return YES;
|
2006-01-20 15:34:02 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
- (BOOL)openWithDecoder:(id<CogDecoder>)d {
|
2022-06-17 01:16:09 +00:00
|
|
|
[self removeObservers];
|
|
|
|
|
2013-10-11 12:03:55 +00:00
|
|
|
DLog(@"Opening with old decoder: %@", d);
|
2007-10-11 02:08:29 +00:00
|
|
|
decoder = d;
|
2008-05-01 23:34:23 +00:00
|
|
|
|
|
|
|
NSDictionary *properties = [decoder properties];
|
|
|
|
int bitsPerSample = [[properties objectForKey:@"bitsPerSample"] intValue];
|
|
|
|
int channels = [[properties objectForKey:@"channels"] intValue];
|
2022-02-07 05:49:27 +00:00
|
|
|
|
2021-12-11 08:22:19 +00:00
|
|
|
bytesPerFrame = ((bitsPerSample + 7) / 8) * channels;
|
2022-02-07 05:49:27 +00:00
|
|
|
|
|
|
|
nodeFormat = propertiesToASBD(properties);
|
2022-02-07 08:56:05 +00:00
|
|
|
if([properties valueForKey:@"channelConfig"])
|
2022-02-08 03:18:45 +00:00
|
|
|
nodeChannelConfig = [[properties valueForKey:@"channelConfig"] unsignedIntValue];
|
2022-02-07 05:49:27 +00:00
|
|
|
nodeLossless = [[properties valueForKey:@"encoding"] isEqualToString:@"lossless"];
|
2022-02-06 11:08:34 +00:00
|
|
|
|
2008-05-01 23:34:23 +00:00
|
|
|
[self registerObservers];
|
|
|
|
|
2007-10-11 02:08:29 +00:00
|
|
|
shouldContinue = YES;
|
|
|
|
shouldSeek = NO;
|
2022-02-07 05:49:27 +00:00
|
|
|
|
2013-10-11 12:03:55 +00:00
|
|
|
DLog(@"DONES: %@", decoder);
|
2007-10-11 02:08:29 +00:00
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
- (void)registerObservers {
|
2022-06-15 23:47:43 +00:00
|
|
|
if(!observersAdded) {
|
|
|
|
DLog(@"REGISTERING OBSERVERS");
|
|
|
|
[decoder addObserver:self
|
|
|
|
forKeyPath:@"properties"
|
|
|
|
options:(NSKeyValueObservingOptionNew)
|
|
|
|
context:kInputNodeContext];
|
|
|
|
|
|
|
|
[decoder addObserver:self
|
|
|
|
forKeyPath:@"metadata"
|
|
|
|
options:(NSKeyValueObservingOptionNew)
|
|
|
|
context:kInputNodeContext];
|
|
|
|
|
|
|
|
observersAdded = YES;
|
|
|
|
}
|
2007-03-03 17:19:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)observeValueForKeyPath:(NSString *)keyPath
|
2022-02-07 05:49:27 +00:00
|
|
|
ofObject:(id)object
|
2007-03-03 17:19:37 +00:00
|
|
|
change:(NSDictionary *)change
|
2022-02-07 05:49:27 +00:00
|
|
|
context:(void *)context {
|
2022-06-15 23:47:43 +00:00
|
|
|
if(context == kInputNodeContext) {
|
|
|
|
DLog(@"SOMETHING CHANGED!");
|
|
|
|
if([keyPath isEqual:@"properties"]) {
|
|
|
|
DLog(@"Input format changed");
|
|
|
|
// Converter may need resetting, it'll do that when it reaches the new chunks
|
|
|
|
NSDictionary *properties = [decoder properties];
|
|
|
|
|
|
|
|
int bitsPerSample = [[properties objectForKey:@"bitsPerSample"] intValue];
|
|
|
|
int channels = [[properties objectForKey:@"channels"] intValue];
|
|
|
|
|
|
|
|
bytesPerFrame = ((bitsPerSample + 7) / 8) * channels;
|
|
|
|
|
|
|
|
nodeFormat = propertiesToASBD(properties);
|
|
|
|
nodeChannelConfig = [[properties valueForKey:@"channelConfig"] unsignedIntValue];
|
|
|
|
nodeLossless = [[properties valueForKey:@"encoding"] isEqualToString:@"lossless"];
|
|
|
|
} else if([keyPath isEqual:@"metadata"]) {
|
|
|
|
// Inform something of metadata change
|
|
|
|
NSDictionary *entryProperties = [decoder properties];
|
|
|
|
if(entryProperties == nil)
|
|
|
|
return;
|
|
|
|
|
|
|
|
NSDictionary *entryInfo = [NSDictionary dictionaryByMerging:entryProperties with:[decoder metadata]];
|
|
|
|
|
|
|
|
[controller pushInfo:entryInfo];
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
|
2007-03-03 17:19:37 +00:00
|
|
|
}
|
2013-10-05 21:15:09 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
- (void)process {
|
2007-11-24 20:16:27 +00:00
|
|
|
int amountInBuffer = 0;
|
2022-02-08 03:18:45 +00:00
|
|
|
int bytesInBuffer = 0;
|
2022-02-11 21:50:26 +00:00
|
|
|
void *inputBuffer = malloc(CHUNK_SIZE * 8 * 18); // Maximum 18 channels, dunno what we'll receive
|
2022-02-07 05:49:27 +00:00
|
|
|
|
2007-10-11 02:08:29 +00:00
|
|
|
BOOL shouldClose = YES;
|
2022-02-07 05:49:27 +00:00
|
|
|
BOOL seekError = NO;
|
|
|
|
|
2022-02-10 10:15:48 +00:00
|
|
|
BOOL isError = NO;
|
|
|
|
|
|
|
|
if([decoder respondsToSelector:@selector(isSilence)]) {
|
|
|
|
if([decoder isSilence]) {
|
|
|
|
isError = YES;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[controller setError:isError];
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
while([self shouldContinue] == YES && [self endOfStream] == NO) {
|
|
|
|
if(shouldSeek == YES) {
|
2022-02-07 08:56:05 +00:00
|
|
|
BufferChain *bufferChain = [[controller controller] bufferChain];
|
|
|
|
ConverterNode *converter = [bufferChain converter];
|
2021-12-26 07:41:45 +00:00
|
|
|
DLog(@"SEEKING! Resetting Buffer");
|
2022-02-07 05:49:27 +00:00
|
|
|
|
|
|
|
amountInBuffer = 0;
|
|
|
|
// This resets the converter's buffer
|
|
|
|
[self resetBuffer];
|
|
|
|
[converter resetBuffer];
|
2022-02-07 08:56:05 +00:00
|
|
|
[converter inputFormatDidChange:[bufferChain inputFormat] inputConfig:[bufferChain inputConfig]];
|
2022-02-07 05:49:27 +00:00
|
|
|
|
|
|
|
DLog(@"Reset buffer!");
|
|
|
|
|
|
|
|
DLog(@"SEEKING!");
|
|
|
|
seekError = [decoder seek:seekFrame] < 0;
|
|
|
|
|
2006-04-02 20:03:12 +00:00
|
|
|
shouldSeek = NO;
|
2013-10-11 12:03:55 +00:00
|
|
|
DLog(@"Seeked! Resetting Buffer");
|
2007-05-26 22:13:11 +00:00
|
|
|
initialBufferFilled = NO;
|
2022-02-10 10:15:48 +00:00
|
|
|
|
|
|
|
if(seekError) {
|
|
|
|
[controller setError:YES];
|
|
|
|
}
|
2006-04-02 20:03:12 +00:00
|
|
|
}
|
2007-05-16 01:06:23 +00:00
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
if(amountInBuffer < CHUNK_SIZE) {
|
2022-02-08 03:18:45 +00:00
|
|
|
int framesToRead = CHUNK_SIZE - amountInBuffer;
|
2022-02-09 21:44:50 +00:00
|
|
|
int framesRead;
|
|
|
|
@autoreleasepool {
|
|
|
|
framesRead = [decoder readAudio:((char *)inputBuffer) + bytesInBuffer frames:framesToRead];
|
|
|
|
}
|
2009-03-07 05:58:50 +00:00
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
if(framesRead > 0 && !seekError) {
|
2022-02-08 03:18:45 +00:00
|
|
|
amountInBuffer += framesRead;
|
|
|
|
bytesInBuffer += framesRead * bytesPerFrame;
|
|
|
|
[self writeData:inputBuffer amount:bytesInBuffer];
|
2022-02-07 05:49:27 +00:00
|
|
|
amountInBuffer = 0;
|
2022-02-08 03:18:45 +00:00
|
|
|
bytesInBuffer = 0;
|
2022-02-07 05:49:27 +00:00
|
|
|
} else {
|
2013-10-11 12:03:55 +00:00
|
|
|
DLog(@"End of stream? %@", [self properties]);
|
2013-10-12 20:52:58 +00:00
|
|
|
|
2007-10-03 20:23:14 +00:00
|
|
|
endOfStream = YES;
|
2022-02-07 05:49:27 +00:00
|
|
|
shouldClose = [controller endOfInputReached]; // Lets us know if we should keep going or not (occassionally, for track changes within a file)
|
2013-10-11 12:03:55 +00:00
|
|
|
DLog(@"closing? is %i", shouldClose);
|
2013-10-12 20:52:58 +00:00
|
|
|
|
2022-03-05 23:53:47 +00:00
|
|
|
// Move this here, so that the above endOfInputReached has a chance to queue another track before starting output
|
|
|
|
// Technically, the output should still play out its buffer first before checking if it should stop
|
|
|
|
if(initialBufferFilled == NO) {
|
|
|
|
[controller initialBufferFilled:self];
|
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
// wait before exiting, as we might still get seeking request
|
|
|
|
DLog("InputNode: Before wait")
|
|
|
|
[exitAtTheEndOfTheStream waitIndefinitely];
|
2022-06-25 12:10:33 +00:00
|
|
|
DLog("InputNode: After wait, should seek = %d", shouldSeek);
|
|
|
|
if(shouldSeek) {
|
2022-02-07 05:49:27 +00:00
|
|
|
endOfStream = NO;
|
|
|
|
shouldClose = NO;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
break;
|
|
|
|
}
|
2007-05-16 23:07:00 +00:00
|
|
|
}
|
2022-02-07 05:49:27 +00:00
|
|
|
}
|
2006-01-20 15:34:02 +00:00
|
|
|
}
|
2013-10-12 20:52:58 +00:00
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
if(shouldClose)
|
2007-10-11 02:08:29 +00:00
|
|
|
[decoder close];
|
2013-10-12 20:52:58 +00:00
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
free(inputBuffer);
|
2013-10-12 20:52:58 +00:00
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
[exitAtTheEndOfTheStream signal];
|
2013-10-12 20:59:34 +00:00
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
DLog("Input node thread stopping");
|
2006-01-20 15:34:02 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
- (void)seek:(long)frame {
|
2007-11-24 20:16:27 +00:00
|
|
|
seekFrame = frame;
|
2006-04-02 20:03:12 +00:00
|
|
|
shouldSeek = YES;
|
2013-10-11 12:03:55 +00:00
|
|
|
DLog(@"Should seek!");
|
2007-05-26 22:13:11 +00:00
|
|
|
[semaphore signal];
|
2013-10-12 20:52:58 +00:00
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
if(endOfStream) {
|
|
|
|
[exitAtTheEndOfTheStream signal];
|
|
|
|
}
|
2006-04-02 20:03:12 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
- (BOOL)setTrack:(NSURL *)track {
|
|
|
|
if([decoder respondsToSelector:@selector(setTrack:)] && [decoder setTrack:track]) {
|
2013-10-11 12:03:55 +00:00
|
|
|
DLog(@"SET TRACK!");
|
2022-02-07 05:49:27 +00:00
|
|
|
|
2007-10-11 02:08:29 +00:00
|
|
|
return YES;
|
|
|
|
}
|
2022-02-07 05:49:27 +00:00
|
|
|
|
2007-10-11 02:08:29 +00:00
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
- (void)removeObservers {
|
|
|
|
if(observersAdded) {
|
2022-06-15 23:47:43 +00:00
|
|
|
[decoder removeObserver:self forKeyPath:@"properties" context:kInputNodeContext];
|
|
|
|
[decoder removeObserver:self forKeyPath:@"metadata" context:kInputNodeContext];
|
2022-02-07 05:49:27 +00:00
|
|
|
observersAdded = NO;
|
|
|
|
}
|
2022-01-30 05:10:59 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
- (void)setShouldContinue:(BOOL)s {
|
|
|
|
[super setShouldContinue:s];
|
|
|
|
if(!s)
|
|
|
|
[self removeObservers];
|
2021-12-26 10:01:02 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
- (void)dealloc {
|
2013-10-11 12:03:55 +00:00
|
|
|
DLog(@"Input Node dealloc");
|
2022-02-07 05:49:27 +00:00
|
|
|
[self removeObservers];
|
2007-02-24 20:36:27 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
- (NSDictionary *)properties {
|
2007-02-24 20:36:27 +00:00
|
|
|
return [decoder properties];
|
2006-01-20 15:34:02 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
- (id<CogDecoder>)decoder {
|
2007-10-11 02:08:29 +00:00
|
|
|
return decoder;
|
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
- (double)secondsBuffered {
|
|
|
|
return [buffer listDuration];
|
2022-01-14 07:05:32 +00:00
|
|
|
}
|
|
|
|
|
2006-01-20 15:34:02 +00:00
|
|
|
@end
|