Implement channel config fields for inputs

This implements channel masks for inputs where applicable, namely the
CoreAudio decoder, FFmpeg, FLAC, and WavPack. All others will still use
guessing from the channel number.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
CQTexperiment
Christopher Snowhill 2022-02-07 02:06:51 -08:00
parent 95fb65527f
commit 22d8b8c132
8 changed files with 90 additions and 3 deletions

View File

@ -43,6 +43,7 @@
int bitsPerSample; int bitsPerSample;
BOOL floatingPoint; BOOL floatingPoint;
int channels; int channels;
uint32_t channelConfig;
float frequency; float frequency;
long totalFrames; long totalFrames;

View File

@ -28,6 +28,33 @@
- (BOOL)readInfoFromExtAudioFileRef; - (BOOL)readInfoFromExtAudioFileRef;
@end @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, static OSStatus readProc(void *clientData,
SInt64 position, SInt64 position,
UInt32 requestCount, UInt32 requestCount,
@ -177,6 +204,28 @@ static SInt64 getSizeProc(void *clientData) {
return NO; 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; bitrate = (_bitrate + 500) / 1000;
CFStringRef formatName; CFStringRef formatName;
@ -330,6 +379,7 @@ static SInt64 getSizeProc(void *clientData) {
- (NSDictionary *)properties { - (NSDictionary *)properties {
return [NSDictionary dictionaryWithObjectsAndKeys: return [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:channels], @"channels", [NSNumber numberWithInt:channels], @"channels",
[NSNumber numberWithInt:channelConfig], @"channelConfig",
[NSNumber numberWithInt:bitsPerSample], @"bitsPerSample", [NSNumber numberWithInt:bitsPerSample], @"bitsPerSample",
[NSNumber numberWithBool:floatingPoint], @"floatingPoint", [NSNumber numberWithBool:floatingPoint], @"floatingPoint",
[NSNumber numberWithInt:bitrate], @"bitrate", [NSNumber numberWithInt:bitrate], @"bitrate",

View File

@ -17,6 +17,7 @@
id<CogSource> source; id<CogSource> source;
BOOL seekable; BOOL seekable;
int channels; int channels;
uint32_t channelConfig;
int bitsPerSample; int bitsPerSample;
BOOL floatingPoint; BOOL floatingPoint;
BOOL lossy; BOOL lossy;

View File

@ -249,6 +249,7 @@ int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) {
frequency = codecCtx->sample_rate; frequency = codecCtx->sample_rate;
channels = codecCtx->channels; channels = codecCtx->channels;
channelConfig = (uint32_t)codecCtx->channel_layout;
floatingPoint = NO; floatingPoint = NO;
switch(codecCtx->sample_fmt) { switch(codecCtx->sample_fmt) {
@ -616,6 +617,7 @@ int64_t ffmpeg_seek(void *opaque, int64_t offset, int whence) {
- (NSDictionary *)properties { - (NSDictionary *)properties {
return [NSDictionary dictionaryWithObjectsAndKeys: return [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:channels], @"channels", [NSNumber numberWithInt:channels], @"channels",
[NSNumber numberWithInt:channelConfig], @"channelConfig",
[NSNumber numberWithInt:bitsPerSample], @"bitsPerSample", [NSNumber numberWithInt:bitsPerSample], @"bitsPerSample",
[NSNumber numberWithBool:(bitsPerSample == 8)], @"Unsigned", [NSNumber numberWithBool:(bitsPerSample == 8)], @"Unsigned",
[NSNumber numberWithFloat:frequency], @"sampleRate", [NSNumber numberWithFloat:frequency], @"sampleRate",

View File

@ -26,6 +26,7 @@
int bitsPerSample; int bitsPerSample;
int channels; int channels;
uint32_t channelConfig;
float frequency; float frequency;
long totalFrames; long totalFrames;

View File

@ -12,6 +12,28 @@
@implementation FlacDecoder @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) { FLAC__StreamDecoderReadStatus ReadCallback(const FLAC__StreamDecoder *decoder, FLAC__byte blockBuffer[], size_t *bytes, void *client_data) {
FlacDecoder *flacDecoder = (__bridge FlacDecoder *)client_data; FlacDecoder *flacDecoder = (__bridge FlacDecoder *)client_data;
long bytesRead = [[flacDecoder source] read:blockBuffer amount:*bytes]; 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->totalFrames = metadata->data.stream_info.total_samples;
[flacDecoder willChangeValueForKey:@"properties"];
[flacDecoder didChangeValueForKey:@"properties"];
flacDecoder->hasStreamInfo = YES; 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) { 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); FLAC__stream_decoder_process_until_end_of_metadata(decoder);
if(hasStreamInfo) {
[self willChangeValueForKey:@"properties"];
[self didChangeValueForKey:@"properties"];
}
blockBuffer = malloc(SAMPLE_blockBuffer_SIZE); blockBuffer = malloc(SAMPLE_blockBuffer_SIZE);
return YES; return YES;
@ -305,6 +333,7 @@ void ErrorCallback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorS
- (NSDictionary *)properties { - (NSDictionary *)properties {
return [NSDictionary dictionaryWithObjectsAndKeys: return [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:channels], @"channels", [NSNumber numberWithInt:channels], @"channels",
[NSNumber numberWithInt:channelConfig], @"channelConfig",
[NSNumber numberWithInt:bitsPerSample], @"bitsPerSample", [NSNumber numberWithInt:bitsPerSample], @"bitsPerSample",
[NSNumber numberWithFloat:frequency], @"sampleRate", [NSNumber numberWithFloat:frequency], @"sampleRate",
[NSNumber numberWithDouble:totalFrames], @"totalFrames", [NSNumber numberWithDouble:totalFrames], @"totalFrames",

View File

@ -39,6 +39,7 @@
int bitsPerSample; int bitsPerSample;
int channels; int channels;
uint32_t channelConfig;
BOOL floatingPoint; BOOL floatingPoint;
int bitrate; int bitrate;
float frequency; float frequency;

View File

@ -144,6 +144,7 @@ int32_t WriteBytesProc(void *ds, void *data, int32_t bcount) {
} }
channels = WavpackGetNumChannels(wpc); channels = WavpackGetNumChannels(wpc);
channelConfig = WavpackGetChannelMask(wpc);
bitsPerSample = WavpackGetBitsPerSample(wpc); bitsPerSample = WavpackGetBitsPerSample(wpc);
frequency = WavpackGetSampleRate(wpc); frequency = WavpackGetSampleRate(wpc);
@ -282,6 +283,7 @@ int32_t WriteBytesProc(void *ds, void *data, int32_t bcount) {
- (NSDictionary *)properties { - (NSDictionary *)properties {
return [NSDictionary dictionaryWithObjectsAndKeys: return [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:channels], @"channels", [NSNumber numberWithInt:channels], @"channels",
[NSNumber numberWithInt:channelConfig], @"channelConfig",
[NSNumber numberWithInt:bitsPerSample], @"bitsPerSample", [NSNumber numberWithInt:bitsPerSample], @"bitsPerSample",
[NSNumber numberWithInt:bitrate], @"bitrate", [NSNumber numberWithInt:bitrate], @"bitrate",
[NSNumber numberWithFloat:frequency], @"sampleRate", [NSNumber numberWithFloat:frequency], @"sampleRate",