diff --git a/Audio/AudioPlayer.m b/Audio/AudioPlayer.m index f9fb3309e..41637ea10 100644 --- a/Audio/AudioPlayer.m +++ b/Audio/AudioPlayer.m @@ -83,7 +83,7 @@ bufferChain = [[BufferChain alloc] initWithController:self]; [self notifyStreamChanged:userInfo]; - while(![bufferChain open:url withOutputFormat:[output format] withRGInfo:rgi]) { + while(![bufferChain open:url withOutputFormat:[output format] withOutputConfig:[output config] withRGInfo:rgi]) { bufferChain = nil; [self requestNextStream:userInfo]; @@ -353,7 +353,7 @@ } if(pathsEqual || ([[nextStream scheme] isEqualToString:[[lastChain streamURL] scheme]] && (([nextStream host] == nil && [[lastChain streamURL] host] == nil) || [[nextStream host] isEqualToString:[[lastChain streamURL] host]]) && [[nextStream path] isEqualToString:[[lastChain streamURL] path]])) { - if([lastChain setTrack:nextStream] && [newChain openWithInput:[lastChain inputNode] withOutputFormat:[output format] withRGInfo:nextStreamRGInfo]) { + if([lastChain setTrack:nextStream] && [newChain openWithInput:[lastChain inputNode] withOutputFormat:[output format] withOutputConfig:[output config] withRGInfo:nextStreamRGInfo]) { [newChain setStreamURL:nextStream]; [newChain setUserInfo:nextStreamUserInfo]; @@ -369,7 +369,7 @@ lastChain = nil; - while(shouldContinue && ![newChain open:nextStream withOutputFormat:[output format] withRGInfo:nextStreamRGInfo]) { + while(shouldContinue && ![newChain open:nextStream withOutputFormat:[output format] withOutputConfig:[output config] withRGInfo:nextStreamRGInfo]) { if(nextStream == nil) { newChain = nil; atomic_fetch_sub(&refCount, 1); diff --git a/Audio/Chain/AudioChunk.h b/Audio/Chain/AudioChunk.h index 54275549e..29fce39e5 100644 --- a/Audio/Chain/AudioChunk.h +++ b/Audio/Chain/AudioChunk.h @@ -13,16 +13,72 @@ NS_ASSUME_NONNULL_BEGIN +enum { + AudioChannelFrontLeft = 1 << 0, + AudioChannelFrontRight = 1 << 1, + AudioChannelFrontCenter = 1 << 2, + AudioChannelLFE = 1 << 3, + AudioChannelBackLeft = 1 << 4, + AudioChannelBackRight = 1 << 5, + AudioChannelFrontCenterLeft = 1 << 6, + AudioChannelFrontCenterRight = 1 << 7, + AudioChannelBackCenter = 1 << 8, + AudioChannelSideLeft = 1 << 9, + AudioChannelSideRight = 1 << 10, + AudioChannelTopCenter = 1 << 11, + AudioChannelTopFrontLeft = 1 << 12, + AudioChannelTopFrontCenter = 1 << 13, + AudioChannelTopFrontRight = 1 << 14, + AudioChannelTopBackLeft = 1 << 15, + AudioChannelTopBackCenter = 1 << 16, + AudioChannelTopBackRight = 1 << 17, + + AudioConfigMono = AudioChannelFrontCenter, + AudioConfigStereo = AudioChannelFrontLeft | AudioChannelFrontRight, + AudioConfig3Point0 = AudioChannelFrontLeft | AudioChannelFrontRight | + AudioChannelFrontCenter, + AudioConfig4Point0 = AudioChannelFrontLeft | AudioChannelFrontRight | + AudioChannelBackLeft | AudioChannelBackRight, + AudioConfig5Point0 = AudioChannelFrontLeft | AudioChannelFrontRight | + AudioChannelFrontCenter | AudioChannelBackLeft | + AudioChannelBackRight, + AudioConfig5Point1 = AudioChannelFrontLeft | AudioChannelFrontRight | + AudioChannelFrontCenter | AudioChannelLFE | + AudioChannelBackLeft | AudioChannelBackRight, + AudioConfig5Point1Side = AudioChannelFrontLeft | AudioChannelFrontRight | + AudioChannelFrontCenter | AudioChannelLFE | + AudioChannelSideLeft | AudioChannelSideRight, + AudioConfig6Point1 = AudioChannelFrontLeft | AudioChannelFrontRight | + AudioChannelFrontCenter | AudioChannelLFE | + AudioChannelBackCenter | AudioChannelSideLeft | + AudioChannelSideRight, + AudioConfig7Point1 = AudioChannelFrontLeft | AudioChannelFrontRight | + AudioChannelFrontCenter | AudioChannelLFE | + AudioChannelBackLeft | AudioChannelBackRight | + AudioChannelSideLeft | AudioChannelSideRight, + + AudioChannelsBackLeftRight = AudioChannelBackLeft | AudioChannelBackRight, + AudioChannelsSideLeftRight = AudioChannelSideLeft | AudioChannelSideRight, +}; + @interface AudioChunk : NSObject { AudioStreamBasicDescription format; NSMutableData *chunkData; + uint32_t channelConfig; BOOL formatAssigned; BOOL lossless; } @property AudioStreamBasicDescription format; +@property uint32_t channelConfig; @property BOOL lossless; ++ (uint32_t)guessChannelConfig:(uint32_t)channelCount; ++ (uint32_t)channelIndexFromConfig:(uint32_t)channelConfig forFlag:(uint32_t)flag; ++ (uint32_t)extractChannelFlag:(uint32_t)index fromConfig:(uint32_t)channelConfig; ++ (uint32_t)countChannels:(uint32_t)channelConfig; ++ (uint32_t)findChannelIndex:(uint32_t)flag; + - (id)init; - (void)assignSamples:(const void *)data frameCount:(size_t)count; diff --git a/Audio/Chain/AudioChunk.m b/Audio/Chain/AudioChunk.m index 25b44df31..05130c506 100644 --- a/Audio/Chain/AudioChunk.m +++ b/Audio/Chain/AudioChunk.m @@ -21,6 +21,86 @@ return self; } +static const uint32_t AudioChannelConfigTable[] = { + 0, + AudioConfigMono, + AudioConfigStereo, + AudioConfig3Point0, + AudioConfig4Point0, + AudioConfig5Point0, + AudioConfig5Point1, + AudioConfig6Point1, + AudioConfig7Point1, + 0, + AudioConfig7Point1 | AudioChannelFrontCenterLeft | AudioChannelFrontCenterRight +}; + ++ (uint32_t)guessChannelConfig:(uint32_t)channelCount { + if(channelCount == 0) return 0; + if(channelCount > 32) return 0; + int ret = 0; + if(channelCount < (sizeof(AudioChannelConfigTable) / sizeof(AudioChannelConfigTable[0]))) + ret = AudioChannelConfigTable[channelCount]; + if(!ret) { + ret = (1 << channelCount) - 1; + } + assert([AudioChunk countChannels:ret] == channelCount); + return ret; +} + ++ (uint32_t)channelIndexFromConfig:(uint32_t)channelConfig forFlag:(uint32_t)flag { + uint32_t index = 0; + for(uint32_t walk = 0; walk < 32; ++walk) { + uint32_t query = 1 << walk; + if(flag & query) return index; + if(channelConfig & query) ++index; + } + return ~0; +} + ++ (uint32_t)extractChannelFlag:(uint32_t)index fromConfig:(uint32_t)channelConfig { + uint32_t toskip = index; + uint32_t flag = 1; + while(flag) { + if(channelConfig & flag) { + if(toskip == 0) break; + toskip--; + } + flag <<= 1; + } + return flag; +} + ++ (uint32_t)countChannels:(uint32_t)channelConfig { + return __builtin_popcount(channelConfig); +} + ++ (uint32_t)findChannelIndex:(uint32_t)flag { + uint32_t rv = 0; + if((flag & 0xFFFF) == 0) { + rv += 16; + flag >>= 16; + } + if((flag & 0xFF) == 0) { + rv += 8; + flag >>= 8; + } + if((flag & 0xF) == 0) { + rv += 4; + flag >>= 4; + } + if((flag & 0x3) == 0) { + rv += 2; + flag >>= 2; + } + if((flag & 0x1) == 0) { + rv += 1; + flag >>= 1; + } + assert(flag & 1); + return rv; +} + @synthesize lossless; - (AudioStreamBasicDescription)format { @@ -30,6 +110,20 @@ - (void)setFormat:(AudioStreamBasicDescription)informat { formatAssigned = YES; format = informat; + channelConfig = [AudioChunk guessChannelConfig:format.mChannelsPerFrame]; +} + +- (uint32_t)channelConfig { + return channelConfig; +} + +- (void)setChannelConfig:(uint32_t)config { + if(formatAssigned) { + if(config == 0) { + config = [AudioChunk guessChannelConfig:format.mChannelsPerFrame]; + } + } + channelConfig = config; } - (void)assignSamples:(const void *)data frameCount:(size_t)count { diff --git a/Audio/Chain/BufferChain.h b/Audio/Chain/BufferChain.h index fcd95082a..2aa09ecff 100644 --- a/Audio/Chain/BufferChain.h +++ b/Audio/Chain/BufferChain.h @@ -17,6 +17,7 @@ ConverterNode *converterNode; AudioStreamBasicDescription inputFormat; + uint32_t inputChannelConfig; NSURL *streamURL; id userInfo; @@ -30,14 +31,15 @@ - (id)initWithController:(id)c; - (void)buildChain; -- (BOOL)open:(NSURL *)url withOutputFormat:(AudioStreamBasicDescription)outputFormat withRGInfo:(NSDictionary *)rgi; +- (BOOL)open:(NSURL *)url withOutputFormat:(AudioStreamBasicDescription)outputFormat withOutputConfig:(uint32_t)outputConfig withRGInfo:(NSDictionary *)rgi; // Used when changing tracks to reuse the same decoder -- (BOOL)openWithInput:(InputNode *)i withOutputFormat:(AudioStreamBasicDescription)outputFormat withRGInfo:(NSDictionary *)rgi; +- (BOOL)openWithInput:(InputNode *)i withOutputFormat:(AudioStreamBasicDescription)outputFormat withOutputConfig:(uint32_t)outputConfig withRGInfo:(NSDictionary *)rgi; // Used when resetting the decoder on seek - (BOOL)openWithDecoder:(id)decoder withOutputFormat:(AudioStreamBasicDescription)outputFormat + withOutputConfig:(uint32_t)outputConfig withRGInfo:(NSDictionary *)rgi; - (void)seek:(double)time; @@ -64,7 +66,7 @@ - (BOOL)endOfInputReached; - (BOOL)setTrack:(NSURL *)track; -- (void)inputFormatDidChange:(AudioStreamBasicDescription)format; +- (void)inputFormatDidChange:(AudioStreamBasicDescription)format inputConfig:(uint32_t)inputConfig; - (BOOL)isRunning; @@ -72,6 +74,7 @@ - (ConverterNode *)converter; - (AudioStreamBasicDescription)inputFormat; +- (uint32_t)inputConfig; - (double)secondsBuffered; diff --git a/Audio/Chain/BufferChain.m b/Audio/Chain/BufferChain.m index bf6c6060a..579d43f76 100644 --- a/Audio/Chain/BufferChain.m +++ b/Audio/Chain/BufferChain.m @@ -40,7 +40,7 @@ finalNode = converterNode; } -- (BOOL)open:(NSURL *)url withOutputFormat:(AudioStreamBasicDescription)outputFormat withRGInfo:(NSDictionary *)rgi { +- (BOOL)open:(NSURL *)url withOutputFormat:(AudioStreamBasicDescription)outputFormat withOutputConfig:(uint32_t)outputConfig withRGInfo:(NSDictionary *)rgi { [self setStreamURL:url]; [self buildChain]; @@ -61,12 +61,16 @@ NSDictionary *properties = [inputNode properties]; inputFormat = [inputNode nodeFormat]; + if([properties valueForKey:@"channelConfig"]) + inputChannelConfig = [[properties valueForKey:@"channelConfig"] intValue]; 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"]]) + outputConfig = inputChannelConfig; + + if(![converterNode setupWithInputFormat:inputFormat withInputConfig:inputChannelConfig outputFormat:outputFormat outputConfig:outputConfig isLossless:[[properties valueForKey:@"encoding"] isEqualToString:@"lossless"]]) return NO; [self setRGInfo:rgi]; @@ -76,7 +80,7 @@ return YES; } -- (BOOL)openWithInput:(InputNode *)i withOutputFormat:(AudioStreamBasicDescription)outputFormat withRGInfo:(NSDictionary *)rgi { +- (BOOL)openWithInput:(InputNode *)i withOutputFormat:(AudioStreamBasicDescription)outputFormat withOutputConfig:(uint32_t)outputConfig withRGInfo:(NSDictionary *)rgi { DLog(@"New buffer chain!"); [self buildChain]; @@ -86,13 +90,17 @@ NSDictionary *properties = [inputNode properties]; inputFormat = [inputNode nodeFormat]; + if([properties valueForKey:@"channelConfig"]) + inputChannelConfig = [[properties valueForKey:@"channelConfig"] intValue]; outputFormat.mChannelsPerFrame = inputFormat.mChannelsPerFrame; outputFormat.mBytesPerFrame = ((outputFormat.mBitsPerChannel + 7) / 8) * outputFormat.mChannelsPerFrame; outputFormat.mBytesPerPacket = outputFormat.mBytesPerFrame * outputFormat.mFramesPerPacket; + outputConfig = inputChannelConfig; + DLog(@"Input Properties: %@", properties); - if(![converterNode setupWithInputFormat:inputFormat outputFormat:outputFormat isLossless:[[properties objectForKey:@"encoding"] isEqualToString:@"lossless"]]) + if(![converterNode setupWithInputFormat:inputFormat withInputConfig:inputChannelConfig outputFormat:outputFormat outputConfig:outputConfig isLossless:[[properties objectForKey:@"encoding"] isEqualToString:@"lossless"]]) return NO; [self setRGInfo:rgi]; @@ -102,6 +110,7 @@ - (BOOL)openWithDecoder:(id)decoder withOutputFormat:(AudioStreamBasicDescription)outputFormat + withOutputConfig:(uint32_t)outputConfig withRGInfo:(NSDictionary *)rgi; { DLog(@"New buffer chain!"); @@ -115,12 +124,16 @@ DLog(@"Input Properties: %@", properties); inputFormat = [inputNode nodeFormat]; + if([properties valueForKey:@"channelConfig"]) + inputChannelConfig = [[properties valueForKey:@"channelConfig"] intValue]; 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"]]) + outputConfig = inputChannelConfig; + + if(![converterNode setupWithInputFormat:inputFormat withInputConfig:inputChannelConfig outputFormat:outputFormat outputConfig:outputConfig isLossless:[[properties objectForKey:@"encoding"] isEqualToString:@"lossless"]]) return NO; [self setRGInfo:rgi]; @@ -180,7 +193,7 @@ [controller launchOutputThread]; } -- (void)inputFormatDidChange:(AudioStreamBasicDescription)format { +- (void)inputFormatDidChange:(AudioStreamBasicDescription)format inputConfig:(uint32_t)inputConfig { DLog(@"FORMAT DID CHANGE!"); } @@ -225,6 +238,10 @@ return inputFormat; } +- (uint32_t)inputConfig { + return inputChannelConfig; +} + - (double)secondsBuffered { double duration = 0.0; OutputNode *outputNode = (OutputNode *)[controller output]; diff --git a/Audio/Chain/ChunkList.m b/Audio/Chain/ChunkList.m index f3a1773aa..0b7c854bb 100644 --- a/Audio/Chain/ChunkList.m +++ b/Audio/Chain/ChunkList.m @@ -86,6 +86,7 @@ NSData *removedData = [chunk removeSamples:maxFrameCount]; AudioChunk *ret = [[AudioChunk alloc] init]; [ret setFormat:[chunk format]]; + [ret setChannelConfig:[chunk channelConfig]]; [ret assignSamples:[removedData bytes] frameCount:maxFrameCount]; listDuration -= [ret duration]; inRemover = NO; diff --git a/Audio/Chain/ConverterNode.h b/Audio/Chain/ConverterNode.h index 1bd5d952a..f8ed72758 100644 --- a/Audio/Chain/ConverterNode.h +++ b/Audio/Chain/ConverterNode.h @@ -68,8 +68,13 @@ AudioStreamBasicDescription dmFloatFormat; // downmixed/upmixed float format AudioStreamBasicDescription outputFormat; + uint32_t inputChannelConfig; + uint32_t outputChannelConfig; + AudioStreamBasicDescription previousOutputFormat; + uint32_t previousOutputConfig; AudioStreamBasicDescription rememberedInputFormat; + uint32_t rememberedInputConfig; RefillNode *refillNode; id __weak originalPreviousNode; @@ -82,7 +87,7 @@ - (id)initWithController:(id)c previous:(id)p; -- (BOOL)setupWithInputFormat:(AudioStreamBasicDescription)inputFormat outputFormat:(AudioStreamBasicDescription)outputFormat isLossless:(BOOL)lossless; +- (BOOL)setupWithInputFormat:(AudioStreamBasicDescription)inputFormat withInputConfig:(uint32_t)inputConfig outputFormat:(AudioStreamBasicDescription)outputFormat outputConfig:(uint32_t)outputConfig isLossless:(BOOL)lossless; - (void)cleanUp; - (void)process; @@ -90,9 +95,9 @@ - (void)setRGInfo:(NSDictionary *)rgi; -- (void)setOutputFormat:(AudioStreamBasicDescription)format; +- (void)setOutputFormat:(AudioStreamBasicDescription)format outputConfig:(uint32_t)outputConfig; -- (void)inputFormatDidChange:(AudioStreamBasicDescription)format; +- (void)inputFormatDidChange:(AudioStreamBasicDescription)format inputConfig:(uint32_t)inputConfig; - (void)refreshVolumeScaling; diff --git a/Audio/Chain/ConverterNode.m b/Audio/Chain/ConverterNode.m index 3f704b084..21c825deb 100644 --- a/Audio/Chain/ConverterNode.m +++ b/Audio/Chain/ConverterNode.m @@ -435,7 +435,7 @@ static void convert_be_to_le(uint8_t *buffer, size_t bitsPerSample, size_t bytes [self setShouldContinue:YES]; refillNode = nil; [self cleanUp]; - [self setupWithInputFormat:rememberedInputFormat outputFormat:outputFormat isLossless:rememberedLossless]; + [self setupWithInputFormat:rememberedInputFormat withInputConfig:rememberedInputConfig outputFormat:outputFormat outputConfig:outputChannelConfig isLossless:rememberedLossless]; continue; } else break; @@ -843,12 +843,16 @@ static float db_to_scale(float db) { volumeScale = scale; } -- (BOOL)setupWithInputFormat:(AudioStreamBasicDescription)inf outputFormat:(AudioStreamBasicDescription)outf isLossless:(BOOL)lossless { +- (BOOL)setupWithInputFormat:(AudioStreamBasicDescription)inf withInputConfig:(uint32_t)inputConfig outputFormat:(AudioStreamBasicDescription)outf outputConfig:(uint32_t)outputConfig isLossless:(BOOL)lossless { // Make the converter inputFormat = inf; outputFormat = outf; + inputChannelConfig = inputConfig; + outputChannelConfig = outputConfig; + nodeFormat = outputFormat; + nodeChannelConfig = outputChannelConfig; rememberedLossless = lossless; @@ -954,26 +958,31 @@ static float db_to_scale(float db) { [self cleanUp]; } -- (void)setOutputFormat:(AudioStreamBasicDescription)format { +- (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 { +- (void)inputFormatDidChange:(AudioStreamBasicDescription)format inputConfig:(uint32_t)inputConfig { DLog(@"FORMAT CHANGED"); paused = YES; [self cleanUp]; if(outputFormatChanged && ![buffer isEmpty] && - memcmp(&outputFormat, &previousOutputFormat, sizeof(outputFormat)) != 0) { + (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(;;) { AudioChunk *chunk = [buffer removeSamples:16384]; @@ -985,9 +994,9 @@ static float db_to_scale(float db) { break; } - [self setupWithInputFormat:previousOutputFormat outputFormat:outputFormat isLossless:rememberedLossless]; + [self setupWithInputFormat:previousOutputFormat withInputConfig:[AudioChunk guessChannelConfig:previousOutputFormat.mChannelsPerFrame] outputFormat:outputFormat outputConfig:outputChannelConfig isLossless:rememberedLossless]; } else { - [self setupWithInputFormat:format outputFormat:outputFormat isLossless:rememberedLossless]; + [self setupWithInputFormat:format withInputConfig:inputConfig outputFormat:outputFormat outputConfig:outputChannelConfig isLossless:rememberedLossless]; } } diff --git a/Audio/Chain/Downmix.h b/Audio/Chain/Downmix.h index a95c45e61..f48e7e33d 100644 --- a/Audio/Chain/Downmix.h +++ b/Audio/Chain/Downmix.h @@ -16,9 +16,12 @@ AudioStreamBasicDescription inputFormat; AudioStreamBasicDescription outputFormat; + + uint32_t inConfig; + uint32_t outConfig; } -- (id)initWithInputFormat:(AudioStreamBasicDescription)inf andOutputFormat:(AudioStreamBasicDescription)outf; +- (id)initWithInputFormat:(AudioStreamBasicDescription)inf inputConfig:(uint32_t)iConfig andOutputFormat:(AudioStreamBasicDescription)outf outputConfig:(uint32_t)oConfig; - (void)process:(const void *)inBuffer frameCount:(size_t)frames output:(void *)outBuffer; diff --git a/Audio/Chain/Downmix.m b/Audio/Chain/Downmix.m index 4ecf5f7cf..cb7d29572 100644 --- a/Audio/Chain/Downmix.m +++ b/Audio/Chain/Downmix.m @@ -10,74 +10,144 @@ #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 } } -}; +#import "AudioChunk.h" -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]; +static void downmix_to_stereo(const float *inBuffer, int channels, uint32_t config, float *outBuffer, size_t count) { + float FrontRatios[2] = { 0.0F, 0.0F }; + float FrontCenterRatio = 0.0F; + float LFERatio = 0.0F; + float BackRatios[2] = { 0.0F, 0.0F }; + float BackCenterRatio = 0.0F; + float SideRatios[2] = { 0.0F, 0.0F }; + if(config & (AudioChannelFrontLeft | AudioChannelFrontRight)) { + FrontRatios[0] = 1.0F; + } + if(config & AudioChannelFrontCenter) { + FrontRatios[0] = 0.5858F; + FrontCenterRatio = 0.4142F; + } + if(config & (AudioChannelBackLeft | AudioChannelBackRight)) { + if(config & AudioChannelFrontCenter) { + FrontRatios[0] = 0.651F; + FrontCenterRatio = 0.46F; + BackRatios[0] = 0.5636F; + BackRatios[1] = 0.3254F; + } else { + FrontRatios[0] = 0.4226F; + BackRatios[0] = 0.366F; + BackRatios[1] = 0.2114F; + } + } + if(config & AudioChannelLFE) { + FrontRatios[0] *= 0.8F; + FrontCenterRatio *= 0.8F; + LFERatio = FrontCenterRatio; + BackRatios[0] *= 0.8F; + BackRatios[1] *= 0.8F; + } + if(config & AudioChannelBackCenter) { + FrontRatios[0] *= 0.86F; + FrontCenterRatio *= 0.86F; + LFERatio *= 0.86F; + BackRatios[0] *= 0.86F; + BackRatios[1] *= 0.86F; + BackCenterRatio = FrontCenterRatio * 0.86F; + } + if(config & (AudioChannelSideLeft | AudioChannelSideRight)) { + float ratio = 0.73F; + if(config & AudioChannelBackCenter) ratio = 0.85F; + FrontRatios[0] *= ratio; + FrontCenterRatio *= ratio; + LFERatio *= ratio; + BackRatios[0] *= ratio; + BackRatios[1] *= ratio; + BackCenterRatio *= ratio; + SideRatios[0] = 0.463882352941176 * ratio; + SideRatios[1] = 0.267882352941176 * ratio; + } + + int32_t channelIndexes[channels]; + for(int i = 0; i < channels; ++i) { + channelIndexes[i] = [AudioChunk findChannelIndex:[AudioChunk extractChannelFlag:i fromConfig:config]]; + } + + for(size_t i = 0; i < count; ++i) { + float left = 0.0F, right = 0.0F; + for(uint32_t j = 0; j < channels; ++j) { + float inSample = inBuffer[i * channels + j]; + switch(channelIndexes[j]) { + case 0: + left += inSample * FrontRatios[0]; + right += inSample * FrontRatios[1]; + break; + + case 1: + left += inSample * FrontRatios[1]; + right += inSample * FrontRatios[0]; + break; + + case 2: + left += inSample * FrontCenterRatio; + right += inSample * FrontCenterRatio; + break; + + case 3: + left += inSample * LFERatio; + right += inSample * LFERatio; + break; + + case 4: + left += inSample * BackRatios[0]; + right += inSample * BackRatios[1]; + break; + + case 5: + left += inSample * BackRatios[1]; + right += inSample * BackRatios[0]; + break; + + case 6: + case 7: + break; + + case 8: + left += inSample * BackCenterRatio; + right += inSample * BackCenterRatio; + break; + + case 9: + left += inSample * SideRatios[0]; + right += inSample * SideRatios[1]; + break; + + case 10: + left += inSample * SideRatios[1]; + right += inSample * SideRatios[0]; + break; + + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + case 17: + default: + break; } + 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) { +static void downmix_to_mono(const float *inBuffer, int channels, uint32_t config, 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; - } + downmix_to_stereo(inBuffer, channels, config, tempBuffer, count); + inBuffer = tempBuffer; + channels = 2; + config = AudioConfigStereo; float invchannels = 1.0 / (float)channels; for(size_t i = 0; i < count; ++i) { float sample = 0; @@ -88,62 +158,100 @@ static void downmix_to_mono(const float *inBuffer, int channels, float *outBuffe } } -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) { +static void upmix(const float *inBuffer, int inchannels, uint32_t inconfig, float *outBuffer, int outchannels, uint32_t outconfig, size_t count) { + if(inconfig == AudioConfigMono && outconfig == AudioConfigStereo) { + for(size_t i = 0; i < count; ++i) { // upmix mono to stereo float sample = inBuffer[i]; outBuffer[i * 2 + 0] = sample; outBuffer[i * 2 + 1] = sample; - } else if(inchannels == 1 && outchannels == 4) { + } + } else if(inconfig == AudioConfigMono && outconfig == AudioConfig4Point0) { + for(size_t i = 0; i < count; ++i) { // 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)) { + } + } else if(inconfig == AudioConfigMono && (outconfig & AudioChannelFrontCenter)) { + uint32_t cIndex = [AudioChunk channelIndexFromConfig:outconfig forFlag:AudioChannelFrontCenter]; + for(size_t i = 0; i < count; ++i) { // upmix mono to center channel float sample = inBuffer[i]; - outBuffer[i * outchannels + 2] = sample; - for(int j = 0; j < 2; ++j) { + outBuffer[i * outchannels + cIndex] = sample; + for(int j = 0; j < cIndex; ++j) { outBuffer[i * outchannels + j] = 0; } - for(int j = 3; j < outchannels; ++j) { + for(int j = cIndex + 1; j < outchannels; ++j) { outBuffer[i * outchannels + j] = 0; } - } else if(inchannels == 4 && outchannels >= 5) { + } + } else if(inconfig == AudioConfig4Point0 && outchannels >= 5) { + uint32_t flIndex = [AudioChunk channelIndexFromConfig:outconfig forFlag:AudioChannelFrontLeft]; + uint32_t frIndex = [AudioChunk channelIndexFromConfig:outconfig forFlag:AudioChannelFrontRight]; + uint32_t blIndex = [AudioChunk channelIndexFromConfig:outconfig forFlag:AudioChannelBackLeft]; + uint32_t brIndex = [AudioChunk channelIndexFromConfig:outconfig forFlag:AudioChannelBackRight]; + for(size_t i = 0; i < count; ++i) { 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; + memset(outBuffer + i * outchannels, 0, sizeof(float) * outchannels); + if(flIndex != ~0) { + outBuffer[i * outchannels + flIndex] = fl; } - for(int j = 4 + skipclfe; j < outchannels; ++j) { - outBuffer[i * outchannels + j] = 0; + if(frIndex != ~0) { + outBuffer[i * outchannels + frIndex] = fr; } - } else if(inchannels == 5 && outchannels >= 6) { + if(blIndex != ~0) { + outBuffer[i * outchannels + blIndex] = bl; + } + if(brIndex != ~0) { + outBuffer[i * outchannels + brIndex] = br; + } + } + } else if(inconfig == AudioConfig5Point0 && outchannels >= 6) { + uint32_t flIndex = [AudioChunk channelIndexFromConfig:outconfig forFlag:AudioChannelFrontLeft]; + uint32_t frIndex = [AudioChunk channelIndexFromConfig:outconfig forFlag:AudioChannelFrontRight]; + uint32_t cIndex = [AudioChunk channelIndexFromConfig:outconfig forFlag:AudioChannelFrontCenter]; + uint32_t blIndex = [AudioChunk channelIndexFromConfig:outconfig forFlag:AudioChannelBackLeft]; + uint32_t brIndex = [AudioChunk channelIndexFromConfig:outconfig forFlag:AudioChannelBackRight]; + for(size_t i = 0; i < count; ++i) { 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; + memset(outBuffer + i * outchannels, 0, sizeof(float) * outchannels); + if(flIndex != ~0) { + outBuffer[i * outchannels + flIndex] = fl; } - } else if(inchannels == 7 && outchannels == 8) { + if(frIndex != ~0) { + outBuffer[i * outchannels + frIndex] = fr; + } + if(cIndex != ~0) { + outBuffer[i * outchannels + cIndex] = c; + } + if(blIndex != ~0) { + outBuffer[i * outchannels + blIndex] = bl; + } + if(brIndex != ~0) { + outBuffer[i * outchannels + brIndex] = br; + } + } + } else if(inconfig == AudioConfig6Point1 && outchannels >= 8) { + uint32_t flIndex = [AudioChunk channelIndexFromConfig:outconfig forFlag:AudioChannelFrontLeft]; + uint32_t frIndex = [AudioChunk channelIndexFromConfig:outconfig forFlag:AudioChannelFrontRight]; + uint32_t cIndex = [AudioChunk channelIndexFromConfig:outconfig forFlag:AudioChannelFrontCenter]; + uint32_t lfeIndex = [AudioChunk channelIndexFromConfig:outconfig forFlag:AudioChannelLFE]; + uint32_t blIndex = [AudioChunk channelIndexFromConfig:outconfig forFlag:AudioChannelBackLeft]; + uint32_t brIndex = [AudioChunk channelIndexFromConfig:outconfig forFlag:AudioChannelBackRight]; + uint32_t bcIndex = [AudioChunk channelIndexFromConfig:outconfig forFlag:AudioChannelBackCenter]; + uint32_t slIndex = [AudioChunk channelIndexFromConfig:outconfig forFlag:AudioChannelSideLeft]; + uint32_t srIndex = [AudioChunk channelIndexFromConfig:outconfig forFlag:AudioChannelSideRight]; + for(size_t i = 0; i < count; ++i) { float fl = inBuffer[i * 7 + 0]; float fr = inBuffer[i * 7 + 1]; float c = inBuffer[i * 7 + 2]; @@ -151,25 +259,48 @@ static void upmix(const float *inBuffer, int inchannels, float *outBuffer, int o 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 { + memset(outBuffer + i * outchannels, 0, sizeof(float) * outchannels); + if(flIndex != ~0) { + outBuffer[i * outchannels + flIndex] = fl; + } + if(frIndex != ~0) { + outBuffer[i * outchannels + frIndex] = fr; + } + if(cIndex != ~0) { + outBuffer[i * outchannels + cIndex] = c; + } + if(lfeIndex != ~0) { + outBuffer[i * outchannels + lfeIndex] = lfe; + } + if(slIndex != ~0) { + outBuffer[i * outchannels + slIndex] = sl; + } + if(srIndex != ~0) { + outBuffer[i * outchannels + srIndex] = sr; + } + if(bcIndex != ~0) { + outBuffer[i * outchannels + bcIndex] = bc; + } else { + if(blIndex != ~0) { + outBuffer[i * outchannels + blIndex] = bc; + } + if(brIndex != ~0) { + outBuffer[i * outchannels + brIndex] = bc; + } + } + } + } else { + uint32_t outIndexes[inchannels]; + for(int i = 0; i < inchannels; ++i) { + uint32_t channelFlag = [AudioChunk extractChannelFlag:i fromConfig:inconfig]; + outIndexes[i] = [AudioChunk channelIndexFromConfig:outconfig forFlag:channelFlag]; + } + for(size_t i = 0; i < count; ++i) { // 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; + if(outIndexes[j] != ~0) { + outBuffer[i * outchannels + outIndexes[j]] = inBuffer[i * inchannels + j]; + } } } } @@ -177,7 +308,7 @@ static void upmix(const float *inBuffer, int inchannels, float *outBuffer, int o @implementation DownmixProcessor -- (id)initWithInputFormat:(AudioStreamBasicDescription)inf andOutputFormat:(AudioStreamBasicDescription)outf { +- (id)initWithInputFormat:(AudioStreamBasicDescription)inf inputConfig:(uint32_t)iConfig andOutputFormat:(AudioStreamBasicDescription)outf outputConfig:(uint32_t)oConfig { self = [super init]; if(self) { @@ -198,6 +329,9 @@ static void upmix(const float *inBuffer, int inchannels, float *outBuffer, int o inputFormat = inf; outputFormat = outf; + inConfig = iConfig; + outConfig = oConfig; + [self setupVirt]; [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.headphoneVirtualization" options:0 context:nil]; @@ -221,8 +355,9 @@ static void upmix(const float *inBuffer, int inchannels, float *outBuffer, int o if(hVirt && outputFormat.mChannelsPerFrame == 2 && + outConfig == AudioConfigStereo && inputFormat.mChannelsPerFrame >= 1 && - inputFormat.mChannelsPerFrame <= 8) { + (inConfig & (AudioConfig7Point1 | AudioChannelBackCenter)) != 0) { NSString *userPreset = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] stringForKey:@"hrirPath"]; NSURL *presetUrl = nil; @@ -241,7 +376,7 @@ static void upmix(const float *inBuffer, int inchannels, float *outBuffer, int o if(presetUrl) { @synchronized(hFilter) { - hFilter = [[HeadphoneFilter alloc] initWithImpulseFile:presetUrl forSampleRate:outputFormat.mSampleRate withInputChannels:inputFormat.mChannelsPerFrame]; + hFilter = [[HeadphoneFilter alloc] initWithImpulseFile:presetUrl forSampleRate:outputFormat.mSampleRate withInputChannels:inputFormat.mChannelsPerFrame withConfig:inConfig]; } } } @@ -267,13 +402,13 @@ static void upmix(const float *inBuffer, int inchannels, float *outBuffer, int o } } - 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); + 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); } else if(inputFormat.mChannelsPerFrame < outputFormat.mChannelsPerFrame) { - upmix((const float *)inBuffer, inputFormat.mChannelsPerFrame, (float *)outBuffer, outputFormat.mChannelsPerFrame, frames); - } else if(inputFormat.mChannelsPerFrame == outputFormat.mChannelsPerFrame) { + upmix((const float *)inBuffer, inputFormat.mChannelsPerFrame, inConfig, (float *)outBuffer, outputFormat.mChannelsPerFrame, outConfig, frames); + } else if(inConfig == outConfig) { memcpy(outBuffer, inBuffer, frames * outputFormat.mBytesPerPacket); } } diff --git a/Audio/Chain/HeadphoneFilter.h b/Audio/Chain/HeadphoneFilter.h index 855bd6e32..880ac417b 100644 --- a/Audio/Chain/HeadphoneFilter.h +++ b/Audio/Chain/HeadphoneFilter.h @@ -42,7 +42,7 @@ + (BOOL)validateImpulseFile:(NSURL *)url; -- (id)initWithImpulseFile:(NSURL *)url forSampleRate:(double)sampleRate withInputChannels:(size_t)channels; +- (id)initWithImpulseFile:(NSURL *)url forSampleRate:(double)sampleRate withInputChannels:(size_t)channels withConfig:(uint32_t)config; - (void)process:(const float *)inBuffer sampleCount:(size_t)count toBuffer:(float *)outBuffer; diff --git a/Audio/Chain/HeadphoneFilter.m b/Audio/Chain/HeadphoneFilter.m index 5fd059f2d..fe8f23e67 100644 --- a/Audio/Chain/HeadphoneFilter.m +++ b/Audio/Chain/HeadphoneFilter.m @@ -6,6 +6,7 @@ // #import "HeadphoneFilter.h" +#import "AudioChunk.h" #import "AudioDecoder.h" #import "AudioSource.h" @@ -17,44 +18,61 @@ @implementation HeadphoneFilter -// Symmetrical / no-reverb sets -static const int8_t speakers_to_hesuvi_7[8][2][8] = { - // mono/center - { { 6 }, { 6 } }, - // left/right - { { 0, 1 }, { 1, 0 } }, - // left/right/center - { { 0, 1, 6 }, { 1, 0, 6 } }, - // left/right/back lef/back right - { { 0, 1, 4, 5 }, { 1, 0, 5, 4 } }, - // left/right/center/back left/back right - { { 0, 1, 6, 4, 5 }, { 1, 0, 6, 5, 4 } }, - // left/right/center/lfe(center)/back left/back right - { { 0, 1, 6, 6, 4, 5 }, { 1, 0, 6, 6, 5, 4 } }, - // left/right/center/lfe(center)/back center(special)/side left/side right - { { 0, 1, 6, 6, -1, 2, 3 }, { 1, 0, 6, 6, -1, 3, 2 } }, - // left/right/center/lfe(center)/back left/back right/side left/side right - { { 0, 1, 6, 6, 4, 5, 2, 3 }, { 1, 0, 6, 6, 5, 4, 3, 2 } } +enum { + speaker_is_back_center = -1, + speaker_not_present = -2, }; -// Asymmetrical / reverb sets -static const int8_t speakers_to_hesuvi_14[8][2][8] = { - // mono/center - { { 6 }, { 13 } }, - // left/right - { { 0, 8 }, { 1, 7 } }, - // left/right/center - { { 0, 8, 6 }, { 1, 7, 13 } }, - // left/right/back left/back right - { { 0, 8, 4, 12 }, { 1, 7, 5, 11 } }, - // left/right/center/back left/back right - { { 0, 8, 6, 4, 12 }, { 1, 7, 13, 5, 11 } }, - // left/right/center/lfe(center)/back left/back right - { { 0, 8, 6, 6, 4, 12 }, { 1, 7, 13, 13, 5, 11 } }, - // left/right/center/lfe(center)/back center(special)/side left/side right - { { 0, 8, 6, 6, -1, 2, 10 }, { 1, 7, 13, 13, -1, 3, 9 } }, - // left/right/center/lfe(center)/back left/back right/side left/side right - { { 0, 8, 6, 6, 4, 12, 2, 10 }, { 1, 7, 13, 13, 5, 11, 3, 9 } } +static const uint32_t max_speaker_index = 10; + +static const int8_t speakers_to_hesuvi_7[11][2] = { + // front left + { 0, 1 }, + // front right + { 1, 0 }, + // front center + { 6, 6 }, + // lfe + { 6, 6 }, + // back left + { 4, 5 }, + // back right + { 5, 4 }, + // front center left + { speaker_not_present, speaker_not_present }, + // front center right + { speaker_not_present, speaker_not_present }, + // back center + { speaker_is_back_center, speaker_is_back_center }, + // side left + { 2, 3 }, + // side right + { 3, 2 } +}; + +static const int8_t speakers_to_hesuvi_14[11][2] = { + // front left + { 0, 1 }, + // front right + { 8, 7 }, + // front center + { 6, 13 }, + // lfe + { 6, 13 }, + // back left + { 4, 5 }, + // back right + { 12, 11 }, + // front center left + { speaker_not_present, speaker_not_present }, + // front center right + { speaker_not_present, speaker_not_present }, + // back center + { speaker_is_back_center, speaker_is_back_center }, + // side left + { 2, 3 }, + // side right + { 10, 9 } }; + (BOOL)validateImpulseFile:(NSURL *)url { @@ -99,7 +117,7 @@ static const int8_t speakers_to_hesuvi_14[8][2][8] = { return YES; } -- (id)initWithImpulseFile:(NSURL *)url forSampleRate:(double)sampleRate withInputChannels:(size_t)channels { +- (id)initWithImpulseFile:(NSURL *)url forSampleRate:(double)sampleRate withInputChannels:(size_t)channels withConfig:(uint32_t)config { self = [super init]; if(self) { @@ -246,7 +264,7 @@ static const int8_t speakers_to_hesuvi_14[8][2][8] = { } fftSize = 2 << pow; - float *deinterleavedImpulseBuffer = (float *)_mm_malloc(fftSize * sizeof(float) * impulseChannels, 16); + float *deinterleavedImpulseBuffer = (float *)_mm_malloc(fftSize * sizeof(float) * (impulseChannels + 1), 16); if(!deinterleavedImpulseBuffer) { free(impulseBuffer); return nil; @@ -259,6 +277,9 @@ static const int8_t speakers_to_hesuvi_14[8][2][8] = { free(impulseBuffer); + // Null impulse + vDSP_vclr(deinterleavedImpulseBuffer + impulseChannels * fftSize, 1, fftSize); + paddedBufferSize = fftSize; fftSizeOver2 = (fftSize + 1) / 2; log2n = log2f(fftSize); @@ -317,18 +338,25 @@ static const int8_t speakers_to_hesuvi_14[8][2][8] = { return nil; } - int leftInChannel; - int rightInChannel; + uint32_t channelFlag = [AudioChunk extractChannelFlag:(uint32_t)i fromConfig:config]; + uint32_t channelIndex = [AudioChunk findChannelIndex:channelFlag]; + + int leftInChannel = speaker_not_present; + int rightInChannel = speaker_not_present; if(impulseChannels == 7) { - leftInChannel = speakers_to_hesuvi_7[channels - 1][0][i]; - rightInChannel = speakers_to_hesuvi_7[channels - 1][1][i]; + if(channelIndex <= max_speaker_index) { + leftInChannel = speakers_to_hesuvi_7[channelIndex][0]; + rightInChannel = speakers_to_hesuvi_7[channelIndex][1]; + } } else { - leftInChannel = speakers_to_hesuvi_14[channels - 1][0][i]; - rightInChannel = speakers_to_hesuvi_14[channels - 1][1][i]; + if(channelIndex <= max_speaker_index) { + leftInChannel = speakers_to_hesuvi_14[channelIndex][0]; + rightInChannel = speakers_to_hesuvi_14[channelIndex][1]; + } } - if(leftInChannel == -1 || rightInChannel == -1) { + if(leftInChannel == speaker_is_back_center || rightInChannel == speaker_is_back_center) { float *temp; if(impulseChannels == 7) { temp = (float *)malloc(sizeof(float) * fftSize); @@ -360,6 +388,9 @@ static const int8_t speakers_to_hesuvi_14[8][2][8] = { } free(temp); + } else if(leftInChannel == speaker_not_present || rightInChannel == speaker_not_present) { + vDSP_ctoz((DSPComplex *)(deinterleavedImpulseBuffer + impulseChannels * fftSize), 2, &impulse_responses[i * 2 + 0], 1, fftSizeOver2); + vDSP_ctoz((DSPComplex *)(deinterleavedImpulseBuffer + impulseChannels * fftSize), 2, &impulse_responses[i * 2 + 1], 1, fftSizeOver2); } else { vDSP_ctoz((DSPComplex *)(deinterleavedImpulseBuffer + leftInChannel * fftSize), 2, &impulse_responses[i * 2 + 0], 1, fftSizeOver2); vDSP_ctoz((DSPComplex *)(deinterleavedImpulseBuffer + rightInChannel * fftSize), 2, &impulse_responses[i * 2 + 1], 1, fftSizeOver2); diff --git a/Audio/Chain/InputNode.m b/Audio/Chain/InputNode.m index a7c2859f4..b342f0703 100644 --- a/Audio/Chain/InputNode.m +++ b/Audio/Chain/InputNode.m @@ -47,6 +47,8 @@ bytesPerFrame = ((bitsPerSample + 7) / 8) * channels; nodeFormat = propertiesToASBD(properties); + if([properties valueForKey:@"channelConfig"]) + nodeChannelConfig = [[properties valueForKey:@"channelConfig"] intValue]; nodeLossless = [[properties valueForKey:@"encoding"] isEqualToString:@"lossless"]; shouldContinue = YES; @@ -66,6 +68,8 @@ bytesPerFrame = ((bitsPerSample + 7) / 8) * channels; nodeFormat = propertiesToASBD(properties); + if([properties valueForKey:@"channelConfig"]) + nodeChannelConfig = [[properties valueForKey:@"channelConfig"] intValue]; nodeLossless = [[properties valueForKey:@"encoding"] isEqualToString:@"lossless"]; [self registerObservers]; @@ -117,14 +121,15 @@ while([self shouldContinue] == YES && [self endOfStream] == NO) { if(shouldSeek == YES) { - ConverterNode *converter = [[[controller controller] bufferChain] converter]; + BufferChain *bufferChain = [[controller controller] bufferChain]; + ConverterNode *converter = [bufferChain converter]; DLog(@"SEEKING! Resetting Buffer"); amountInBuffer = 0; // This resets the converter's buffer [self resetBuffer]; [converter resetBuffer]; - [converter inputFormatDidChange:[[[controller controller] bufferChain] inputFormat]]; + [converter inputFormatDidChange:[bufferChain inputFormat] inputConfig:[bufferChain inputConfig]]; DLog(@"Reset buffer!"); diff --git a/Audio/Chain/Node.h b/Audio/Chain/Node.h index db95e3fbe..5a284dd9a 100644 --- a/Audio/Chain/Node.h +++ b/Audio/Chain/Node.h @@ -29,6 +29,7 @@ BOOL initialBufferFilled; AudioStreamBasicDescription nodeFormat; + uint32_t nodeChannelConfig; BOOL nodeLossless; } - (id)initWithController:(id)c previous:(id)p; @@ -54,6 +55,7 @@ - (void)resetBuffer; // WARNING! DANGER WILL ROBINSON! - (AudioStreamBasicDescription)nodeFormat; +- (uint32_t)nodeChannelConfig; - (BOOL)nodeLossless; - (Semaphore *)semaphore; diff --git a/Audio/Chain/Node.m b/Audio/Chain/Node.m index 46f96ef7a..5271c68a7 100644 --- a/Audio/Chain/Node.m +++ b/Audio/Chain/Node.m @@ -27,6 +27,7 @@ endOfStream = NO; shouldContinue = YES; + nodeChannelConfig = 0; nodeLossless = NO; [self setPreviousNode:p]; @@ -39,6 +40,10 @@ return nodeFormat; } +- (uint32_t)nodeChannelConfig { + return nodeChannelConfig; +} + - (BOOL)nodeLossless { return nodeLossless; } @@ -48,6 +53,9 @@ AudioChunk *chunk = [[AudioChunk alloc] init]; [chunk setFormat:nodeFormat]; + if(nodeChannelConfig) { + [chunk setChannelConfig:nodeChannelConfig]; + } [chunk setLossless:nodeLossless]; [chunk assignSamples:ptr frameCount:amount / nodeFormat.mBytesPerPacket]; diff --git a/Audio/Chain/OutputNode.h b/Audio/Chain/OutputNode.h index e68d3574c..8d5999609 100644 --- a/Audio/Chain/OutputNode.h +++ b/Audio/Chain/OutputNode.h @@ -17,6 +17,7 @@ @interface OutputNode : Node { AudioStreamBasicDescription format; + uint32_t config; double amountPlayed; double sampleRatio; @@ -48,8 +49,9 @@ - (AudioChunk *)readChunk:(size_t)amount; -- (void)setFormat:(AudioStreamBasicDescription *)f; +- (void)setFormat:(AudioStreamBasicDescription *)f channelConfig:(uint32_t)channelConfig; - (AudioStreamBasicDescription)format; +- (uint32_t)config; - (void)setVolume:(double)v; diff --git a/Audio/Chain/OutputNode.m b/Audio/Chain/OutputNode.m index 1928193b9..586437e6b 100644 --- a/Audio/Chain/OutputNode.m +++ b/Audio/Chain/OutputNode.m @@ -91,8 +91,13 @@ return format; } -- (void)setFormat:(AudioStreamBasicDescription *)f { +- (uint32_t)config { + return config; +} + +- (void)setFormat:(AudioStreamBasicDescription *)f channelConfig:(uint32_t)channelConfig { format = *f; + 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); @@ -110,8 +115,9 @@ if (oldSampleRatio) amountPlayed += oldSampleRatio * [[converter buffer] bufferedLength]; #endif - [converter setOutputFormat:format]; - [converter inputFormatDidChange:[bufferChain inputFormat]]; + [converter setOutputFormat:format + outputConfig:channelConfig]; + [converter inputFormatDidChange:[bufferChain inputFormat] inputConfig:[bufferChain inputConfig]]; } } } diff --git a/Audio/Chain/RefillNode.h b/Audio/Chain/RefillNode.h index 402b20746..95673c27b 100644 --- a/Audio/Chain/RefillNode.h +++ b/Audio/Chain/RefillNode.h @@ -22,5 +22,6 @@ } - (void)setFormat:(AudioStreamBasicDescription)format; +- (void)setChannelConfig:(uint32_t)config; @end diff --git a/Audio/Chain/RefillNode.m b/Audio/Chain/RefillNode.m index 2faff2bc5..83c6f53a0 100644 --- a/Audio/Chain/RefillNode.m +++ b/Audio/Chain/RefillNode.m @@ -42,4 +42,8 @@ nodeFormat = format; } +- (void)setChannelConfig:(uint32_t)config { + nodeChannelConfig = config; +} + @end diff --git a/Audio/Output/OutputCoreAudio.h b/Audio/Output/OutputCoreAudio.h index a4e7d4f5e..d66353ec8 100644 --- a/Audio/Output/OutputCoreAudio.h +++ b/Audio/Output/OutputCoreAudio.h @@ -60,6 +60,9 @@ AudioStreamBasicDescription deviceFormat; // info about the default device AudioStreamBasicDescription streamFormat; // stream format last seen in render callback + uint32_t deviceChannelConfig; + uint32_t streamChannelConfig; + AUAudioUnit *_au; size_t _bufferSize; diff --git a/Audio/Output/OutputCoreAudio.m b/Audio/Output/OutputCoreAudio.m index 93a4a1445..b67d25733 100644 --- a/Audio/Output/OutputCoreAudio.m +++ b/Audio/Output/OutputCoreAudio.m @@ -76,12 +76,14 @@ static OSStatus renderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioAct size_t frameCount = [chunk frameCount]; AudioStreamBasicDescription format = [chunk format]; + uint32_t config = [chunk channelConfig]; if(frameCount) { - if(!_self->streamFormatStarted || memcmp(&_self->streamFormat, &format, sizeof(format)) != 0) { + if(!_self->streamFormatStarted || config != _self->streamChannelConfig || memcmp(&_self->streamFormat, &format, sizeof(format)) != 0) { _self->streamFormat = format; + _self->streamChannelConfig = config; _self->streamFormatStarted = YES; - _self->downmixer = [[DownmixProcessor alloc] initWithInputFormat:format andOutputFormat:_self->deviceFormat]; + _self->downmixer = [[DownmixProcessor alloc] initWithInputFormat:format inputConfig:config andOutputFormat:_self->deviceFormat outputConfig:_self->deviceChannelConfig]; } double chunkDuration = [chunk duration]; @@ -104,11 +106,12 @@ static OSStatus renderCallback(void *inRefCon, AudioUnitRenderActionFlags *ioAct chunk = [[_self->outputController buffer] removeSamples:((amountToRead - amountRead) / bytesPerPacket)]; frameCount = [chunk frameCount]; format = [chunk format]; + config = [chunk channelConfig]; if(frameCount) { - if(!_self->streamFormatStarted || memcmp(&_self->streamFormat, &format, sizeof(format)) != 0) { + if(!_self->streamFormatStarted || config != _self->streamChannelConfig || memcmp(&_self->streamFormat, &format, sizeof(format)) != 0) { _self->streamFormat = format; _self->streamFormatStarted = YES; - _self->downmixer = [[DownmixProcessor alloc] initWithInputFormat:format andOutputFormat:_self->deviceFormat]; + _self->downmixer = [[DownmixProcessor alloc] initWithInputFormat:format inputConfig:config andOutputFormat:_self->deviceFormat outputConfig:_self->deviceChannelConfig]; } atomic_fetch_add(&_self->bytesRendered, frameCount * bytesPerPacket); double chunkDuration = [chunk duration]; @@ -485,27 +488,35 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const switch(deviceFormat.mChannelsPerFrame) { case 1: tag = kAudioChannelLayoutTag_Mono; + deviceChannelConfig = AudioConfigMono; break; case 2: tag = kAudioChannelLayoutTag_Stereo; + deviceChannelConfig = AudioConfigStereo; break; case 3: tag = kAudioChannelLayoutTag_DVD_4; + deviceChannelConfig = AudioConfig3Point0; break; case 4: tag = kAudioChannelLayoutTag_Quadraphonic; + deviceChannelConfig = AudioConfig4Point0; break; case 5: tag = kAudioChannelLayoutTag_MPEG_5_0_A; + deviceChannelConfig = AudioConfig5Point0; break; case 6: tag = kAudioChannelLayoutTag_MPEG_5_1_A; + deviceChannelConfig = AudioConfig5Point1; break; case 7: tag = kAudioChannelLayoutTag_MPEG_6_1_A; + deviceChannelConfig = AudioConfig6Point1; break; case 8: tag = kAudioChannelLayoutTag_MPEG_7_1_A; + deviceChannelConfig = AudioConfig7Point1; break; } @@ -514,7 +525,7 @@ default_device_changed(AudioObjectID inObjectID, UInt32 inNumberAddresses, const if(err != nil) return NO; - [outputController setFormat:&deviceFormat]; + [outputController setFormat:&deviceFormat channelConfig:deviceChannelConfig]; AudioStreamBasicDescription asbd = deviceFormat;