diff --git a/Audio/AudioPlayer.m b/Audio/AudioPlayer.m index 1a0f4694e..3c6497867 100644 --- a/Audio/AudioPlayer.m +++ b/Audio/AudioPlayer.m @@ -93,7 +93,7 @@ bufferChain = [[BufferChain alloc] initWithController:self]; [self notifyStreamChanged:userInfo]; - while (![bufferChain open:url withOutputFormatHint:[output format] withRGInfo:rgi]) + while (![bufferChain open:url withOutputFormat:[output format] withRGInfo:rgi]) { bufferChain = nil; @@ -401,7 +401,7 @@ && [[nextStream path] isEqualToString:[[lastChain streamURL] path]])) { if ([lastChain setTrack:nextStream] - && [newChain openWithInput:[lastChain inputNode] withOutputFormatHint:[output format] withRGInfo:nextStreamRGInfo]) + && [newChain openWithInput:[lastChain inputNode] withOutputFormat:[output format] withRGInfo:nextStreamRGInfo]) { [newChain setStreamURL:nextStream]; [newChain setUserInfo:nextStreamUserInfo]; @@ -418,7 +418,7 @@ lastChain = nil; - while (shouldContinue && ![newChain open:nextStream withOutputFormatHint:[output format] withRGInfo:nextStreamRGInfo]) + while (shouldContinue && ![newChain open:nextStream withOutputFormat:[output format] withRGInfo:nextStreamRGInfo]) { if (nextStream == nil) { diff --git a/Audio/Chain/BufferChain.h b/Audio/Chain/BufferChain.h index 69cdc625f..4f11b19e7 100644 --- a/Audio/Chain/BufferChain.h +++ b/Audio/Chain/BufferChain.h @@ -30,14 +30,14 @@ - (id)initWithController:(id)c; - (void)buildChain; -- (BOOL)open:(NSURL *)url withOutputFormatHint:(AudioStreamBasicDescription)outputFormat withRGInfo:(NSDictionary*)rgi; +- (BOOL)open:(NSURL *)url withOutputFormat:(AudioStreamBasicDescription)outputFormat withRGInfo:(NSDictionary*)rgi; //Used when changing tracks to reuse the same decoder -- (BOOL)openWithInput:(InputNode *)i withOutputFormatHint:(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)decoder - withOutputFormatHint:(AudioStreamBasicDescription)outputFormat + withOutputFormat:(AudioStreamBasicDescription)outputFormat withRGInfo:(NSDictionary*)rgi; - (void)seek:(double)time; diff --git a/Audio/Chain/BufferChain.m b/Audio/Chain/BufferChain.m index 6b17d525d..29a428f72 100644 --- a/Audio/Chain/BufferChain.m +++ b/Audio/Chain/BufferChain.m @@ -43,7 +43,7 @@ finalNode = converterNode; } -- (BOOL)open:(NSURL *)url withOutputFormatHint:(AudioStreamBasicDescription)outputFormat withRGInfo:(NSDictionary *)rgi +- (BOOL)open:(NSURL *)url withOutputFormat:(AudioStreamBasicDescription)outputFormat withRGInfo:(NSDictionary *)rgi { [self setStreamURL:url]; @@ -64,14 +64,8 @@ return NO; NSDictionary * properties = [inputNode properties]; - - inputFormat = [inputNode nodeFormat]; - - outputFormat.mChannelsPerFrame = inputFormat.mChannelsPerFrame; - outputFormat.mBytesPerFrame = ((outputFormat.mBitsPerChannel + 7) / 8) * outputFormat.mChannelsPerFrame; - outputFormat.mBytesPerPacket = outputFormat.mBytesPerFrame * outputFormat.mFramesPerPacket; - if (![converterNode setupWithInputFormat:inputFormat outputFormat:outputFormat isLossless:[[properties valueForKey:@"encoding"] isEqualToString:@"lossless"]]) + if (![converterNode setupWithInputFormat:(inputFormat = propertiesToASBD(properties)) outputFormat:outputFormat isLossless:[[properties valueForKey:@"encoding"] isEqualToString:@"lossless"]]) return NO; [self setRGInfo:rgi]; @@ -81,7 +75,7 @@ return YES; } -- (BOOL)openWithInput:(InputNode *)i withOutputFormatHint:(AudioStreamBasicDescription)outputFormat withRGInfo:(NSDictionary *)rgi +- (BOOL)openWithInput:(InputNode *)i withOutputFormat:(AudioStreamBasicDescription)outputFormat withRGInfo:(NSDictionary *)rgi { DLog(@"New buffer chain!"); [self buildChain]; @@ -92,14 +86,7 @@ NSDictionary * properties = [inputNode properties]; DLog(@"Input Properties: %@", properties); - - inputFormat = [inputNode nodeFormat]; - - outputFormat.mChannelsPerFrame = inputFormat.mChannelsPerFrame; - outputFormat.mBytesPerFrame = ((outputFormat.mBitsPerChannel + 7) / 8) * outputFormat.mChannelsPerFrame; - outputFormat.mBytesPerPacket = outputFormat.mBytesPerFrame * outputFormat.mFramesPerPacket; - - if (![converterNode setupWithInputFormat:inputFormat outputFormat:outputFormat isLossless:[[properties objectForKey:@"encoding"] isEqualToString:@"lossless"]]) + if (![converterNode setupWithInputFormat:(inputFormat = propertiesToASBD(properties)) outputFormat:outputFormat isLossless:[[properties objectForKey:@"encoding"] isEqualToString:@"lossless"]]) return NO; [self setRGInfo:rgi]; @@ -108,7 +95,7 @@ } - (BOOL)openWithDecoder:(id)decoder - withOutputFormatHint:(AudioStreamBasicDescription)outputFormat + withOutputFormat:(AudioStreamBasicDescription)outputFormat withRGInfo:(NSDictionary*)rgi; { DLog(@"New buffer chain!"); @@ -120,14 +107,7 @@ NSDictionary * properties = [inputNode properties]; DLog(@"Input Properties: %@", properties); - - inputFormat = [inputNode nodeFormat]; - - outputFormat.mChannelsPerFrame = inputFormat.mChannelsPerFrame; - outputFormat.mBytesPerFrame = ((outputFormat.mBitsPerChannel + 7) / 8) * outputFormat.mChannelsPerFrame; - outputFormat.mBytesPerPacket = outputFormat.mBytesPerFrame * outputFormat.mFramesPerPacket; - - if (![converterNode setupWithInputFormat:inputFormat outputFormat:outputFormat isLossless:[[properties objectForKey:@"encoding"] isEqualToString:@"lossless"]]) + if (![converterNode setupWithInputFormat:(inputFormat = propertiesToASBD(properties)) outputFormat:outputFormat isLossless:[[properties objectForKey:@"encoding"] isEqualToString:@"lossless"]]) return NO; [self setRGInfo:rgi]; diff --git a/Audio/Chain/ConverterNode.h b/Audio/Chain/ConverterNode.h index e00129294..514524436 100644 --- a/Audio/Chain/ConverterNode.h +++ b/Audio/Chain/ConverterNode.h @@ -74,6 +74,8 @@ id __weak originalPreviousNode; void *hdcd_decoder; + + HeadphoneFilter *hFilter; } @property AudioStreamBasicDescription inputFormat; diff --git a/Audio/Chain/ConverterNode.m b/Audio/Chain/ConverterNode.m index e679c9868..966940733 100644 --- a/Audio/Chain/ConverterNode.m +++ b/Audio/Chain/ConverterNode.m @@ -72,11 +72,189 @@ void PrintStreamDesc (AudioStreamBasicDescription *inDesc) hdcd_decoder = NULL; [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.volumeScaling" options:0 context:nil]; + [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.headphoneVirtualization" options:0 context:nil]; + [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.hrirPath" options:0 context:nil]; } return self; } +static const float STEREO_DOWNMIX[8-2][8][2]={ + /*3.0*/ + { + {0.5858F,0.0F},{0.0F,0.5858F},{0.4142F,0.4142F} + }, + /*quadrophonic*/ + { + {0.4226F,0.0F},{0.0F,0.4226F},{0.366F,0.2114F},{0.2114F,0.336F} + }, + /*5.0*/ + { + {0.651F,0.0F},{0.0F,0.651F},{0.46F,0.46F},{0.5636F,0.3254F}, + {0.3254F,0.5636F} + }, + /*5.1*/ + { + {0.529F,0.0F},{0.0F,0.529F},{0.3741F,0.3741F},{0.3741F,0.3741F},{0.4582F,0.2645F}, + {0.2645F,0.4582F} + }, + /*6.1*/ + { + {0.4553F,0.0F},{0.0F,0.4553F},{0.322F,0.322F},{0.322F,0.322F},{0.2788F,0.2788F}, + {0.3943F,0.2277F},{0.2277F,0.3943F} + }, + /*7.1*/ + { + {0.3886F,0.0F},{0.0F,0.3886F},{0.2748F,0.2748F},{0.2748F,0.2748F},{0.3366F,0.1943F}, + {0.1943F,0.3366F},{0.3366F,0.1943F},{0.1943F,0.3366F} + } +}; + +static void downmix_to_stereo(float * buffer, int channels, size_t count) +{ + if (channels >= 3 && channels <= 8) + for (size_t i = 0; i < count; ++i) + { + float left = 0, right = 0; + for (int j = 0; j < channels; ++j) + { + left += buffer[i * channels + j] * STEREO_DOWNMIX[channels - 3][j][0]; + right += buffer[i * channels + j] * STEREO_DOWNMIX[channels - 3][j][1]; + } + buffer[i * 2 + 0] = left; + buffer[i * 2 + 1] = right; + } +} + +static void downmix_to_mono(float * buffer, int channels, size_t count) +{ + if (channels >= 3 && channels <= 8) + { + downmix_to_stereo(buffer, channels, count); + channels = 2; + } + 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 += buffer[i * channels + j]; + } + buffer[i] = sample * invchannels; + } +} + +static void upmix(float * buffer, int inchannels, int outchannels, size_t count) +{ + for (ssize_t i = count - 1; i >= 0; --i) + { + if (inchannels == 1 && outchannels == 2) + { + // upmix mono to stereo + float sample = buffer[i]; + buffer[i * 2 + 0] = sample; + buffer[i * 2 + 1] = sample; + } + else if (inchannels == 1 && outchannels == 4) + { + // upmix mono to quad + float sample = buffer[i]; + buffer[i * 4 + 0] = sample; + buffer[i * 4 + 1] = sample; + buffer[i * 4 + 2] = 0; + buffer[i * 4 + 3] = 0; + } + else if (inchannels == 1 && (outchannels == 3 || outchannels >= 5)) + { + // upmix mono to center channel + float sample = buffer[i]; + buffer[i * outchannels + 2] = sample; + for (int j = 0; j < 2; ++j) + { + buffer[i * outchannels + j] = 0; + } + for (int j = 3; j < outchannels; ++j) + { + buffer[i * outchannels + j] = 0; + } + } + else if (inchannels == 4 && outchannels >= 5) + { + float fl = buffer[i * 4 + 0]; + float fr = buffer[i * 4 + 1]; + float bl = buffer[i * 4 + 2]; + float br = buffer[i * 4 + 3]; + const int skipclfe = (outchannels == 5) ? 1 : 2; + buffer[i * outchannels + 0] = fl; + buffer[i * outchannels + 1] = fr; + buffer[i * outchannels + skipclfe + 2] = bl; + buffer[i * outchannels + skipclfe + 3] = br; + for (int j = 0; j < skipclfe; ++j) + { + buffer[i * outchannels + 2 + j] = 0; + } + for (int j = 4 + skipclfe; j < outchannels; ++j) + { + buffer[i * outchannels + j] = 0; + } + } + else if (inchannels == 5 && outchannels >= 6) + { + float fl = buffer[i * 5 + 0]; + float fr = buffer[i * 5 + 1]; + float c = buffer[i * 5 + 2]; + float bl = buffer[i * 5 + 3]; + float br = buffer[i * 5 + 4]; + buffer[i * outchannels + 0] = fl; + buffer[i * outchannels + 1] = fr; + buffer[i * outchannels + 2] = c; + buffer[i * outchannels + 3] = 0; + buffer[i * outchannels + 4] = bl; + buffer[i * outchannels + 5] = br; + for (int j = 6; j < outchannels; ++j) + { + buffer[i * outchannels + j] = 0; + } + } + else if (inchannels == 7 && outchannels == 8) + { + float fl = buffer[i * 7 + 0]; + float fr = buffer[i * 7 + 1]; + float c = buffer[i * 7 + 2]; + float lfe = buffer[i * 7 + 3]; + float sl = buffer[i * 7 + 4]; + float sr = buffer[i * 7 + 5]; + float bc = buffer[i * 7 + 6]; + buffer[i * 8 + 0] = fl; + buffer[i * 8 + 1] = fr; + buffer[i * 8 + 2] = c; + buffer[i * 8 + 3] = lfe; + buffer[i * 8 + 4] = bc; + buffer[i * 8 + 5] = bc; + buffer[i * 8 + 6] = sl; + buffer[i * 8 + 7] = sr; + } + else + { + // upmix N channels to N channels plus silence the empty channels + float samples[inchannels]; + for (int j = 0; j < inchannels; ++j) + { + samples[j] = buffer[i * inchannels + j]; + } + for (int j = 0; j < inchannels; ++j) + { + buffer[i * outchannels + j] = samples[j]; + } + for (int j = inchannels; j < outchannels; ++j) + { + buffer[i * outchannels + j] = 0; + } + } + } +} + void scale_by_volume(float * buffer, size_t count, float volume) { if ( volume != 1.0 ) @@ -842,6 +1020,31 @@ tryagain: scale_by_volume( (float*) floatBuffer, amountReadFromFC / sizeof(float), volumeScale); + if ( hFilter ) { + int samples = amountReadFromFC / floatFormat.mBytesPerFrame; + [hFilter process:floatBuffer sampleCount:samples toBuffer:floatBuffer + amountReadFromFC]; + memmove(floatBuffer, floatBuffer + amountReadFromFC, samples * sizeof(float) * 2); + amountReadFromFC = samples * sizeof(float) * 2; + } + else if ( inputFormat.mChannelsPerFrame > 2 && outputFormat.mChannelsPerFrame == 2 ) + { + int samples = amountReadFromFC / floatFormat.mBytesPerFrame; + downmix_to_stereo( (float*) floatBuffer, inputFormat.mChannelsPerFrame, samples ); + amountReadFromFC = samples * sizeof(float) * 2; + } + else if ( inputFormat.mChannelsPerFrame > 1 && outputFormat.mChannelsPerFrame == 1 ) + { + int samples = amountReadFromFC / floatFormat.mBytesPerFrame; + downmix_to_mono( (float*) floatBuffer, inputFormat.mChannelsPerFrame, samples ); + amountReadFromFC = samples * sizeof(float); + } + else if ( inputFormat.mChannelsPerFrame < outputFormat.mChannelsPerFrame ) + { + int samples = amountReadFromFC / floatFormat.mBytesPerFrame; + upmix( (float*) floatBuffer, inputFormat.mChannelsPerFrame, outputFormat.mChannelsPerFrame, samples ); + amountReadFromFC = samples * sizeof(float) * outputFormat.mChannelsPerFrame; + } + floatSize = amountReadFromFC; floatOffset = 0; } @@ -872,6 +1075,15 @@ tryagain: //User reset the volume scaling option [self refreshVolumeScaling]; } + else if ([keyPath isEqualToString:@"values.headphoneVirtualization"] || + [keyPath isEqualToString:@"values.hrirPath"]) { + // Reset the converter, without rebuffering + if (outputFormat.mChannelsPerFrame == 2 && + inputFormat.mChannelsPerFrame >= 1 && + inputFormat.mChannelsPerFrame <= 8) { + [self inputFormatDidChange:inputFormat]; + } + } } static float db_to_scale(float db) @@ -929,8 +1141,6 @@ static float db_to_scale(float db) inputFormat = inf; outputFormat = outf; - nodeFormat = outputFormat; - rememberedLossless = lossless; // These are the only sample formats we support translating @@ -982,6 +1192,33 @@ static float db_to_scale(float db) dmFloatFormat.mBytesPerFrame = (32/8)*dmFloatFormat.mChannelsPerFrame; dmFloatFormat.mBytesPerPacket = dmFloatFormat.mBytesPerFrame * floatFormat.mFramesPerPacket; + BOOL hVirt = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] boolForKey:@"headphoneVirtualization"]; + + if (hVirt && + outputFormat.mChannelsPerFrame == 2 && + inputFormat.mChannelsPerFrame >= 1 && + inputFormat.mChannelsPerFrame <= 8) { + NSString * userPreset = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] stringForKey:@"hrirPath"]; + + NSURL * presetUrl = nil; + + if (userPreset && ![userPreset isEqualToString:@""]) { + presetUrl = [NSURL fileURLWithPath:userPreset]; + if (![HeadphoneFilter validateImpulseFile:presetUrl]) + presetUrl = nil; + } + + if (!presetUrl) { + presetUrl = [[NSBundle mainBundle] URLForResource:@"gsx" withExtension:@"wv"]; + if (![HeadphoneFilter validateImpulseFile:presetUrl]) + presetUrl = nil; + } + + if (presetUrl) { + hFilter = [[HeadphoneFilter alloc] initWithImpulseFile:presetUrl forSampleRate:outputFormat.mSampleRate withInputChannels:inputFormat.mChannelsPerFrame]; + } + } + skipResampler = outputFormat.mSampleRate == floatFormat.mSampleRate; sampleRatio = (double)outputFormat.mSampleRate / (double)floatFormat.mSampleRate; @@ -1034,6 +1271,8 @@ static float db_to_scale(float db) DLog(@"Decoder dealloc"); [[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.volumeScaling"]; + [[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.headphoneVirtualization"]; + [[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.hrirPath"]; paused = NO; [self cleanUp]; @@ -1098,6 +1337,9 @@ static float db_to_scale(float db) { usleep(500); } + if (hFilter) { + hFilter = nil; + } if (hdcd_decoder) { free(hdcd_decoder); diff --git a/Audio/Chain/Downmix.h b/Audio/Chain/Downmix.h deleted file mode 100644 index 3d9116837..000000000 --- a/Audio/Chain/Downmix.h +++ /dev/null @@ -1,26 +0,0 @@ -// -// Downmix.h -// Cog -// -// Created by Christopher Snowhill on 2/05/22. -// Copyright 2022 __LoSnoCo__. All rights reserved. -// - -#import -#import - -#import "HeadphoneFilter.h" - -@interface DownmixProcessor : NSObject { - HeadphoneFilter *hFilter; - - AudioStreamBasicDescription inputFormat; - AudioStreamBasicDescription outputFormat; -} - -- (id) initWithInputFormat:(AudioStreamBasicDescription)inf andOutputFormat:(AudioStreamBasicDescription)outf; - -- (void) process:(const void*)inBuffer frameCount:(size_t)frames output:(void *)outBuffer; - -@end - diff --git a/Audio/Chain/Downmix.m b/Audio/Chain/Downmix.m deleted file mode 100644 index 1348a1277..000000000 --- a/Audio/Chain/Downmix.m +++ /dev/null @@ -1,302 +0,0 @@ -// -// Downmix.m -// Cog -// -// Created by Christopher Snowhill on 2/05/22. -// Copyright 2022 __LoSnoCo__. All rights reserved. -// - -#import "Downmix.h" - -#import "Logging.h" - -static const float STEREO_DOWNMIX[8-2][8][2]={ - /*3.0*/ - { - {0.5858F,0.0F},{0.0F,0.5858F},{0.4142F,0.4142F} - }, - /*quadrophonic*/ - { - {0.4226F,0.0F},{0.0F,0.4226F},{0.366F,0.2114F},{0.2114F,0.336F} - }, - /*5.0*/ - { - {0.651F,0.0F},{0.0F,0.651F},{0.46F,0.46F},{0.5636F,0.3254F}, - {0.3254F,0.5636F} - }, - /*5.1*/ - { - {0.529F,0.0F},{0.0F,0.529F},{0.3741F,0.3741F},{0.3741F,0.3741F},{0.4582F,0.2645F}, - {0.2645F,0.4582F} - }, - /*6.1*/ - { - {0.4553F,0.0F},{0.0F,0.4553F},{0.322F,0.322F},{0.322F,0.322F},{0.2788F,0.2788F}, - {0.3943F,0.2277F},{0.2277F,0.3943F} - }, - /*7.1*/ - { - {0.3886F,0.0F},{0.0F,0.3886F},{0.2748F,0.2748F},{0.2748F,0.2748F},{0.3366F,0.1943F}, - {0.1943F,0.3366F},{0.3366F,0.1943F},{0.1943F,0.3366F} - } -}; - -static void downmix_to_stereo(const float * inBuffer, int channels, float * outBuffer, size_t count) -{ - if (channels >= 3 && channels <= 8) - for (size_t i = 0; i < count; ++i) - { - float left = 0, right = 0; - for (int j = 0; j < channels; ++j) - { - left += inBuffer[i * channels + j] * STEREO_DOWNMIX[channels - 3][j][0]; - right += inBuffer[i * channels + j] * STEREO_DOWNMIX[channels - 3][j][1]; - } - outBuffer[i * 2 + 0] = left; - outBuffer[i * 2 + 1] = right; - } -} - -static void downmix_to_mono(const float * inBuffer, int channels, float * outBuffer, size_t count) -{ - float tempBuffer[count * 2]; - if (channels >= 3 && channels <= 8) - { - downmix_to_stereo(inBuffer, channels, tempBuffer, count); - inBuffer = tempBuffer; - channels = 2; - } - 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; - } -} - -static void upmix(const float * inBuffer, int inchannels, float * outBuffer, int outchannels, size_t count) -{ - for (ssize_t i = 0; i < count; ++i) - { - if (inchannels == 1 && outchannels == 2) - { - // upmix mono to stereo - float sample = inBuffer[i]; - outBuffer[i * 2 + 0] = sample; - outBuffer[i * 2 + 1] = sample; - } - else if (inchannels == 1 && outchannels == 4) - { - // upmix mono to quad - float sample = inBuffer[i]; - outBuffer[i * 4 + 0] = sample; - outBuffer[i * 4 + 1] = sample; - outBuffer[i * 4 + 2] = 0; - outBuffer[i * 4 + 3] = 0; - } - else if (inchannels == 1 && (outchannels == 3 || outchannels >= 5)) - { - // upmix mono to center channel - float sample = inBuffer[i]; - outBuffer[i * outchannels + 2] = sample; - for (int j = 0; j < 2; ++j) - { - outBuffer[i * outchannels + j] = 0; - } - for (int j = 3; j < outchannels; ++j) - { - outBuffer[i * outchannels + j] = 0; - } - } - else if (inchannels == 4 && outchannels >= 5) - { - float fl = inBuffer[i * 4 + 0]; - float fr = inBuffer[i * 4 + 1]; - float bl = inBuffer[i * 4 + 2]; - float br = inBuffer[i * 4 + 3]; - const int skipclfe = (outchannels == 5) ? 1 : 2; - outBuffer[i * outchannels + 0] = fl; - outBuffer[i * outchannels + 1] = fr; - outBuffer[i * outchannels + skipclfe + 2] = bl; - outBuffer[i * outchannels + skipclfe + 3] = br; - for (int j = 0; j < skipclfe; ++j) - { - outBuffer[i * outchannels + 2 + j] = 0; - } - for (int j = 4 + skipclfe; j < outchannels; ++j) - { - outBuffer[i * outchannels + j] = 0; - } - } - else if (inchannels == 5 && outchannels >= 6) - { - float fl = inBuffer[i * 5 + 0]; - float fr = inBuffer[i * 5 + 1]; - float c = inBuffer[i * 5 + 2]; - float bl = inBuffer[i * 5 + 3]; - float br = inBuffer[i * 5 + 4]; - outBuffer[i * outchannels + 0] = fl; - outBuffer[i * outchannels + 1] = fr; - outBuffer[i * outchannels + 2] = c; - outBuffer[i * outchannels + 3] = 0; - outBuffer[i * outchannels + 4] = bl; - outBuffer[i * outchannels + 5] = br; - for (int j = 6; j < outchannels; ++j) - { - outBuffer[i * outchannels + j] = 0; - } - } - else if (inchannels == 7 && outchannels == 8) - { - float fl = inBuffer[i * 7 + 0]; - float fr = inBuffer[i * 7 + 1]; - float c = inBuffer[i * 7 + 2]; - float lfe = inBuffer[i * 7 + 3]; - float sl = inBuffer[i * 7 + 4]; - float sr = inBuffer[i * 7 + 5]; - float bc = inBuffer[i * 7 + 6]; - outBuffer[i * 8 + 0] = fl; - outBuffer[i * 8 + 1] = fr; - outBuffer[i * 8 + 2] = c; - outBuffer[i * 8 + 3] = lfe; - outBuffer[i * 8 + 4] = bc; - outBuffer[i * 8 + 5] = bc; - outBuffer[i * 8 + 6] = sl; - outBuffer[i * 8 + 7] = sr; - } - else - { - // upmix N channels to N channels plus silence the empty channels - float samples[inchannels]; - for (int j = 0; j < inchannels; ++j) - { - samples[j] = inBuffer[i * inchannels + j]; - } - for (int j = 0; j < inchannels; ++j) - { - outBuffer[i * outchannels + j] = samples[j]; - } - for (int j = inchannels; j < outchannels; ++j) - { - outBuffer[i * outchannels + j] = 0; - } - } - } -} - -@implementation DownmixProcessor - -- (id) initWithInputFormat:(AudioStreamBasicDescription)inf andOutputFormat:(AudioStreamBasicDescription)outf { - self = [super init]; - - if (self) { - if (inf.mFormatID != kAudioFormatLinearPCM || - (inf.mFormatFlags & kAudioFormatFlagsNativeFloatPacked) != kAudioFormatFlagsNativeFloatPacked || - inf.mBitsPerChannel != 32 || - inf.mBytesPerFrame != (4 * inf.mChannelsPerFrame) || - inf.mBytesPerPacket != inf.mFramesPerPacket * inf.mBytesPerFrame) - return nil; - - if (outf.mFormatID != kAudioFormatLinearPCM || - (outf.mFormatFlags & kAudioFormatFlagsNativeFloatPacked) != kAudioFormatFlagsNativeFloatPacked || - outf.mBitsPerChannel != 32 || - outf.mBytesPerFrame != (4 * outf.mChannelsPerFrame) || - outf.mBytesPerPacket != outf.mFramesPerPacket * outf.mBytesPerFrame) - return nil; - - inputFormat = inf; - outputFormat = outf; - - [self setupVirt]; - - [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.headphoneVirtualization" options:0 context:nil]; - [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.hrirPath" options:0 context:nil]; - } - - return self; -} - -- (void) dealloc { - [[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.headphoneVirtualization"]; - [[NSUserDefaultsController sharedUserDefaultsController] removeObserver:self forKeyPath:@"values.hrirPath"]; -} - -- (void) setupVirt { - @synchronized(hFilter) { - hFilter = nil; - } - - BOOL hVirt = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] boolForKey:@"headphoneVirtualization"]; - - if (hVirt && - outputFormat.mChannelsPerFrame == 2 && - inputFormat.mChannelsPerFrame >= 1 && - inputFormat.mChannelsPerFrame <= 8) { - NSString * userPreset = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] stringForKey:@"hrirPath"]; - - NSURL * presetUrl = nil; - - if (userPreset && ![userPreset isEqualToString:@""]) { - presetUrl = [NSURL fileURLWithPath:userPreset]; - if (![HeadphoneFilter validateImpulseFile:presetUrl]) - presetUrl = nil; - } - - if (!presetUrl) { - presetUrl = [[NSBundle mainBundle] URLForResource:@"gsx" withExtension:@"wv"]; - if (![HeadphoneFilter validateImpulseFile:presetUrl]) - presetUrl = nil; - } - - if (presetUrl) { - @synchronized(hFilter) { - hFilter = [[HeadphoneFilter alloc] initWithImpulseFile:presetUrl forSampleRate:outputFormat.mSampleRate withInputChannels:inputFormat.mChannelsPerFrame]; - } - } - } -} - -- (void)observeValueForKeyPath:(NSString *)keyPath - ofObject:(id)object - change:(NSDictionary *)change - context:(void *)context -{ - DLog(@"SOMETHING CHANGED!"); - if ([keyPath isEqualToString:@"values.headphoneVirtualization"] || - [keyPath isEqualToString:@"values.hrirPath"]) { - // Reset the converter, without rebuffering - [self setupVirt]; - } -} - -- (void) process:(const void *)inBuffer frameCount:(size_t)frames output:(void *)outBuffer { - @synchronized (hFilter) { - if ( hFilter ) { - [hFilter process:(const float *) inBuffer sampleCount:frames toBuffer:(float *) outBuffer]; - return; - } - } - - if ( inputFormat.mChannelsPerFrame > 2 && outputFormat.mChannelsPerFrame == 2 ) - { - downmix_to_stereo( (const float*) inBuffer, inputFormat.mChannelsPerFrame, (float*) outBuffer, frames ); - } - else if ( inputFormat.mChannelsPerFrame > 1 && outputFormat.mChannelsPerFrame == 1 ) - { - downmix_to_mono( (const float*) inBuffer, inputFormat.mChannelsPerFrame, (float*) outBuffer, frames ); - } - else if ( inputFormat.mChannelsPerFrame < outputFormat.mChannelsPerFrame ) - { - upmix( (const float*) inBuffer, inputFormat.mChannelsPerFrame, (float*) outBuffer, outputFormat.mChannelsPerFrame, frames ); - } - else if ( inputFormat.mChannelsPerFrame == outputFormat.mChannelsPerFrame ) - { - memcpy(outBuffer, inBuffer, frames * outputFormat.mBytesPerPacket); - } -} - -@end diff --git a/Audio/Chain/InputNode.m b/Audio/Chain/InputNode.m index fe8d64bef..cbdadaa4e 100644 --- a/Audio/Chain/InputNode.m +++ b/Audio/Chain/InputNode.m @@ -51,8 +51,6 @@ bytesPerFrame = ((bitsPerSample + 7) / 8) * channels; - nodeFormat = propertiesToASBD(properties); - shouldContinue = YES; shouldSeek = NO; @@ -70,8 +68,6 @@ bytesPerFrame = ((bitsPerSample + 7) / 8) * channels; - nodeFormat = propertiesToASBD(properties); - [self registerObservers]; shouldContinue = YES; diff --git a/Audio/Chain/Node.h b/Audio/Chain/Node.h index bf327fe78..65eca918f 100644 --- a/Audio/Chain/Node.h +++ b/Audio/Chain/Node.h @@ -7,7 +7,6 @@ // #import -#import #import "VirtualRingBuffer.h" #import "Semaphore.h" @@ -26,12 +25,7 @@ BOOL shouldContinue; BOOL endOfStream; //All data is now in buffer BOOL initialBufferFilled; - - AudioStreamBasicDescription nodeFormat; } - -@property (readonly) AudioStreamBasicDescription nodeFormat; - - (id)initWithController:(id)c previous:(id)p; - (int)writeData:(void *)ptr amount:(int)a; diff --git a/Audio/Chain/Node.m b/Audio/Chain/Node.m index 74b5500d2..354a6a22c 100644 --- a/Audio/Chain/Node.m +++ b/Audio/Chain/Node.m @@ -13,8 +13,6 @@ @implementation Node -@synthesize nodeFormat; - - (id)initWithController:(id)c previous:(id)p { self = [super init]; @@ -43,8 +41,7 @@ while (shouldContinue == YES && amountLeft > 0) { - BOOL wrapped; - availOutput = [buffer lengthAvailableToWriteReturningPointer:&writePtr bufferWrapped:&wrapped]; + availOutput = [buffer lengthAvailableToWriteReturningPointer:&writePtr]; if (availOutput == 0) { if (initialBufferFilled == NO) { initialBufferFilled = YES; diff --git a/Audio/Chain/OutputNode.h b/Audio/Chain/OutputNode.h index 5278439b8..89f0a3354 100644 --- a/Audio/Chain/OutputNode.h +++ b/Audio/Chain/OutputNode.h @@ -24,9 +24,6 @@ BOOL paused; BOOL started; - - BOOL formatSetup; - BOOL formatChanged; } - (void)beginEqualizer:(AudioUnit)eq; @@ -65,6 +62,4 @@ - (void)sustainHDCD; -- (BOOL)formatChanged; - @end diff --git a/Audio/Chain/OutputNode.m b/Audio/Chain/OutputNode.m index 7d85fc623..538448ede 100644 --- a/Audio/Chain/OutputNode.m +++ b/Audio/Chain/OutputNode.m @@ -22,9 +22,6 @@ paused = YES; started = NO; - - formatSetup = NO; - formatChanged = NO; output = [[OutputCoreAudio alloc] initWithController:self]; @@ -87,7 +84,7 @@ @autoreleasepool { int n; [self setPreviousNode:[[controller bufferChain] finalNode]]; - + n = [super readData:ptr amount:amount]; /* if (n == 0) { @@ -131,14 +128,6 @@ if (oldSampleRatio) amountPlayed += oldSampleRatio * [[converter buffer] bufferedLength]; #endif - AudioStreamBasicDescription inf = [bufferChain inputFormat]; - - format.mChannelsPerFrame = inf.mChannelsPerFrame; - format.mBytesPerFrame = ((format.mBitsPerChannel + 7) / 8) * format.mChannelsPerFrame; - format.mBytesPerPacket = format.mBytesPerFrame * format.mFramesPerPacket; - - sampleRatio = 1.0 / (format.mSampleRate * format.mBytesPerPacket); - [converter setOutputFormat:format]; [converter inputFormatDidChange:[bufferChain inputFormat]]; } @@ -189,21 +178,4 @@ [output sustainHDCD]; } -- (BOOL)formatChanged -{ - [self setPreviousNode:[[controller bufferChain] finalNode]]; - - AudioStreamBasicDescription inf = [[self previousNode] nodeFormat]; - - if (!formatSetup || memcmp(&nodeFormat, &inf, sizeof(nodeFormat)) != 0) { - nodeFormat = inf; - formatSetup = YES; - formatChanged = YES; - } - - BOOL copyFormatChanged = formatChanged; - formatChanged = NO; - return copyFormatChanged; -} - @end diff --git a/Audio/CogAudio.xcodeproj/project.pbxproj b/Audio/CogAudio.xcodeproj/project.pbxproj index e5d0c37dd..0871028fd 100644 --- a/Audio/CogAudio.xcodeproj/project.pbxproj +++ b/Audio/CogAudio.xcodeproj/project.pbxproj @@ -59,8 +59,6 @@ 83725A8E27AA0DE60003F694 /* libsoxr.0.dylib in CopyFiles */ = {isa = PBXBuildFile; fileRef = 83725A8A27AA0DBF0003F694 /* libsoxr.0.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 83725A9027AA16C90003F694 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83725A7B27AA0D8A0003F694 /* Accelerate.framework */; }; 83725A9127AA16D50003F694 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 83725A7C27AA0D8E0003F694 /* AVFoundation.framework */; }; - 8380F2D927AE6053009183C1 /* Downmix.m in Sources */ = {isa = PBXBuildFile; fileRef = 8380F2D727AE6053009183C1 /* Downmix.m */; }; - 8380F2DA27AE6053009183C1 /* Downmix.h in Headers */ = {isa = PBXBuildFile; fileRef = 8380F2D827AE6053009183C1 /* Downmix.h */; }; 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 */; }; @@ -153,8 +151,6 @@ 83725A7C27AA0D8E0003F694 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; }; 83725A8827AA0DBF0003F694 /* soxr.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = soxr.h; sourceTree = ""; }; 83725A8A27AA0DBF0003F694 /* libsoxr.0.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libsoxr.0.dylib; sourceTree = ""; }; - 8380F2D727AE6053009183C1 /* Downmix.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Downmix.m; sourceTree = ""; }; - 8380F2D827AE6053009183C1 /* Downmix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Downmix.h; sourceTree = ""; }; 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 = ""; }; @@ -288,8 +284,6 @@ 17D21C750B8BE4BA00D1EBDE /* Chain */ = { isa = PBXGroup; children = ( - 8380F2D827AE6053009183C1 /* Downmix.h */, - 8380F2D727AE6053009183C1 /* Downmix.m */, 83A44A00279119B50049B6E2 /* RefillNode.h */, 83A449FF279119B50049B6E2 /* RefillNode.m */, 17D21C760B8BE4BA00D1EBDE /* BufferChain.h */, @@ -452,7 +446,6 @@ 8347C7412796C58800FA8A7D /* NSFileHandle+CreateFile.h in Headers */, 17C940230B900909008627D6 /* AudioMetadataReader.h in Headers */, 83725A8B27AA0DBF0003F694 /* soxr.h in Headers */, - 8380F2DA27AE6053009183C1 /* Downmix.h in Headers */, 17B619300B909BC300BC003F /* AudioPropertiesReader.h in Headers */, 835EDD7D279FE307001EDCCE /* HeadphoneFilter.h in Headers */, 839366671815923C006DD712 /* CogPluginMulti.h in Headers */, @@ -553,7 +546,6 @@ 8347C7422796C58800FA8A7D /* NSFileHandle+CreateFile.m in Sources */, 17D21DC80B8BE79700D1EBDE /* CoreAudioUtils.m in Sources */, 839366681815923C006DD712 /* CogPluginMulti.m in Sources */, - 8380F2D927AE6053009183C1 /* Downmix.m in Sources */, 835C88AA2797D4D400E28EAE /* lpc.c in Sources */, 17D21EBE0B8BF44000D1EBDE /* AudioPlayer.m in Sources */, 17F94DD60B8D0F7000A34E87 /* PluginController.m in Sources */, diff --git a/Audio/Output/OutputCoreAudio.h b/Audio/Output/OutputCoreAudio.h index cc56df1a4..ef4f49b92 100644 --- a/Audio/Output/OutputCoreAudio.h +++ b/Audio/Output/OutputCoreAudio.h @@ -19,8 +19,6 @@ #import "Semaphore.h" -#import "Downmix.h" - //#define OUTPUT_LOG #ifdef OUTPUT_LOG #import @@ -44,9 +42,6 @@ BOOL eqEnabled; - BOOL streamFormatSetup; - - atomic_long bytesBuffered; atomic_long bytesRendered; atomic_long bytesHdcdSustained; @@ -59,19 +54,12 @@ AudioDeviceID outputDeviceID; AudioStreamBasicDescription deviceFormat; // info about the default device - AudioStreamBasicDescription outerStreamFormat; // this is set when the buffer changes - AudioStreamBasicDescription streamFormat; // this is set when the output callback gets it AUAudioUnit *_au; size_t _bufferSize; AudioUnit _eq; - - DownmixProcessor *downmixer; - void * savedBuffer; - size_t savedSize; - size_t savedMaxSize; #ifdef OUTPUT_LOG FILE *_logFile; #endif diff --git a/Audio/Output/OutputCoreAudio.m b/Audio/Output/OutputCoreAudio.m index 23f235791..4d29201ff 100644 --- a/Audio/Output/OutputCoreAudio.m +++ b/Audio/Output/OutputCoreAudio.m @@ -83,132 +83,45 @@ static OSStatus renderCallback( void *inRefCon, AudioUnitRenderActionFlags *ioAc return 0; } - if (_self->savedSize) { - int readBytes = (int) _self->savedSize; - - const int streamchannels = _self->streamFormat.mChannelsPerFrame; - const int streamBytesPerPacket = streamchannels * sizeof(float); - - int samplesToRead = readBytes / streamBytesPerPacket; - - if (samplesToRead > (amountToRead / bytesPerPacket)) - samplesToRead = amountToRead / bytesPerPacket; - - readBytes = samplesToRead * streamBytesPerPacket; - - atomic_fetch_sub(&_self->bytesBuffered, readBytes); - - float downmixBuffer[samplesToRead * channels]; - [_self->downmixer process:_self->savedBuffer frameCount:samplesToRead output:downmixBuffer]; - fillBuffers(ioData, downmixBuffer, samplesToRead, 0); - amountRead += samplesToRead * bytesPerPacket; - [_self->outputController incrementAmountPlayed:samplesToRead * bytesPerPacket]; - atomic_fetch_add(&_self->bytesRendered, samplesToRead * bytesPerPacket); - [_self->writeSemaphore signal]; - - if (_self->savedSize > readBytes) { - _self->savedSize -= readBytes; - memmove(_self->savedBuffer, _self->savedBuffer + readBytes, _self->savedSize); - } - else { - _self->savedSize = 0; - } - } + void * readPtr; + int toRead = [[_self->outputController buffer] lengthAvailableToReadReturningPointer:&readPtr]; - while (amountRead < amountToRead && [_self->outputController shouldContinue]) + if (toRead > amountToRead) + toRead = amountToRead; + + if (toRead) { + fillBuffers(ioData, (float*)readPtr, toRead / bytesPerPacket, 0); + amountRead = toRead; + [[_self->outputController buffer] didReadLength:toRead]; + [_self->outputController incrementAmountPlayed:amountRead]; + atomic_fetch_add(&_self->bytesRendered, amountRead); + [_self->writeSemaphore signal]; + } + else + [[_self->outputController buffer] didReadLength:0]; + + // Try repeatedly! Buffer wraps can cause a slight data shortage, as can + // unexpected track changes. + while ((amountRead < amountToRead) && [_self->outputController shouldContinue] == YES) { - void * readPtr; - int toRead = 0; - do { - toRead = [[_self->outputController buffer] lengthAvailableToReadReturningPointer:&readPtr]; - if (toRead && *((uint8_t*)readPtr) == 0xFF) { - size_t toSkip = 0; - while (toRead && *((uint8_t*)readPtr) == 0xFF) { - toSkip++; - readPtr++; - toRead--; - } - [[_self->outputController buffer] didReadLength:(int)toSkip]; - toRead = 0; - } - } - while (!toRead); + int amountRead2; //Use this since return type of readdata isnt known...may want to fix then can do a simple += to readdata + amountRead2 = [[_self->outputController buffer] lengthAvailableToReadReturningPointer:&readPtr]; + if (amountRead2 > (amountToRead - amountRead)) + amountRead2 = amountToRead - amountRead; + if (amountRead2) { + atomic_fetch_add(&_self->bytesRendered, amountRead2); + fillBuffers(ioData, (float*)readPtr, amountRead2 / bytesPerPacket, amountRead / bytesPerPacket); + [[_self->outputController buffer] didReadLength:amountRead2]; - int bytesRead = 0; - - int32_t chunkId = -1; - - if (toRead >= 4) { - memcpy(&chunkId, readPtr, 4); - readPtr += 4; - toRead -= 4; - bytesRead += 4; - } - - if (chunkId == 1 && toRead >= sizeof(AudioStreamBasicDescription)) { - AudioStreamBasicDescription inf; - memcpy(&inf, readPtr, sizeof(inf)); - readPtr += sizeof(inf); - toRead -= sizeof(inf); - bytesRead += sizeof(inf); + [_self->outputController incrementAmountPlayed:amountRead2]; - if (!_self->streamFormatSetup || memcmp(&inf, &_self->streamFormat, sizeof(inf)) != 0) { - _self->streamFormatSetup = YES; - _self->streamFormat = inf; - _self->downmixer = [[DownmixProcessor alloc] initWithInputFormat:inf andOutputFormat:_self->deviceFormat]; - } - - if (toRead >= 4) { - memcpy(&chunkId, readPtr, 4); - readPtr += 4; - toRead -= 4; - bytesRead += 4; - } - else chunkId = -1; - } - - const int streamchannels = _self->streamFormat.mChannelsPerFrame; - const int streamBytesPerPacket = streamchannels * sizeof(float); - - if (chunkId == 0 && toRead >= 4) { - memcpy(&chunkId, readPtr, 4); - readPtr += 4; - bytesRead += 4; - toRead = chunkId; - } - - if (toRead) { - size_t samplesToRead = toRead / streamBytesPerPacket; - size_t saveBytes = 0; - - if (samplesToRead * bytesPerPacket > (amountToRead - amountRead)) { - size_t shortToRead = (amountToRead - amountRead) / bytesPerPacket; - saveBytes = (samplesToRead - shortToRead) * streamBytesPerPacket; - samplesToRead = shortToRead; - } - float downmixBuffer[samplesToRead * channels]; - [_self->downmixer process:readPtr frameCount:samplesToRead output:downmixBuffer]; - fillBuffers(ioData, downmixBuffer, samplesToRead, amountRead / bytesPerPacket); - amountRead += samplesToRead * bytesPerPacket; - bytesRead += toRead; - - if (saveBytes) { - if (!_self->savedBuffer || _self->savedMaxSize < saveBytes) { - _self->savedBuffer = realloc(_self->savedBuffer, _self->savedMaxSize = saveBytes * 3); - } - _self->savedSize = saveBytes; - memcpy(_self->savedBuffer, readPtr + toRead - saveBytes, saveBytes); - } - - atomic_fetch_sub(&_self->bytesBuffered, toRead - saveBytes); - - [[_self->outputController buffer] didReadLength:bytesRead]; - [_self->outputController incrementAmountPlayed:samplesToRead * bytesPerPacket]; - atomic_fetch_add(&_self->bytesRendered, samplesToRead * bytesPerPacket); + amountRead += amountRead2; [_self->writeSemaphore signal]; } - else - [[_self->outputController buffer] didReadLength:bytesRead]; + else { + [[_self->outputController buffer] didReadLength:0]; + [_self->readSemaphore timedWait:500]; + } } float volumeScale = 1.0; @@ -225,6 +138,14 @@ static OSStatus renderCallback( void *inRefCon, AudioUnitRenderActionFlags *ioAc scaleBuffersByVolume(ioData, _self->volume * volumeScale); + if (amountRead < amountToRead) + { + // Either underrun, or no data at all. Caller output tends to just + // buffer loop if it doesn't get anything, so always produce a full + // buffer, and silence anything we couldn't supply. + clearBuffers(ioData, (amountToRead - amountRead) / bytesPerPacket, amountRead / bytesPerPacket); + } + return 0; }; @@ -244,15 +165,6 @@ static OSStatus renderCallback( void *inRefCon, AudioUnitRenderActionFlags *ioAc started = NO; stopNext = NO; - streamFormatSetup = NO; - - downmixer = nil; - - savedBuffer = NULL; - savedSize = 0; - savedMaxSize = 0; - - atomic_init(&bytesBuffered, 0); atomic_init(&bytesRendered, 0); atomic_init(&bytesHdcdSustained, 0); @@ -312,7 +224,6 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const if ([outputController shouldReset]) { [[outputController buffer] empty]; - atomic_store(&bytesBuffered, 0); [outputController setShouldReset:NO]; [delayedEvents removeAllObjects]; delayedEventsPopped = YES; @@ -333,73 +244,13 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const break; void *writePtr; - BOOL wrapped = NO; - int toWrite = [[outputController buffer] lengthAvailableToWriteReturningPointer:&writePtr bufferWrapped:&wrapped]; - int bytesWritten = 0; - if (toWrite >= 4 + sizeof(AudioStreamBasicDescription)) { - if ([outputController formatChanged]) { - int32_t chunkId = 1; // ASBD - memcpy(writePtr, &chunkId, 4); - - writePtr += 4; - toWrite -= 4; - bytesWritten += 4; - - AudioStreamBasicDescription inf = [outputController nodeFormat]; - - outerStreamFormat = inf; - - memcpy(writePtr, &inf, sizeof(inf)); - - writePtr += sizeof(inf); - toWrite -= sizeof(inf); - bytesWritten += sizeof(inf); - } - } - [[outputController buffer] didWriteLength:bytesWritten]; - - toWrite = [[outputController buffer] lengthAvailableToWriteReturningPointer:&writePtr bufferWrapped:&wrapped]; + int toWrite = [[outputController buffer] lengthAvailableToWriteReturningPointer:&writePtr]; int bytesRead = 0; - bytesWritten = 0; - if (toWrite >= 4 + 4 + 512 * outerStreamFormat.mBytesPerPacket) { - uint8_t buffer[512 * outerStreamFormat.mBytesPerPacket]; - - bytesRead = [outputController readData:buffer amount:(int)sizeof(buffer)]; - - while (bytesRead < sizeof(buffer) && ![outputController endOfStream]) { - int bytesRead2 = [outputController readData:buffer + bytesRead amount:(int)(sizeof(buffer) - bytesRead)]; - bytesRead += bytesRead2; - } - - int32_t chunkId = 0; // audio data - memcpy(writePtr, &chunkId, 4); - writePtr += 4; - toWrite -= 4; - bytesWritten += 4; - - chunkId = bytesRead; - memcpy(writePtr, &chunkId, 4); - writePtr += 4; - toWrite -= 4; - bytesWritten += 4; - - memcpy(writePtr, buffer, bytesRead); - writePtr += bytesRead; - toWrite -= bytesRead; - bytesWritten += bytesRead; - - atomic_fetch_add(&bytesBuffered, bytesRead); - - [[outputController buffer] didWriteLength:bytesWritten]; - } - else if (wrapped && toWrite > 0) { - memset(writePtr, 0xFF, toWrite); - [[outputController buffer] didWriteLength:toWrite]; - } - else if (toWrite) { - [[outputController buffer] didWriteLength:0]; - toWrite = 0; - } + if (toWrite > CHUNK_SIZE) + toWrite = CHUNK_SIZE; + if (toWrite) + bytesRead = [outputController readData:writePtr amount:toWrite]; + [[outputController buffer] didWriteLength:bytesRead]; if (bytesRead) { [readSemaphore signal]; continue; @@ -419,20 +270,20 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const // End of input possibly reached if (delayedEventsPopped && [outputController endOfStream] == YES) { - long _bytesBuffered = atomic_load_explicit(&bytesBuffered, memory_order_relaxed) * deviceFormat.mBytesPerPacket / outerStreamFormat.mBytesPerPacket; - _bytesBuffered += atomic_load_explicit(&bytesRendered, memory_order_relaxed); + long bytesBuffered = [[outputController buffer] bufferedLength]; + bytesBuffered += atomic_load_explicit(&bytesRendered, memory_order_relaxed); if ([outputController chainQueueHasTracks]) { - if (_bytesBuffered < CHUNK_SIZE / 2) - _bytesBuffered = 0; + if (bytesBuffered < CHUNK_SIZE / 2) + bytesBuffered = 0; else - _bytesBuffered -= CHUNK_SIZE / 2; + bytesBuffered -= CHUNK_SIZE / 2; } else { stopNext = YES; break; } - [delayedEvents addObject:[NSNumber numberWithLong:_bytesBuffered]]; + [delayedEvents addObject:[NSNumber numberWithLong:bytesBuffered]]; delayedEventsPopped = NO; if (!started) { started = YES; @@ -626,9 +477,8 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const NSError *err; AVAudioFormat *renderFormat; - [outputController incrementAmountPlayed:atomic_load_explicit(&bytesBuffered, memory_order_relaxed)]; + [outputController incrementAmountPlayed:[[outputController buffer] bufferedLength]]; [[outputController buffer] empty]; - atomic_store(&bytesBuffered, 0); _deviceFormat = format; deviceFormat = *(format.streamDescription); @@ -712,12 +562,6 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const paused = NO; stopNext = NO; outputDeviceID = -1; - - streamFormatSetup = NO; - - savedBuffer = NULL; - savedSize = 0; - savedMaxSize = 0; AudioComponentDescription desc; NSError *err; @@ -824,8 +668,6 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const return 0; }; - - [_au setMaximumFramesToRender:512]; UInt32 value; UInt32 size = sizeof(value); @@ -946,14 +788,6 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const _logFile = NULL; } #endif - if (savedBuffer) - { - free(savedBuffer); - savedBuffer = NULL; - savedSize = 0; - savedMaxSize = 0; - } - outputController = nil; } diff --git a/Audio/ThirdParty/VirtualRingBuffer/VirtualRingBuffer.h b/Audio/ThirdParty/VirtualRingBuffer/VirtualRingBuffer.h index 790fc9ebc..d185394b0 100644 --- a/Audio/ThirdParty/VirtualRingBuffer/VirtualRingBuffer.h +++ b/Audio/ThirdParty/VirtualRingBuffer/VirtualRingBuffer.h @@ -83,7 +83,7 @@ // Write operations: // The writing thread must call this method first. -- (UInt32)lengthAvailableToWriteReturningPointer:(void **)returnedWritePointer bufferWrapped:(BOOL*)wrapped; +- (UInt32)lengthAvailableToWriteReturningPointer:(void **)returnedWritePointer; // Iff a value > 0 is returned, the writing thread may then write that much data into the returned pointer. // Afterwards, the writing thread must call didWriteLength:. - (void)didWriteLength:(UInt32)length; diff --git a/Audio/ThirdParty/VirtualRingBuffer/VirtualRingBuffer.m b/Audio/ThirdParty/VirtualRingBuffer/VirtualRingBuffer.m index b7b01cc0c..98bd34a0e 100644 --- a/Audio/ThirdParty/VirtualRingBuffer/VirtualRingBuffer.m +++ b/Audio/ThirdParty/VirtualRingBuffer/VirtualRingBuffer.m @@ -140,7 +140,7 @@ static void deallocateVirtualBuffer(void *buffer, UInt32 bufferLength); // Write operations // -- (UInt32)lengthAvailableToWriteReturningPointer:(void **)returnedWritePointer bufferWrapped:(BOOL *)wrapped +- (UInt32)lengthAvailableToWriteReturningPointer:(void **)returnedWritePointer { // Assumptions: // returnedWritePointer != NULL @@ -152,10 +152,8 @@ static void deallocateVirtualBuffer(void *buffer, UInt32 bufferLength); int localBufferFilled = atomic_load_explicit(&bufferFilled, memory_order_relaxed); length = bufferLength - localBufferFilled; - if (length >= bufferLength - localWritePointer) { - *wrapped = YES; + if (length > bufferLength - localWritePointer) length = bufferLength - localWritePointer; - } *returnedWritePointer = buffer + localWritePointer;