diff --git a/Audio/Chain/BufferChain.h b/Audio/Chain/BufferChain.h index 1646e573e..50ec43847 100644 --- a/Audio/Chain/BufferChain.h +++ b/Audio/Chain/BufferChain.h @@ -15,6 +15,7 @@ #import "DSPHRTFNode.h" #import "DSPEqualizerNode.h" #import "VisualizationNode.h" +#import "DSPDownmixNode.h" #import "InputNode.h" @interface BufferChain : NSObject { @@ -24,6 +25,7 @@ DSPFSurroundNode *fsurroundNode; DSPHRTFNode *hrtfNode; DSPEqualizerNode *equalizerNode; + DSPDownmixNode *downmixNode; VisualizationNode *visualizationNode; NSURL *streamURL; @@ -89,6 +91,8 @@ - (DSPEqualizerNode *)equalizer; +- (DSPDownmixNode *)downmix; + - (VisualizationNode *)visualization; - (double)secondsBuffered; diff --git a/Audio/Chain/BufferChain.m b/Audio/Chain/BufferChain.m index 5eb8fb4ef..21ac5c1d0 100644 --- a/Audio/Chain/BufferChain.m +++ b/Audio/Chain/BufferChain.m @@ -11,6 +11,8 @@ #import "CoreAudioUtils.h" #import "OutputNode.h" +#import "AudioPlayer.h" + #import "Logging.h" @implementation BufferChain @@ -30,6 +32,7 @@ fsurroundNode = nil; equalizerNode = nil; hrtfNode = nil; + downmixNode = nil; visualizationNode = nil; } @@ -47,9 +50,10 @@ fsurroundNode = [[DSPFSurroundNode alloc] initWithController:self previous:rubberbandNode latency:0.03]; equalizerNode = [[DSPEqualizerNode alloc] initWithController:self previous:fsurroundNode latency:0.03]; hrtfNode = [[DSPHRTFNode alloc] initWithController:self previous:equalizerNode latency:0.03]; + downmixNode = [[DSPDownmixNode alloc] initWithController:self previous:hrtfNode latency:0.03]; // Approximately five frames - visualizationNode = [[VisualizationNode alloc] initWithController:self previous:hrtfNode latency:5.0 / 60.0]; + visualizationNode = [[VisualizationNode alloc] initWithController:self previous:downmixNode latency:5.0 / 60.0]; finalNode = visualizationNode; } @@ -86,6 +90,7 @@ if(![converterNode setupWithInputFormat:inputFormat withInputConfig:inputChannelConfig outputFormat:outputFormat isLossless:[[properties valueForKey:@"encoding"] isEqualToString:@"lossless"]]) return NO; + [self initDownmixer]; [self setRGInfo:rgi]; @@ -116,6 +121,7 @@ DLog(@"Input Properties: %@", properties); if(![converterNode setupWithInputFormat:inputFormat withInputConfig:inputChannelConfig outputFormat:outputFormat isLossless:[[properties objectForKey:@"encoding"] isEqualToString:@"lossless"]]) return NO; + [self initDownmixer]; [self setRGInfo:rgi]; @@ -149,12 +155,19 @@ if(![converterNode setupWithInputFormat:inputFormat withInputConfig:inputChannelConfig outputFormat:outputFormat isLossless:[[properties objectForKey:@"encoding"] isEqualToString:@"lossless"]]) return NO; + [self initDownmixer]; [self setRGInfo:rgi]; return YES; } +- (void)initDownmixer { + AudioPlayer * audioPlayer = controller; + OutputNode *outputNode = [audioPlayer output]; + [downmixNode setOutputFormat:[outputNode deviceFormat] withChannelConfig:[outputNode deviceChannelConfig]]; +} + - (void)launchThreads { DLog(@"Properties: %@", [inputNode properties]); @@ -164,6 +177,7 @@ [fsurroundNode launchThread]; [equalizerNode launchThread]; [hrtfNode launchThread]; + [downmixNode launchThread]; [visualizationNode launchThread]; } @@ -239,6 +253,7 @@ [fsurroundNode setShouldContinue:s]; [equalizerNode setShouldContinue:s]; [hrtfNode setShouldContinue:s]; + [downmixNode setShouldContinue:s]; [visualizationNode setShouldContinue:s]; } @@ -274,6 +289,10 @@ return equalizerNode; } +- (DSPDownmixNode *)downmix { + return downmixNode; +} + - (VisualizationNode *)visualization { return visualizationNode; } diff --git a/Audio/Chain/DSP/DSPDownmixNode.h b/Audio/Chain/DSP/DSPDownmixNode.h new file mode 100644 index 000000000..dc7915736 --- /dev/null +++ b/Audio/Chain/DSP/DSPDownmixNode.h @@ -0,0 +1,32 @@ +// +// DSPDownmixNode.h +// CogAudio +// +// Created by Christopher Snowhill on 2/13/25. +// + +#ifndef DSPDownmixNode_h +#define DSPDownmixNode_h + +#import + +#import "DSPNode.h" + +@interface DSPDownmixNode : DSPNode { +} + +- (id _Nullable)initWithController:(id _Nonnull)c previous:(id _Nullable)p latency:(double)latency; + +- (BOOL)setup; +- (void)cleanUp; + +- (void)resetBuffer; + +- (void)process; +- (AudioChunk * _Nullable)convert; + +- (void)setOutputFormat:(AudioStreamBasicDescription)format withChannelConfig:(uint32_t)config; + +@end + +#endif /* DSPDownmixNode_h */ diff --git a/Audio/Chain/DSP/DSPDownmixNode.m b/Audio/Chain/DSP/DSPDownmixNode.m new file mode 100644 index 000000000..f6e8bfb87 --- /dev/null +++ b/Audio/Chain/DSP/DSPDownmixNode.m @@ -0,0 +1,184 @@ +// +// DSPDownmixNode.m +// CogAudio Framework +// +// Created by Christopher Snowhill on 2/13/25. +// + +#import + +#import "Downmix.h" + +#import "Logging.h" + +#import "DSPDownmixNode.h" + +@implementation DSPDownmixNode { + DownmixProcessor *downmix; + + BOOL stopping, paused; + BOOL processEntered; + BOOL formatSet; + + AudioStreamBasicDescription lastInputFormat; + AudioStreamBasicDescription inputFormat; + AudioStreamBasicDescription outputFormat; + + uint32_t lastInputChannelConfig, inputChannelConfig; + uint32_t outputChannelConfig; + + float outBuffer[4096 * 32]; +} + +- (id _Nullable)initWithController:(id _Nonnull)c previous:(id _Nullable)p latency:(double)latency { + self = [super initWithController:c previous:p latency:latency]; + return self; +} + +- (void)dealloc { + DLog(@"Downmix dealloc"); + [self cleanUp]; +} + +- (BOOL)fullInit { + if(formatSet) { + downmix = [[DownmixProcessor alloc] initWithInputFormat:inputFormat inputConfig:inputChannelConfig andOutputFormat:outputFormat outputConfig:outputChannelConfig]; + if(!downmix) { + return NO; + } + } + return YES; +} + +- (void)fullShutdown { + downmix = nil; +} + +- (BOOL)setup { + if(stopping) + return NO; + [self fullShutdown]; + return [self fullInit]; +} + +- (void)cleanUp { + stopping = YES; + while(processEntered) { + usleep(500); + } + [self fullShutdown]; + formatSet = NO; +} + +- (void)resetBuffer { + paused = YES; + while(processEntered) { + usleep(500); + } + [super resetBuffer]; + [self fullShutdown]; + paused = NO; +} + +- (void)setOutputFormat:(AudioStreamBasicDescription)format withChannelConfig:(uint32_t)config { + outputFormat = format; + outputChannelConfig = config; + formatSet = YES; +} + +- (void)process { + while([self shouldContinue] == YES) { + if(paused) { + usleep(500); + continue; + } + @autoreleasepool { + AudioChunk *chunk = nil; + chunk = [self convert]; + if(!chunk || ![chunk frameCount]) { + if([self endOfStream] == YES) { + break; + } + if(paused) { + continue; + } + usleep(500); + } else { + [self writeChunk:chunk]; + chunk = nil; + } + } + } +} + +- (AudioChunk *)convert { + if(stopping) + return nil; + + processEntered = YES; + + if(stopping || [self endOfStream] == YES || [self shouldContinue] == NO) { + processEntered = NO; + return nil; + } + + if(![self peekFormat:&inputFormat channelConfig:&inputChannelConfig]) { + processEntered = NO; + return nil; + } + + if(!inputFormat.mSampleRate || + !inputFormat.mBitsPerChannel || + !inputFormat.mChannelsPerFrame || + !inputFormat.mBytesPerFrame || + !inputFormat.mFramesPerPacket || + !inputFormat.mBytesPerPacket) { + processEntered = NO; + return nil; + } + + if((formatSet && !downmix) || + memcmp(&inputFormat, &lastInputFormat, sizeof(inputFormat)) != 0 || + inputChannelConfig != lastInputChannelConfig) { + lastInputFormat = inputFormat; + lastInputChannelConfig = inputChannelConfig; + [self fullShutdown]; + if(formatSet && ![self setup]) { + processEntered = NO; + return nil; + } + } + + if(!downmix) { + processEntered = NO; + return [self readChunk:4096]; + } + + AudioChunk *chunk = [self readChunkAsFloat32:4096]; + if(!chunk || ![chunk frameCount]) { + processEntered = NO; + return nil; + } + + double streamTimestamp = [chunk streamTimestamp]; + + size_t frameCount = [chunk frameCount]; + NSData *sampleData = [chunk removeSamples:frameCount]; + + [downmix process:[sampleData bytes] frameCount:frameCount output:&outBuffer[0]]; + + AudioChunk *outputChunk = [[AudioChunk alloc] init]; + [outputChunk setFormat:outputFormat]; + if(outputChannelConfig) { + [outputChunk setChannelConfig:outputChannelConfig]; + } + if([chunk isHDCD]) [outputChunk setHDCD]; + [outputChunk setStreamTimestamp:streamTimestamp]; + [outputChunk setStreamTimeRatio:[chunk streamTimeRatio]]; + [outputChunk assignSamples:&outBuffer[0] frameCount:frameCount]; + + processEntered = NO; + return outputChunk; +} + +@end diff --git a/Audio/Chain/Downmix.h b/Audio/Chain/DSP/Downmix.h similarity index 100% rename from Audio/Chain/Downmix.h rename to Audio/Chain/DSP/Downmix.h diff --git a/Audio/Chain/Downmix.m b/Audio/Chain/DSP/Downmix.m similarity index 97% rename from Audio/Chain/Downmix.m rename to Audio/Chain/DSP/Downmix.m index a8291640e..4d91f108f 100644 --- a/Audio/Chain/Downmix.m +++ b/Audio/Chain/DSP/Downmix.m @@ -283,7 +283,11 @@ static void *kDownmixProcessorContext = &kDownmixProcessorContext; } - (void)process:(const void *)inBuffer frameCount:(size_t)frames output:(void *)outBuffer { - if(inputFormat.mChannelsPerFrame > 2 && outConfig == AudioConfigStereo) { + if(inputFormat.mChannelsPerFrame == 2 && outConfig == AudioConfigStereo && + inConfig == (AudioChannelSideLeft | AudioChannelSideRight)) { + // Workaround for HRTF output + memcpy(outBuffer, inBuffer, frames * outputFormat.mBytesPerPacket); + } else if(inputFormat.mChannelsPerFrame > 2 && outConfig == AudioConfigStereo) { downmix_to_stereo((const float *)inBuffer, inputFormat.mChannelsPerFrame, inConfig, (float *)outBuffer, frames); } else if(inputFormat.mChannelsPerFrame > 1 && outConfig == AudioConfigMono) { downmix_to_mono((const float *)inBuffer, inputFormat.mChannelsPerFrame, inConfig, (float *)outBuffer, frames); diff --git a/Audio/Chain/InputNode.m b/Audio/Chain/InputNode.m index e5e4983aa..fecb8b160 100644 --- a/Audio/Chain/InputNode.m +++ b/Audio/Chain/InputNode.m @@ -166,6 +166,7 @@ static void *kInputNodeContext = &kInputNodeContext; DSPFSurroundNode *fsurround = [bufferChain fsurround]; DSPEqualizerNode *equalizer = [bufferChain equalizer]; DSPHRTFNode *hrtf = [bufferChain hrtf]; + DSPDownmixNode *downmix = [bufferChain downmix]; DLog(@"SEEKING! Resetting Buffer"); // This resets the converter's buffer @@ -177,6 +178,7 @@ static void *kInputNodeContext = &kInputNodeContext; [fsurround resetBuffer]; [equalizer resetBuffer]; [hrtf resetBuffer]; + [downmix resetBuffer]; DLog(@"Reset buffer!"); diff --git a/Audio/Chain/OutputNode.h b/Audio/Chain/OutputNode.h index 3969d17a2..072af0c59 100644 --- a/Audio/Chain/OutputNode.h +++ b/Audio/Chain/OutputNode.h @@ -56,6 +56,9 @@ - (AudioStreamBasicDescription)format; - (uint32_t)config; +- (AudioStreamBasicDescription)deviceFormat; +- (uint32_t)deviceChannelConfig; + - (void)setVolume:(double)v; - (void)setShouldContinue:(BOOL)s; diff --git a/Audio/Chain/OutputNode.m b/Audio/Chain/OutputNode.m index 52835421f..c690840c2 100644 --- a/Audio/Chain/OutputNode.m +++ b/Audio/Chain/OutputNode.m @@ -136,6 +136,14 @@ return config; } +- (AudioStreamBasicDescription)deviceFormat { + return [output deviceFormat]; +} + +- (uint32_t)deviceChannelConfig { + return [output deviceChannelConfig]; +} + - (void)setFormat:(AudioStreamBasicDescription *)f channelConfig:(uint32_t)channelConfig { format = *f; config = channelConfig; @@ -161,6 +169,10 @@ [converter setOutputFormat:format]; [converter inputFormatDidChange:[bufferChain inputFormat] inputConfig:[bufferChain inputConfig]]; } + DSPDownmixNode *downmix = [bufferChain downmix]; + if(downmix && output) { + [downmix setOutputFormat:[output deviceFormat] withChannelConfig:[output deviceChannelConfig]]; + } } } diff --git a/Audio/CogAudio.xcodeproj/project.pbxproj b/Audio/CogAudio.xcodeproj/project.pbxproj index 2732f671b..84cd0fb0f 100644 --- a/Audio/CogAudio.xcodeproj/project.pbxproj +++ b/Audio/CogAudio.xcodeproj/project.pbxproj @@ -47,6 +47,10 @@ 8328995727CB51B700D7F028 /* SHA256Digest.h in Headers */ = {isa = PBXBuildFile; fileRef = 8328995527CB51B700D7F028 /* SHA256Digest.h */; }; 8328995827CB51B700D7F028 /* SHA256Digest.m in Sources */ = {isa = PBXBuildFile; fileRef = 8328995627CB51B700D7F028 /* SHA256Digest.m */; }; 8328995A27CB51C900D7F028 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8328995927CB51C900D7F028 /* Security.framework */; }; + 833738EA2D5EA52500278628 /* DSPDownmixNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 833738E92D5EA52500278628 /* DSPDownmixNode.h */; }; + 833738EC2D5EA53500278628 /* DSPDownmixNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 833738EB2D5EA53500278628 /* DSPDownmixNode.m */; }; + 833738EF2D5EA5B700278628 /* Downmix.m in Sources */ = {isa = PBXBuildFile; fileRef = 833738EE2D5EA5B700278628 /* Downmix.m */; }; + 833738F02D5EA5B700278628 /* Downmix.h in Headers */ = {isa = PBXBuildFile; fileRef = 833738ED2D5EA5B700278628 /* Downmix.h */; }; 8347C7412796C58800FA8A7D /* NSFileHandle+CreateFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 8347C73F2796C58800FA8A7D /* NSFileHandle+CreateFile.h */; }; 8347C7422796C58800FA8A7D /* NSFileHandle+CreateFile.m in Sources */ = {isa = PBXBuildFile; fileRef = 8347C7402796C58800FA8A7D /* NSFileHandle+CreateFile.m */; }; 834A41A9287A90AB00EB9D9B /* freesurround_decoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 834A41A5287A90AB00EB9D9B /* freesurround_decoder.h */; }; @@ -57,8 +61,6 @@ 834FD4ED27AF91220063BC83 /* AudioChunk.m in Sources */ = {isa = PBXBuildFile; fileRef = 834FD4EC27AF91220063BC83 /* AudioChunk.m */; }; 834FD4F027AF93680063BC83 /* ChunkList.h in Headers */ = {isa = PBXBuildFile; fileRef = 834FD4EE27AF93680063BC83 /* ChunkList.h */; }; 834FD4F127AF93680063BC83 /* ChunkList.m in Sources */ = {isa = PBXBuildFile; fileRef = 834FD4EF27AF93680063BC83 /* ChunkList.m */; }; - 83504165286447DA006B32CC /* Downmix.h in Headers */ = {isa = PBXBuildFile; fileRef = 83504163286447DA006B32CC /* Downmix.h */; }; - 83504166286447DA006B32CC /* Downmix.m in Sources */ = {isa = PBXBuildFile; fileRef = 83504164286447DA006B32CC /* Downmix.m */; }; 8350416D28646149006B32CC /* CoreMedia.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8350416C28646149006B32CC /* CoreMedia.framework */; }; 835C88B1279811A500E28EAE /* hdcd_decode2.h in Headers */ = {isa = PBXBuildFile; fileRef = 835C88AF279811A500E28EAE /* hdcd_decode2.h */; }; 835C88B2279811A500E28EAE /* hdcd_decode2.c in Sources */ = {isa = PBXBuildFile; fileRef = 835C88B0279811A500E28EAE /* hdcd_decode2.c */; }; @@ -173,6 +175,10 @@ 8328995527CB51B700D7F028 /* SHA256Digest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SHA256Digest.h; path = ../../Utils/SHA256Digest.h; sourceTree = ""; }; 8328995627CB51B700D7F028 /* SHA256Digest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SHA256Digest.m; path = ../../Utils/SHA256Digest.m; sourceTree = ""; }; 8328995927CB51C900D7F028 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; + 833738E92D5EA52500278628 /* DSPDownmixNode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DSPDownmixNode.h; sourceTree = ""; }; + 833738EB2D5EA53500278628 /* DSPDownmixNode.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DSPDownmixNode.m; sourceTree = ""; }; + 833738ED2D5EA5B700278628 /* Downmix.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Downmix.h; sourceTree = ""; }; + 833738EE2D5EA5B700278628 /* Downmix.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Downmix.m; sourceTree = ""; }; 8347C73F2796C58800FA8A7D /* NSFileHandle+CreateFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSFileHandle+CreateFile.h"; path = "../../Utils/NSFileHandle+CreateFile.h"; sourceTree = ""; }; 8347C7402796C58800FA8A7D /* NSFileHandle+CreateFile.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "NSFileHandle+CreateFile.m"; path = "../../Utils/NSFileHandle+CreateFile.m"; sourceTree = ""; }; 834A41A5287A90AB00EB9D9B /* freesurround_decoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = freesurround_decoder.h; sourceTree = ""; }; @@ -183,8 +189,6 @@ 834FD4EC27AF91220063BC83 /* AudioChunk.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AudioChunk.m; sourceTree = ""; }; 834FD4EE27AF93680063BC83 /* ChunkList.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ChunkList.h; sourceTree = ""; }; 834FD4EF27AF93680063BC83 /* ChunkList.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ChunkList.m; sourceTree = ""; }; - 83504163286447DA006B32CC /* Downmix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Downmix.h; sourceTree = ""; }; - 83504164286447DA006B32CC /* Downmix.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Downmix.m; sourceTree = ""; }; 8350416C28646149006B32CC /* CoreMedia.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMedia.framework; path = System/Library/Frameworks/CoreMedia.framework; sourceTree = SDKROOT; }; 835C88AF279811A500E28EAE /* hdcd_decode2.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = hdcd_decode2.h; sourceTree = ""; }; 835C88B0279811A500E28EAE /* hdcd_decode2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = hdcd_decode2.c; sourceTree = ""; }; @@ -378,8 +382,6 @@ 834FD4EF27AF93680063BC83 /* ChunkList.m */, 8EC1225D0B993BD500C5B3AD /* ConverterNode.h */, 8EC1225E0B993BD500C5B3AD /* ConverterNode.m */, - 83504163286447DA006B32CC /* Downmix.h */, - 83504164286447DA006B32CC /* Downmix.m */, 17D21C7A0B8BE4BA00D1EBDE /* InputNode.h */, 17D21C7B0B8BE4BA00D1EBDE /* InputNode.m */, 17D21C7C0B8BE4BA00D1EBDE /* Node.h */, @@ -538,6 +540,8 @@ 83A349692D5C3F430096D530 /* DSP */ = { isa = PBXGroup; children = ( + 833738ED2D5EA5B700278628 /* Downmix.h */, + 833738EE2D5EA5B700278628 /* Downmix.m */, 83F8431E2D5C6272008C123B /* HeadphoneFilter.h */, 83F8431F2D5C6272008C123B /* HeadphoneFilter.mm */, 83A349702D5C41810096D530 /* FSurroundFilter.h */, @@ -550,6 +554,8 @@ 83A349762D5C50B20096D530 /* DSPHRTFNode.m */, 83F843222D5C66DA008C123B /* DSPEqualizerNode.h */, 83F843242D5C66E9008C123B /* DSPEqualizerNode.m */, + 833738E92D5EA52500278628 /* DSPDownmixNode.h */, + 833738EB2D5EA53500278628 /* DSPDownmixNode.m */, ); path = DSP; sourceTree = ""; @@ -575,9 +581,9 @@ 8399CF2C27B5D1D5008751F1 /* NSDictionary+Merge.h in Headers */, 17D21CA90B8BE4BA00D1EBDE /* OutputNode.h in Headers */, 8328995427CB511000D7F028 /* RedundantPlaylistDataStore.h in Headers */, - 83504165286447DA006B32CC /* Downmix.h in Headers */, 839E56E52879450300DFB5F4 /* HrtfData.h in Headers */, 83B74281289E027F005AAC28 /* CogAudio-Bridging-Header.h in Headers */, + 833738F02D5EA5B700278628 /* Downmix.h in Headers */, 17D21CC70B8BE4BA00D1EBDE /* Status.h in Headers */, 17D21CF30B8BE5EF00D1EBDE /* CogSemaphore.h in Headers */, 835DD2682ACAF1D90057E319 /* OutputCoreAudio.h in Headers */, @@ -611,6 +617,7 @@ 835FAC5E27BCA14D00BA8562 /* BadSampleCleaner.h in Headers */, 8E8D3D2F0CBAEE6E00135C1B /* AudioContainer.h in Headers */, B0575F2D0D687A0800411D77 /* Helper.h in Headers */, + 833738EA2D5EA52500278628 /* DSPDownmixNode.h in Headers */, 07DB5F3E0ED353A900C2E3EF /* AudioMetadataWriter.h in Headers */, 839E56EA28794F6300DFB5F4 /* HrtfTypes.h in Headers */, ); @@ -692,16 +699,17 @@ 83A349772D5C50B20096D530 /* DSPHRTFNode.m in Sources */, 17D21CA60B8BE4BA00D1EBDE /* InputNode.m in Sources */, 83A3496A2D5C3F430096D530 /* DSPRubberbandNode.m in Sources */, - 83504166286447DA006B32CC /* Downmix.m in Sources */, 8399CF2D27B5D1D5008751F1 /* NSDictionary+Merge.m in Sources */, 83F843252D5C66E9008C123B /* DSPEqualizerNode.m in Sources */, 834A41AB287A90AB00EB9D9B /* channelmaps.cpp in Sources */, + 833738EC2D5EA53500278628 /* DSPDownmixNode.m in Sources */, 831A50162865A8800049CFE4 /* rsstate.cpp in Sources */, 17D21CA80B8BE4BA00D1EBDE /* Node.m in Sources */, 17D21CAA0B8BE4BA00D1EBDE /* OutputNode.m in Sources */, 835C88B2279811A500E28EAE /* hdcd_decode2.c in Sources */, 835FAC5F27BCA14D00BA8562 /* BadSampleCleaner.m in Sources */, 834FD4ED27AF91220063BC83 /* AudioChunk.m in Sources */, + 833738EF2D5EA5B700278628 /* Downmix.m in Sources */, 17D21CF40B8BE5EF00D1EBDE /* CogSemaphore.m in Sources */, 839B83FA286D91ED00F529EE /* VisualizationController.swift in Sources */, 839E89A02D5DBA1700A13526 /* VisualizationNode.m in Sources */, diff --git a/Audio/Output/OutputCoreAudio.h b/Audio/Output/OutputCoreAudio.h index 1cc728e67..c65592299 100644 --- a/Audio/Output/OutputCoreAudio.h +++ b/Audio/Output/OutputCoreAudio.h @@ -22,8 +22,6 @@ using std::atomic_long; #import #endif -#import "Downmix.h" - #import #import @@ -93,8 +91,6 @@ using std::atomic_long; size_t _bufferSize; - DownmixProcessor *downmixer; - VisualizationController *visController; int inputRemain; @@ -108,7 +104,6 @@ using std::atomic_long; float *samplePtr; float tempBuffer[512 * 32]; float inputBuffer[4096 * 32]; // 4096 samples times maximum supported channel count - float downmixBuffer[4096 * 8]; #ifdef OUTPUT_LOG FILE *_logFile; @@ -133,4 +128,7 @@ using std::atomic_long; - (void)sustainHDCD; +- (AudioStreamBasicDescription)deviceFormat; +- (uint32_t)deviceChannelConfig; + @end diff --git a/Audio/Output/OutputCoreAudio.m b/Audio/Output/OutputCoreAudio.m index ae860d977..38b01ab5a 100644 --- a/Audio/Output/OutputCoreAudio.m +++ b/Audio/Output/OutputCoreAudio.m @@ -41,16 +41,6 @@ static void *kOutputCoreAudioContext = &kOutputCoreAudioContext; uint32_t origConfig = config; origFormat = format; - UInt32 srcChannels = format.mChannelsPerFrame; - UInt32 dstChannels = deviceFormat.mChannelsPerFrame; - if(srcChannels != dstChannels) { - format.mChannelsPerFrame = dstChannels; - format.mBytesPerFrame = ((format.mBitsPerChannel + 7) / 8) * dstChannels; - format.mBytesPerPacket = format.mBytesPerFrame * format.mFramesPerPacket; - downmixer = [[DownmixProcessor alloc] initWithInputFormat:origFormat inputConfig:origConfig andOutputFormat:format outputConfig:deviceChannelConfig]; - } else { - downmixer = nil; - } if(!streamFormatStarted || config != realStreamChannelConfig || memcmp(&realStreamFormat, &format, sizeof(format)) != 0) { realStreamFormat = format; realStreamChannelConfig = config; @@ -598,18 +588,13 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons samplePtr = &inputBuffer[0]; - if(samplesRendered) { - if(downmixer) { - [downmixer process:samplePtr frameCount:samplesRendered output:&downmixBuffer[0]]; - samplePtr = &downmixBuffer[0]; - } - #ifdef OUTPUT_LOG + if(samplesRendered) { size_t dataByteSize = samplesRendered * sizeof(float) * deviceFormat.mChannelsPerFrame; fwrite(samplePtr, 1, dataByteSize, _logFile); -#endif } +#endif return samplesRendered; } @@ -687,8 +672,6 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons outputDeviceID = -1; restarted = NO; - downmixer = nil; - lastClippedSampleRate = 0.0; inputRemain = 0; @@ -856,4 +839,12 @@ current_device_listener(AudioObjectID inObjectID, UInt32 inNumberAddresses, cons shouldPlayOutBuffer = s; } +- (AudioStreamBasicDescription)deviceFormat { + return deviceFormat; +} + +- (uint32_t)deviceChannelConfig { + return deviceChannelConfig; +} + @end