diff --git a/Plugins/CoreAudio/CoreAudioDecoder.h b/Plugins/CoreAudio/CoreAudioDecoder.h index 4c5919f73..39e1d0766 100644 --- a/Plugins/CoreAudio/CoreAudioDecoder.h +++ b/Plugins/CoreAudio/CoreAudioDecoder.h @@ -43,6 +43,7 @@ int bitsPerSample; BOOL floatingPoint; int channels; + uint32_t channelConfig; float frequency; long totalFrames; diff --git a/Plugins/CoreAudio/CoreAudioDecoder.m b/Plugins/CoreAudio/CoreAudioDecoder.m index 7eb0af21a..cb64eb6a9 100644 --- a/Plugins/CoreAudio/CoreAudioDecoder.m +++ b/Plugins/CoreAudio/CoreAudioDecoder.m @@ -28,6 +28,33 @@ - (BOOL)readInfoFromExtAudioFileRef; @end +static int ffat_get_channel_id(AudioChannelLabel label) { + if(label == 0) + return -1; + else if(label <= kAudioChannelLabel_LFEScreen) + return label - 1; + else if(label <= kAudioChannelLabel_RightSurround) + return label + 4; + else if(label <= kAudioChannelLabel_CenterSurround) + return label + 1; + else if(label <= kAudioChannelLabel_RightSurroundDirect) + return label + 23; + else if(label <= kAudioChannelLabel_TopBackRight) + return label - 1; + else if(label < kAudioChannelLabel_RearSurroundLeft) + return -1; + else if(label <= kAudioChannelLabel_RearSurroundRight) + return label - 29; + else if(label <= kAudioChannelLabel_RightWide) + return label - 4; + else if(label == kAudioChannelLabel_LFE2) + return -1; + else if(label == kAudioChannelLabel_Mono) + return 2; // Front center + else + return -1; +} + static OSStatus readProc(void *clientData, SInt64 position, UInt32 requestCount, @@ -177,6 +204,28 @@ static SInt64 getSizeProc(void *clientData) { return NO; } + err = AudioFileGetPropertyInfo(afi, kAudioFilePropertyChannelLayout, &size, NULL); + if(err != noErr || size == 0) { + err = ExtAudioFileDispose(_in); + return NO; + } + AudioChannelLayout *acl = malloc(size); + err = AudioFileGetProperty(afi, kAudioFilePropertyChannelLayout, &size, acl); + if(err != noErr) { + free(acl); + err = ExtAudioFileDispose(_in); + return NO; + } + + uint32_t config = 0; + for(uint32_t i = 0; i < acl->mNumberChannelDescriptions; ++i) { + int channelNumber = ffat_get_channel_id(acl->mChannelDescriptions[i].mChannelLabel); + if(channelNumber >= 0) + config |= 1 << channelNumber; + } + + channelConfig = config; + bitrate = (_bitrate + 500) / 1000; CFStringRef formatName; @@ -330,6 +379,7 @@ static SInt64 getSizeProc(void *clientData) { - (NSDictionary *)properties { return [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInt:channels], @"channels", + [NSNumber numberWithInt:channelConfig], @"channelConfig", [NSNumber numberWithInt:bitsPerSample], @"bitsPerSample", [NSNumber numberWithBool:floatingPoint], @"floatingPoint", [NSNumber numberWithInt:bitrate], @"bitrate", diff --git a/Plugins/FFMPEG/FFMPEGDecoder.h b/Plugins/FFMPEG/FFMPEGDecoder.h index 2ee76e21b..b3681a75e 100644 --- a/Plugins/FFMPEG/FFMPEGDecoder.h +++ b/Plugins/FFMPEG/FFMPEGDecoder.h @@ -17,6 +17,7 @@ id source; BOOL seekable; int channels; + uint32_t channelConfig; int bitsPerSample; BOOL floatingPoint; BOOL lossy; diff --git a/Plugins/FFMPEG/FFMPEGDecoder.m b/Plugins/FFMPEG/FFMPEGDecoder.m index 59ae5a564..d060c0242 100644 --- a/Plugins/FFMPEG/FFMPEGDecoder.m +++ b/Plugins/FFMPEG/FFMPEGDecoder.m @@ -249,6 +249,7 @@ int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) { frequency = codecCtx->sample_rate; channels = codecCtx->channels; + channelConfig = (uint32_t)codecCtx->channel_layout; floatingPoint = NO; switch(codecCtx->sample_fmt) { @@ -616,6 +617,7 @@ int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) { - (NSDictionary *)properties { return [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInt:channels], @"channels", + [NSNumber numberWithInt:channelConfig], @"channelConfig", [NSNumber numberWithInt:bitsPerSample], @"bitsPerSample", [NSNumber numberWithBool:(bitsPerSample == 8)], @"Unsigned", [NSNumber numberWithFloat:frequency], @"sampleRate", diff --git a/Plugins/Flac/FlacDecoder.h b/Plugins/Flac/FlacDecoder.h index 94b011db9..2d75f2e8f 100644 --- a/Plugins/Flac/FlacDecoder.h +++ b/Plugins/Flac/FlacDecoder.h @@ -26,6 +26,7 @@ int bitsPerSample; int channels; + uint32_t channelConfig; float frequency; long totalFrames; diff --git a/Plugins/Flac/FlacDecoder.m b/Plugins/Flac/FlacDecoder.m index e90c47755..5ccdccfb1 100644 --- a/Plugins/Flac/FlacDecoder.m +++ b/Plugins/Flac/FlacDecoder.m @@ -12,6 +12,28 @@ @implementation FlacDecoder +static const char *CHANNEL_MASK_TAG = "WAVEFORMATEXTENSIBLE_CHANNEL_MASK"; + +static FLAC__bool flac__utils_get_channel_mask_tag(const FLAC__StreamMetadata *object, FLAC__uint32 *channel_mask) { + int offset; + uint32_t val; + char *p; + FLAC__ASSERT(object); + FLAC__ASSERT(object->type == FLAC__METADATA_TYPE_VORBIS_COMMENT); + if(0 > (offset = FLAC__metadata_object_vorbiscomment_find_entry_from(object, /*offset=*/0, CHANNEL_MASK_TAG))) + return false; + if(object->data.vorbis_comment.comments[offset].length < strlen(CHANNEL_MASK_TAG) + 4) + return false; + if(0 == (p = strchr((const char *)object->data.vorbis_comment.comments[offset].entry, '='))) /* should never happen, but just in case */ + return false; + if(strncasecmp(p, "=0x", 3)) + return false; + if(sscanf(p + 3, "%x", &val) != 1) + return false; + *channel_mask = val; + return true; +} + FLAC__StreamDecoderReadStatus ReadCallback(const FLAC__StreamDecoder *decoder, FLAC__byte blockBuffer[], size_t *bytes, void *client_data) { FlacDecoder *flacDecoder = (__bridge FlacDecoder *)client_data; long bytesRead = [[flacDecoder source] read:blockBuffer amount:*bytes]; @@ -168,11 +190,12 @@ void MetadataCallback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMeta flacDecoder->totalFrames = metadata->data.stream_info.total_samples; - [flacDecoder willChangeValueForKey:@"properties"]; - [flacDecoder didChangeValueForKey:@"properties"]; - flacDecoder->hasStreamInfo = YES; } + + if(metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) { + (void)flac__utils_get_channel_mask_tag(metadata, &flacDecoder->channelConfig); + } } void ErrorCallback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) { @@ -208,6 +231,11 @@ void ErrorCallback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorS FLAC__stream_decoder_process_until_end_of_metadata(decoder); + if(hasStreamInfo) { + [self willChangeValueForKey:@"properties"]; + [self didChangeValueForKey:@"properties"]; + } + blockBuffer = malloc(SAMPLE_blockBuffer_SIZE); return YES; @@ -305,6 +333,7 @@ void ErrorCallback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorS - (NSDictionary *)properties { return [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInt:channels], @"channels", + [NSNumber numberWithInt:channelConfig], @"channelConfig", [NSNumber numberWithInt:bitsPerSample], @"bitsPerSample", [NSNumber numberWithFloat:frequency], @"sampleRate", [NSNumber numberWithDouble:totalFrames], @"totalFrames", diff --git a/Plugins/WavPack/WavPackDecoder.h b/Plugins/WavPack/WavPackDecoder.h index 3987d78ba..e635dfa3e 100644 --- a/Plugins/WavPack/WavPackDecoder.h +++ b/Plugins/WavPack/WavPackDecoder.h @@ -39,6 +39,7 @@ int bitsPerSample; int channels; + uint32_t channelConfig; BOOL floatingPoint; int bitrate; float frequency; diff --git a/Plugins/WavPack/WavPackDecoder.m b/Plugins/WavPack/WavPackDecoder.m index 97613845e..ccdabbdfc 100644 --- a/Plugins/WavPack/WavPackDecoder.m +++ b/Plugins/WavPack/WavPackDecoder.m @@ -144,6 +144,7 @@ int32_t WriteBytesProc(void *ds, void *data, int32_t bcount) { } channels = WavpackGetNumChannels(wpc); + channelConfig = WavpackGetChannelMask(wpc); bitsPerSample = WavpackGetBitsPerSample(wpc); frequency = WavpackGetSampleRate(wpc); @@ -282,6 +283,7 @@ int32_t WriteBytesProc(void *ds, void *data, int32_t bcount) { - (NSDictionary *)properties { return [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInt:channels], @"channels", + [NSNumber numberWithInt:channelConfig], @"channelConfig", [NSNumber numberWithInt:bitsPerSample], @"bitsPerSample", [NSNumber numberWithInt:bitrate], @"bitrate", [NSNumber numberWithFloat:frequency], @"sampleRate",