2022-02-06 11:08:34 +00:00
|
|
|
//
|
|
|
|
// AudioChunk.m
|
|
|
|
// CogAudio Framework
|
|
|
|
//
|
|
|
|
// Created by Christopher Snowhill on 2/5/22.
|
|
|
|
//
|
|
|
|
|
|
|
|
#import "AudioChunk.h"
|
|
|
|
|
|
|
|
@implementation AudioChunk
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
- (id)init {
|
|
|
|
self = [super init];
|
|
|
|
|
|
|
|
if(self) {
|
|
|
|
chunkData = [[NSMutableData alloc] init];
|
|
|
|
formatAssigned = NO;
|
|
|
|
lossless = NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
return self;
|
2022-02-06 11:08:34 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 08:56:05 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2022-02-06 11:08:34 +00:00
|
|
|
@synthesize lossless;
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
- (AudioStreamBasicDescription)format {
|
|
|
|
return format;
|
2022-02-06 11:08:34 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
- (void)setFormat:(AudioStreamBasicDescription)informat {
|
|
|
|
formatAssigned = YES;
|
|
|
|
format = informat;
|
2022-02-07 08:56:05 +00:00
|
|
|
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;
|
2022-02-06 11:08:34 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
- (void)assignSamples:(const void *)data frameCount:(size_t)count {
|
|
|
|
if(formatAssigned) {
|
|
|
|
const size_t bytesPerPacket = format.mBytesPerPacket;
|
|
|
|
[chunkData appendBytes:data length:bytesPerPacket * count];
|
|
|
|
}
|
2022-02-06 11:08:34 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
- (NSData *)removeSamples:(size_t)frameCount {
|
|
|
|
if(formatAssigned) {
|
|
|
|
const size_t bytesPerPacket = format.mBytesPerPacket;
|
|
|
|
const size_t byteCount = bytesPerPacket * frameCount;
|
|
|
|
NSData *ret = [chunkData subdataWithRange:NSMakeRange(0, byteCount)];
|
|
|
|
[chunkData replaceBytesInRange:NSMakeRange(0, byteCount) withBytes:NULL length:0];
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
return [NSData data];
|
2022-02-06 11:08:34 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
- (BOOL)isEmpty {
|
|
|
|
return [chunkData length] == 0;
|
2022-02-06 11:08:34 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
- (size_t)frameCount {
|
|
|
|
if(formatAssigned) {
|
|
|
|
const size_t bytesPerPacket = format.mBytesPerPacket;
|
|
|
|
return [chunkData length] / bytesPerPacket;
|
|
|
|
}
|
|
|
|
return 0;
|
2022-02-06 11:08:34 +00:00
|
|
|
}
|
|
|
|
|
2022-02-07 05:49:27 +00:00
|
|
|
- (double)duration {
|
|
|
|
if(formatAssigned) {
|
|
|
|
const size_t bytesPerPacket = format.mBytesPerPacket;
|
|
|
|
const double sampleRate = format.mSampleRate;
|
|
|
|
return (double)([chunkData length] / bytesPerPacket) / sampleRate;
|
|
|
|
}
|
|
|
|
return 0.0;
|
2022-02-06 11:08:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@end
|