Compare commits
5 commits
0494ef981c
...
5aec42d518
Author | SHA1 | Date | |
---|---|---|---|
|
5aec42d518 | ||
|
cec0049d93 | ||
|
cc00ab5125 | ||
|
648a98833e | ||
|
3f7ec658fe |
3 changed files with 74 additions and 36 deletions
|
@ -13,10 +13,8 @@
|
|||
|
||||
#import "DSPRubberbandNode.h"
|
||||
#import "DSPFSurroundNode.h"
|
||||
#import "DSPHRTFNode.h"
|
||||
#import "DSPEqualizerNode.h"
|
||||
#import "VisualizationNode.h"
|
||||
#import "DSPDownmixNode.h"
|
||||
|
||||
#import "Logging.h"
|
||||
|
||||
|
@ -27,9 +25,7 @@
|
|||
|
||||
DSPRubberbandNode *rubberbandNode;
|
||||
DSPFSurroundNode *fsurroundNode;
|
||||
DSPHRTFNode *hrtfNode;
|
||||
DSPEqualizerNode *equalizerNode;
|
||||
DSPDownmixNode *downmixNode;
|
||||
VisualizationNode *visualizationNode;
|
||||
}
|
||||
|
||||
|
@ -61,13 +57,9 @@
|
|||
if(!fsurroundNode) return NO;
|
||||
equalizerNode = [[DSPEqualizerNode alloc] initWithController:self previous:fsurroundNode latency:0.03];
|
||||
if(!equalizerNode) return NO;
|
||||
hrtfNode = [[DSPHRTFNode alloc] initWithController:self previous:equalizerNode latency:0.03];
|
||||
if(!hrtfNode) return NO;
|
||||
downmixNode = [[DSPDownmixNode alloc] initWithController:self previous:hrtfNode latency:0.03];
|
||||
if(!downmixNode) return NO;
|
||||
|
||||
// Approximately double the chunk size for Vis at 44100Hz
|
||||
visualizationNode = [[VisualizationNode alloc] initWithController:self previous:downmixNode latency:8192.0 / 44100.0];
|
||||
visualizationNode = [[VisualizationNode alloc] initWithController:self previous:equalizerNode latency:8192.0 / 44100.0];
|
||||
if(!visualizationNode) return NO;
|
||||
|
||||
[self setPreviousNode:visualizationNode];
|
||||
|
@ -171,7 +163,7 @@
|
|||
|
||||
- (NSArray *)DSPs {
|
||||
if(DSPsLaunched) {
|
||||
return @[rubberbandNode, fsurroundNode, equalizerNode, hrtfNode, downmixNode, visualizationNode];
|
||||
return @[rubberbandNode, fsurroundNode, equalizerNode, visualizationNode];
|
||||
} else {
|
||||
return @[];
|
||||
}
|
||||
|
@ -288,7 +280,11 @@
|
|||
formatChanged = YES;
|
||||
}
|
||||
}
|
||||
if(downmixNode && output && !formatChanged) {
|
||||
DSPDownmixNode *downmixNode = nil;
|
||||
if(output) {
|
||||
downmixNode = [output downmix];
|
||||
}
|
||||
if(downmixNode && !formatChanged) {
|
||||
outputFormat = [output deviceFormat];
|
||||
outputChannelConfig = [output deviceChannelConfig];
|
||||
AudioStreamBasicDescription currentOutputFormat = [downmixNode nodeFormat];
|
||||
|
@ -303,7 +299,7 @@
|
|||
if(converter) {
|
||||
[converter setOutputFormat:format];
|
||||
}
|
||||
if(downmixNode && output) {
|
||||
if(downmixNode) {
|
||||
[downmixNode setOutputFormat:[output deviceFormat] withChannelConfig:[output deviceChannelConfig]];
|
||||
}
|
||||
if(inputNode) {
|
||||
|
@ -327,8 +323,6 @@
|
|||
}
|
||||
previousNode = nil;
|
||||
visualizationNode = nil;
|
||||
downmixNode = nil;
|
||||
hrtfNode = nil;
|
||||
fsurroundNode = nil;
|
||||
rubberbandNode = nil;
|
||||
previousInput = nil;
|
||||
|
@ -393,7 +387,7 @@
|
|||
}
|
||||
|
||||
- (id)downmix {
|
||||
return downmixNode;
|
||||
return [output downmix];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -25,7 +25,11 @@ using std::atomic_long;
|
|||
#import <simd/simd.h>
|
||||
|
||||
#import <CogAudio/ChunkList.h>
|
||||
#import <CogAudio/HeadphoneFilter.h>
|
||||
|
||||
#import <CogAudio/Node.h>
|
||||
|
||||
#import <CogAudio/DSPDownmixNode.h>
|
||||
#import <CogAudio/DSPHRTFNode.h>
|
||||
|
||||
//#define OUTPUT_LOG
|
||||
|
||||
|
@ -33,12 +37,9 @@ using std::atomic_long;
|
|||
|
||||
@class AudioChunk;
|
||||
|
||||
@interface OutputCoreAudio : NSObject {
|
||||
@interface OutputCoreAudio : Node {
|
||||
OutputNode *outputController;
|
||||
|
||||
dispatch_semaphore_t writeSemaphore;
|
||||
dispatch_semaphore_t readSemaphore;
|
||||
|
||||
NSLock *outputLock;
|
||||
|
||||
double streamTimestamp;
|
||||
|
@ -96,7 +97,9 @@ using std::atomic_long;
|
|||
|
||||
BOOL shouldPlayOutBuffer;
|
||||
|
||||
ChunkList *outputBuffer;
|
||||
BOOL DSPsLaunched;
|
||||
DSPHRTFNode *hrtfNode;
|
||||
DSPDownmixNode *downmixNode;
|
||||
|
||||
#ifdef OUTPUT_LOG
|
||||
NSFileHandle *_logFile;
|
||||
|
@ -129,4 +132,6 @@ using std::atomic_long;
|
|||
- (AudioStreamBasicDescription)deviceFormat;
|
||||
- (uint32_t)deviceChannelConfig;
|
||||
|
||||
- (DSPDownmixNode *)downmix;
|
||||
|
||||
@end
|
||||
|
|
|
@ -133,6 +133,10 @@ static void *kOutputCoreAudioContext = &kOutputCoreAudioContext;
|
|||
- (id)initWithController:(OutputNode *)c {
|
||||
self = [super init];
|
||||
if(self) {
|
||||
buffer = [[ChunkList alloc] initWithMaximumDuration:0.5];
|
||||
writeSemaphore = [[Semaphore alloc] init];
|
||||
readSemaphore = [[Semaphore alloc] init];
|
||||
|
||||
outputController = c;
|
||||
volume = 1.0;
|
||||
outputDeviceID = -1;
|
||||
|
@ -208,6 +212,26 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
|
|||
return NO;
|
||||
}
|
||||
|
||||
- (NSArray *)DSPs {
|
||||
if(DSPsLaunched) {
|
||||
return @[hrtfNode, downmixNode];
|
||||
} else {
|
||||
return @[];
|
||||
}
|
||||
}
|
||||
|
||||
- (DSPDownmixNode *)downmix {
|
||||
return downmixNode;
|
||||
}
|
||||
|
||||
- (void)launchDSPs {
|
||||
NSArray *DSPs = [self DSPs];
|
||||
|
||||
for (Node *node in DSPs) {
|
||||
[node launchThread];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)threadEntry:(id)arg {
|
||||
@autoreleasepool {
|
||||
NSThread *currentThread = [NSThread currentThread];
|
||||
|
@ -236,14 +260,15 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
|
|||
[outputLock lock];
|
||||
started = NO;
|
||||
restarted = NO;
|
||||
[outputBuffer reset];
|
||||
[buffer reset];
|
||||
[self setShouldReset:YES];
|
||||
[outputLock unlock];
|
||||
}
|
||||
|
||||
if(stopping)
|
||||
break;
|
||||
|
||||
if(!cutOffInput && ![outputBuffer isFull]) {
|
||||
if(!cutOffInput && ![buffer isFull]) {
|
||||
[self renderAndConvert];
|
||||
rendered = YES;
|
||||
} else {
|
||||
|
@ -556,7 +581,8 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
|
|||
[outputController setFormat:&deviceFormat channelConfig:deviceChannelConfig];
|
||||
|
||||
[outputLock lock];
|
||||
[outputBuffer reset];
|
||||
[buffer reset];
|
||||
[self setShouldReset:YES];
|
||||
[outputLock unlock];
|
||||
|
||||
if(started) {
|
||||
|
@ -639,8 +665,9 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
|
|||
size_t frameCount = 0;
|
||||
if(chunk && (frameCount = [chunk frameCount])) {
|
||||
[outputLock lock];
|
||||
[outputBuffer addChunk:chunk];
|
||||
[buffer addChunk:chunk];
|
||||
[outputLock unlock];
|
||||
[readSemaphore signal];
|
||||
}
|
||||
|
||||
if(streamFormatChanged) {
|
||||
|
@ -691,8 +718,8 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
|
|||
while(renderedSamples < frameCount) {
|
||||
[refLock lock];
|
||||
AudioChunk *chunk = nil;
|
||||
if(_self->outputBuffer && ![_self->outputBuffer isEmpty]) {
|
||||
chunk = [_self->outputBuffer removeSamples:frameCount - renderedSamples];
|
||||
if(![_self->downmixNode.buffer isEmpty]) {
|
||||
chunk = [self->downmixNode.buffer removeSamples:frameCount - renderedSamples];
|
||||
}
|
||||
[refLock unlock];
|
||||
|
||||
|
@ -827,15 +854,19 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
|
|||
|
||||
visController = [VisualizationController sharedController];
|
||||
|
||||
hrtfNode = [[DSPHRTFNode alloc] initWithController:self previous:self latency:0.03];
|
||||
downmixNode = [[DSPDownmixNode alloc] initWithController:self previous:hrtfNode latency:0.03];
|
||||
|
||||
[self setShouldContinue:YES];
|
||||
[self setEndOfStream:NO];
|
||||
|
||||
DSPsLaunched = YES;
|
||||
[self launchDSPs];
|
||||
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.outputDevice" options:0 context:kOutputCoreAudioContext];
|
||||
|
||||
observersapplied = YES;
|
||||
|
||||
outputBuffer = [[ChunkList alloc] initWithMaximumDuration:0.5];
|
||||
if(!outputBuffer) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
return (err == nil);
|
||||
}
|
||||
}
|
||||
|
@ -857,7 +888,7 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
|
|||
}
|
||||
|
||||
- (double)latency {
|
||||
return [outputBuffer listDuration];
|
||||
return [buffer listDuration] + [[hrtfNode buffer] listDuration] + [[downmixNode buffer] listDuration];
|
||||
}
|
||||
|
||||
- (void)start {
|
||||
|
@ -932,6 +963,14 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
|
|||
usleep(5000);
|
||||
}
|
||||
}
|
||||
if(DSPsLaunched) {
|
||||
[self setShouldContinue:NO];
|
||||
[hrtfNode setShouldContinue:NO];
|
||||
[downmixNode setShouldContinue:NO];
|
||||
hrtfNode = nil;
|
||||
downmixNode = nil;
|
||||
DSPsLaunched = NO;
|
||||
}
|
||||
#ifdef OUTPUT_LOG
|
||||
if(_logFile) {
|
||||
[_logFile closeFile];
|
||||
|
@ -995,9 +1034,9 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons
|
|||
cutOffInput = YES;
|
||||
[outputLock lock];
|
||||
[fadedBuffersLock lock];
|
||||
FadedBuffer *buffer = [[FadedBuffer alloc] initWithBuffer:outputBuffer fadeTarget:0.0 sampleRate:deviceFormat.mSampleRate];
|
||||
outputBuffer = [[ChunkList alloc] initWithMaximumDuration:0.5];
|
||||
[fadedBuffers addObject:buffer];
|
||||
FadedBuffer *fbuffer = [[FadedBuffer alloc] initWithBuffer:buffer fadeTarget:0.0 sampleRate:deviceFormat.mSampleRate];
|
||||
buffer = [[ChunkList alloc] initWithMaximumDuration:0.5];
|
||||
[fadedBuffers addObject:fbuffer];
|
||||
[fadedBuffersLock unlock];
|
||||
[outputLock unlock];
|
||||
cutOffInput = NO;
|
||||
|
|
Loading…
Reference in a new issue