diff --git a/Audio/Chain/BufferChain.h b/Audio/Chain/BufferChain.h index 392c6f1a2..b99c26271 100644 --- a/Audio/Chain/BufferChain.h +++ b/Audio/Chain/BufferChain.h @@ -16,9 +16,6 @@ InputNode *inputNode; ConverterNode *converterNode; - AudioStreamBasicDescription inputFormat; - uint32_t inputChannelConfig; - NSURL *streamURL; id userInfo; NSDictionary *rgInfo; diff --git a/Audio/Chain/BufferChain.m b/Audio/Chain/BufferChain.m index 282f38ae4..e5125487d 100644 --- a/Audio/Chain/BufferChain.m +++ b/Audio/Chain/BufferChain.m @@ -60,7 +60,8 @@ NSDictionary *properties = [inputNode properties]; - inputFormat = [inputNode nodeFormat]; + AudioStreamBasicDescription inputFormat = [inputNode nodeFormat]; + uint32_t inputChannelConfig = 0; if([properties valueForKey:@"channelConfig"]) inputChannelConfig = [[properties valueForKey:@"channelConfig"] unsignedIntValue]; @@ -89,7 +90,8 @@ NSDictionary *properties = [inputNode properties]; - inputFormat = [inputNode nodeFormat]; + AudioStreamBasicDescription inputFormat = [inputNode nodeFormat]; + uint32_t inputChannelConfig = 0; if([properties valueForKey:@"channelConfig"]) inputChannelConfig = [[properties valueForKey:@"channelConfig"] unsignedIntValue]; @@ -123,7 +125,8 @@ DLog(@"Input Properties: %@", properties); - inputFormat = [inputNode nodeFormat]; + AudioStreamBasicDescription inputFormat = [inputNode nodeFormat]; + uint32_t inputChannelConfig = 0; if([properties valueForKey:@"channelConfig"]) inputChannelConfig = [[properties valueForKey:@"channelConfig"] unsignedIntValue]; @@ -231,11 +234,11 @@ } - (AudioStreamBasicDescription)inputFormat { - return inputFormat; + return [inputNode nodeFormat]; } - (uint32_t)inputConfig { - return inputChannelConfig; + return [inputNode nodeChannelConfig]; } - (double)secondsBuffered { diff --git a/Audio/Chain/ChunkList.m b/Audio/Chain/ChunkList.m index 475f23fbe..3afe0e845 100644 --- a/Audio/Chain/ChunkList.m +++ b/Audio/Chain/ChunkList.m @@ -50,7 +50,7 @@ } - (BOOL)isFull { - return (maxDuration - listDuration) < 0.01; + return (maxDuration - listDuration) < 0.05; } - (void)addChunk:(AudioChunk *)chunk { diff --git a/Audio/Chain/ConverterNode.h b/Audio/Chain/ConverterNode.h index 8e0dfee6d..81f08047a 100644 --- a/Audio/Chain/ConverterNode.h +++ b/Audio/Chain/ConverterNode.h @@ -15,7 +15,6 @@ #import #import "Node.h" -#import "RefillNode.h" #import "HeadphoneFilter.h" @@ -31,7 +30,6 @@ BOOL stopping; BOOL convertEntered; BOOL paused; - BOOL outputFormatChanged; BOOL skipResampler; @@ -77,13 +75,6 @@ AudioChunk *lastChunkIn; - AudioStreamBasicDescription previousOutputFormat; - uint32_t previousOutputConfig; - AudioStreamBasicDescription rememberedInputFormat; - uint32_t rememberedInputConfig; - RefillNode *refillNode; - id __weak originalPreviousNode; - void *hdcd_decoder; HeadphoneFilter *hFilter; diff --git a/Audio/Chain/ConverterNode.m b/Audio/Chain/ConverterNode.m index 2cda5986f..4872ad889 100644 --- a/Audio/Chain/ConverterNode.m +++ b/Audio/Chain/ConverterNode.m @@ -53,13 +53,9 @@ void PrintStreamDesc(AudioStreamBasicDescription *inDesc) { stopping = NO; convertEntered = NO; paused = NO; - outputFormatChanged = NO; skipResampler = YES; - refillNode = nil; - originalPreviousNode = nil; - extrapolateBuffer = NULL; extrapolateBufferSize = 0; @@ -68,6 +64,8 @@ void PrintStreamDesc(AudioStreamBasicDescription *inDesc) { hdcd_decoder = NULL; + lastChunkIn = nil; + [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.volumeScaling" options:0 context:nil]; } @@ -423,22 +421,14 @@ static void convert_be_to_le(uint8_t *buffer, size_t bitsPerSample, size_t bytes // and returns 0 samples when it has nothing more to process at the end of stream. while([self shouldContinue] == YES) { int amountConverted; + while(paused) { + usleep(500); + } @autoreleasepool { amountConverted = [self convert:writeBuf amount:CHUNK_SIZE]; } if(!amountConverted) { if(paused) { - while(paused) - usleep(500); - continue; - } else if(refillNode) { - // refill node just ended, file resumes - [self setPreviousNode:originalPreviousNode]; - [self setEndOfStream:NO]; - [self setShouldContinue:YES]; - refillNode = nil; - [self cleanUp]; - [self setupWithInputFormat:rememberedInputFormat withInputConfig:rememberedInputConfig outputFormat:outputFormat outputConfig:outputChannelConfig isLossless:rememberedLossless]; continue; } else if(streamFormatChanged) { [self cleanUp]; @@ -503,7 +493,7 @@ tryagain: ssize_t bytesReadFromInput = 0; - while(bytesReadFromInput < amountToWrite && !stopping && !streamFormatChanged && [self shouldContinue] == YES && [self endOfStream] == NO) { + while(bytesReadFromInput < amountToWrite && !stopping && !paused && !streamFormatChanged && [self shouldContinue] == YES && [self endOfStream] == NO) { AudioStreamBasicDescription inf; uint32_t config; if([self peekFormat:&inf channelConfig:&config]) { @@ -536,16 +526,13 @@ tryagain: } bytesReadFromInput += bytesRead; if(!frameCount) { - if(refillNode) - [self setEndOfStream:YES]; - else - usleep(500); + usleep(500); } } // Pad end of track with input format silence - if(stopping || streamFormatChanged || [self shouldContinue] == NO || [self endOfStream] == YES) { + if(stopping || paused || streamFormatChanged || [self shouldContinue] == NO || [self endOfStream] == YES) { if(!skipResampler && !is_postextrapolated_) { if(dsd2pcm) { amountToSkip = dsd2pcmLatency * inputFormat.mBytesPerPacket; @@ -989,9 +976,8 @@ static float db_to_scale(float db) { // Move this here so process call isn't running the resampler until it's allocated stopping = NO; convertEntered = NO; - paused = NO; - outputFormatChanged = NO; streamFormatChanged = NO; + paused = NO; return YES; } @@ -1007,46 +993,18 @@ static float db_to_scale(float db) { - (void)setOutputFormat:(AudioStreamBasicDescription)format outputConfig:(uint32_t)outputConfig { DLog(@"SETTING OUTPUT FORMAT!"); - previousOutputFormat = outputFormat; - previousOutputConfig = outputChannelConfig; outputFormat = format; outputChannelConfig = outputConfig; - outputFormatChanged = YES; } - (void)inputFormatDidChange:(AudioStreamBasicDescription)format inputConfig:(uint32_t)inputConfig { DLog(@"FORMAT CHANGED"); paused = YES; - [self cleanUp]; - if(outputFormatChanged && ![buffer isEmpty] && - (outputChannelConfig != previousOutputConfig || - memcmp(&outputFormat, &previousOutputFormat, sizeof(outputFormat)) != 0)) { - // Transfer previously buffered data, remember input format - rememberedInputFormat = format; - rememberedInputConfig = inputChannelConfig; - originalPreviousNode = previousNode; - refillNode = [[RefillNode alloc] initWithController:controller previous:nil]; - [self setPreviousNode:refillNode]; - - [refillNode setFormat:previousOutputFormat]; - [refillNode setChannelConfig:previousOutputConfig]; - - for(;;) { - @autoreleasepool { - AudioChunk *chunk = [buffer removeSamples:16384]; - size_t frameCount = [chunk frameCount]; - if(frameCount) { - NSData *samples = [chunk removeSamples:frameCount]; - [refillNode writeData:[samples bytes] amount:frameCount]; - } else - break; - } - } - - [self setupWithInputFormat:previousOutputFormat withInputConfig:[AudioChunk guessChannelConfig:previousOutputFormat.mChannelsPerFrame] outputFormat:outputFormat outputConfig:outputChannelConfig isLossless:rememberedLossless]; - } else { - [self setupWithInputFormat:format withInputConfig:inputConfig outputFormat:outputFormat outputConfig:outputChannelConfig isLossless:rememberedLossless]; + while(convertEntered) { + usleep(500); } + [self cleanUp]; + [self setupWithInputFormat:format withInputConfig:inputConfig outputFormat:outputFormat outputConfig:outputChannelConfig isLossless:rememberedLossless]; } - (void)setRGInfo:(NSDictionary *)rgi { diff --git a/Audio/Chain/Downmix.m b/Audio/Chain/Downmix.m index 98a0bf3f0..60089d1cb 100644 --- a/Audio/Chain/Downmix.m +++ b/Audio/Chain/Downmix.m @@ -147,13 +147,12 @@ static void downmix_to_mono(const float *inBuffer, int channels, uint32_t config inBuffer = tempBuffer; channels = 2; config = AudioConfigStereo; - float invchannels = 1.0 / (float)channels; for(size_t i = 0; i < count; ++i) { float sample = 0; for(int j = 0; j < channels; ++j) { sample += inBuffer[i * channels + j]; } - outBuffer[i] = sample * invchannels; + outBuffer[i] = sample; } } diff --git a/Audio/Chain/OutputNode.h b/Audio/Chain/OutputNode.h index 8d5999609..db7312055 100644 --- a/Audio/Chain/OutputNode.h +++ b/Audio/Chain/OutputNode.h @@ -20,7 +20,6 @@ uint32_t config; double amountPlayed; - double sampleRatio; OutputCoreAudio *output; BOOL paused; diff --git a/Audio/Chain/OutputNode.m b/Audio/Chain/OutputNode.m index 586437e6b..f2cf016fe 100644 --- a/Audio/Chain/OutputNode.m +++ b/Audio/Chain/OutputNode.m @@ -17,7 +17,6 @@ - (void)setup { amountPlayed = 0.0; - sampleRatio = 0.0; paused = YES; started = NO; @@ -100,7 +99,6 @@ config = channelConfig; // Calculate a ratio and add to double(seconds) instead, as format may change // double oldSampleRatio = sampleRatio; - sampleRatio = 1.0 / (format.mSampleRate * format.mBytesPerPacket); BufferChain *bufferChain = [controller bufferChain]; if(bufferChain) { ConverterNode *converter = [bufferChain converter]; @@ -108,13 +106,16 @@ // This clears the resampler buffer, but not the input buffer // We also have to jump the play position ahead accounting for // the data we are flushing -#if 0 - // We no longer need to do this, because outputchanged converter - // now uses the RefillNode to slap the previous samples into - // itself - if (oldSampleRatio) - amountPlayed += oldSampleRatio * [[converter buffer] bufferedLength]; -#endif + amountPlayed += [[converter buffer] listDuration]; + + AudioStreamBasicDescription inf = [bufferChain inputFormat]; + uint32_t config = [bufferChain inputConfig]; + + format.mChannelsPerFrame = inf.mChannelsPerFrame; + format.mBytesPerFrame = ((inf.mBitsPerChannel + 7) / 8) * format.mChannelsPerFrame; + format.mBytesPerPacket = format.mBytesPerFrame * format.mFramesPerPacket; + channelConfig = config; + [converter setOutputFormat:format outputConfig:channelConfig]; [converter inputFormatDidChange:[bufferChain inputFormat] inputConfig:[bufferChain inputConfig]]; diff --git a/Audio/Chain/RefillNode.h b/Audio/Chain/RefillNode.h deleted file mode 100644 index 95673c27b..000000000 --- a/Audio/Chain/RefillNode.h +++ /dev/null @@ -1,27 +0,0 @@ -// -// RefillNode.h -// Cog -// -// Created by Christopher SNowhill on 1/13/22. -// Copyright 2022 __LoSnoCo__. All rights reserved. -// - -#import - -#import -#import -#import - -#import "Node.h" -#import "Plugin.h" - -#define INPUT_NODE_SEEK - -@interface RefillNode : Node { - // This node just slaps pre-converted data into its buffer for re-buffering -} - -- (void)setFormat:(AudioStreamBasicDescription)format; -- (void)setChannelConfig:(uint32_t)config; - -@end diff --git a/Audio/Chain/RefillNode.m b/Audio/Chain/RefillNode.m deleted file mode 100644 index 83c6f53a0..000000000 --- a/Audio/Chain/RefillNode.m +++ /dev/null @@ -1,49 +0,0 @@ -// -// RefillNode.m -// Cog -// -// Created by Christopher Snowhill on 1/13/22. -// Copyright 2022 __LoSnoCo__. All rights reserved. -// - -#import "RefillNode.h" -#import "Plugin.h" - -#import "Logging.h" - -@implementation RefillNode - -- (id)initWithController:(id)c previous:(id)p { - self = [super init]; - if(self) { - // This special node should be able to handle up to four buffers - buffer = [[ChunkList alloc] initWithMaximumDuration:12.0]; - semaphore = [[Semaphore alloc] init]; - - initialBufferFilled = NO; - - controller = c; - endOfStream = NO; - shouldContinue = YES; - - nodeLossless = NO; - - [self setPreviousNode:p]; - } - - return self; -} - -- (void)dealloc { - DLog(@"Refill Node dealloc"); -} - -- (void)setFormat:(AudioStreamBasicDescription)format { - nodeFormat = format; -} - -- (void)setChannelConfig:(uint32_t)config { - nodeChannelConfig = config; -} - -@end diff --git a/Audio/CogAudio.xcodeproj/project.pbxproj b/Audio/CogAudio.xcodeproj/project.pbxproj index c163a09fd..68fca5c55 100644 --- a/Audio/CogAudio.xcodeproj/project.pbxproj +++ b/Audio/CogAudio.xcodeproj/project.pbxproj @@ -66,8 +66,6 @@ 8384912718080FF100E7332D /* Logging.h in Headers */ = {isa = PBXBuildFile; fileRef = 8384912618080FF100E7332D /* Logging.h */; }; 839366671815923C006DD712 /* CogPluginMulti.h in Headers */ = {isa = PBXBuildFile; fileRef = 839366651815923C006DD712 /* CogPluginMulti.h */; }; 839366681815923C006DD712 /* CogPluginMulti.m in Sources */ = {isa = PBXBuildFile; fileRef = 839366661815923C006DD712 /* CogPluginMulti.m */; }; - 83A44A01279119B50049B6E2 /* RefillNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 83A449FF279119B50049B6E2 /* RefillNode.m */; }; - 83A44A02279119B50049B6E2 /* RefillNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 83A44A00279119B50049B6E2 /* RefillNode.h */; }; 8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; }; 8E8D3D2F0CBAEE6E00135C1B /* AudioContainer.h in Headers */ = {isa = PBXBuildFile; fileRef = 8E8D3D2D0CBAEE6E00135C1B /* AudioContainer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 8E8D3D300CBAEE6E00135C1B /* AudioContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8E8D3D2E0CBAEE6E00135C1B /* AudioContainer.m */; }; @@ -162,8 +160,6 @@ 8384912618080FF100E7332D /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = ../../Utils/Logging.h; sourceTree = ""; }; 839366651815923C006DD712 /* CogPluginMulti.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CogPluginMulti.h; sourceTree = ""; }; 839366661815923C006DD712 /* CogPluginMulti.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CogPluginMulti.m; sourceTree = ""; }; - 83A449FF279119B50049B6E2 /* RefillNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RefillNode.m; sourceTree = ""; }; - 83A44A00279119B50049B6E2 /* RefillNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RefillNode.h; sourceTree = ""; }; 8DC2EF5A0486A6940098B216 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; 8DC2EF5B0486A6940098B216 /* CogAudio.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CogAudio.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8E8D3D2D0CBAEE6E00135C1B /* AudioContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioContainer.h; sourceTree = ""; }; @@ -298,8 +294,6 @@ 834FD4EF27AF93680063BC83 /* ChunkList.m */, 834FD4F227AFA2150063BC83 /* Downmix.h */, 834FD4F327AFA2150063BC83 /* Downmix.m */, - 83A44A00279119B50049B6E2 /* RefillNode.h */, - 83A449FF279119B50049B6E2 /* RefillNode.m */, 17D21C760B8BE4BA00D1EBDE /* BufferChain.h */, 17D21C770B8BE4BA00D1EBDE /* BufferChain.m */, 8EC1225D0B993BD500C5B3AD /* ConverterNode.h */, @@ -457,7 +451,6 @@ 839366671815923C006DD712 /* CogPluginMulti.h in Headers */, 17ADB13C0B97926D00257CA2 /* AudioSource.h in Headers */, 835C88B1279811A500E28EAE /* hdcd_decode2.h in Headers */, - 83A44A02279119B50049B6E2 /* RefillNode.h in Headers */, 8EC1225F0B993BD500C5B3AD /* ConverterNode.h in Headers */, 8384912718080FF100E7332D /* Logging.h in Headers */, 8E8D3D2F0CBAEE6E00135C1B /* AudioContainer.h in Headers */, @@ -560,7 +553,6 @@ 17C940240B900909008627D6 /* AudioMetadataReader.m in Sources */, 17B619310B909BC300BC003F /* AudioPropertiesReader.m in Sources */, 17ADB13D0B97926D00257CA2 /* AudioSource.m in Sources */, - 83A44A01279119B50049B6E2 /* RefillNode.m in Sources */, 834FD4F127AF93680063BC83 /* ChunkList.m in Sources */, 8EC122600B993BD500C5B3AD /* ConverterNode.m in Sources */, 8E8D3D300CBAEE6E00135C1B /* AudioContainer.m in Sources */, diff --git a/Audio/Output/OutputCoreAudio.m b/Audio/Output/OutputCoreAudio.m index 9a2bc60e3..a3e56e1ef 100644 --- a/Audio/Output/OutputCoreAudio.m +++ b/Audio/Output/OutputCoreAudio.m @@ -211,16 +211,10 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const running = YES; started = NO; stopNext = NO; - size_t eventCount = 0; atomic_store(&bytesRendered, 0); NSMutableArray *delayedEvents = [[NSMutableArray alloc] init]; BOOL delayedEventsPopped = YES; while(!stopping) { - if(++eventCount == 128) { - [self updateDeviceFormat]; - eventCount = 0; - } - if([outputController shouldReset]) { @autoreleasepool { [[outputController buffer] reset]; @@ -671,7 +665,7 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const #endif inputData->mBuffers[0].mNumberChannels = channels; - + return 0; };