Channel Mixer: Rewrite upmixing, changed HRIR

Simple upmixing algorithms now use Accelerate framework functions
instead of complex loops, and the HRIR filter now supports forcing
stereo output to any channel output configuration that has at least
front stereo speakers.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
CQTexperiment
Christopher Snowhill 2022-02-11 23:51:41 -08:00
parent 5bcedab274
commit 6b148fef11
1 changed files with 70 additions and 117 deletions

View File

@ -158,87 +158,53 @@ static void downmix_to_mono(const float *inBuffer, int channels, uint32_t config
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;
}
cblas_scopy((int)count, inBuffer, 1, outBuffer, 2);
cblas_scopy((int)count, inBuffer, 1, outBuffer + 1, 2);
} 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;
}
cblas_scopy((int)count, inBuffer, 1, outBuffer, 4);
cblas_scopy((int)count, inBuffer, 1, outBuffer + 1, 4);
vDSP_vclr(outBuffer + 2, 4, count);
vDSP_vclr(outBuffer + 3, 4, count);
} 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 + cIndex] = sample;
for(int j = 0; j < cIndex; ++j) {
outBuffer[i * outchannels + j] = 0;
}
for(int j = cIndex + 1; j < outchannels; ++j) {
outBuffer[i * outchannels + j] = 0;
}
cblas_scopy((int)count, inBuffer, 1, outBuffer + cIndex, outchannels);
for(size_t i = 0; i < cIndex; ++i) {
vDSP_vclr(outBuffer + i, outchannels, (int)count);
}
for(size_t i = cIndex + 1; i < outchannels; ++i) {
vDSP_vclr(outBuffer + i, outchannels, (int)count);
}
} 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];
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(blIndex != ~0) {
outBuffer[i * outchannels + blIndex] = bl;
}
if(brIndex != ~0) {
outBuffer[i * outchannels + brIndex] = br;
}
}
vDSP_vclr(outBuffer, 1, count * outchannels);
if(flIndex != ~0)
cblas_scopy((int)count, inBuffer + 0, 4, outBuffer + flIndex, outchannels);
if(frIndex != ~0)
cblas_scopy((int)count, inBuffer + 1, 4, outBuffer + frIndex, outchannels);
if(blIndex != ~0)
cblas_scopy((int)count, inBuffer + 2, 4, outBuffer + blIndex, outchannels);
if(brIndex != ~0)
cblas_scopy((int)count, inBuffer + 3, 4, outBuffer + brIndex, outchannels);
} 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];
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(blIndex != ~0) {
outBuffer[i * outchannels + blIndex] = bl;
}
if(brIndex != ~0) {
outBuffer[i * outchannels + brIndex] = br;
}
}
vDSP_vclr(outBuffer, 1, count * outchannels);
if(flIndex != ~0)
cblas_scopy((int)count, inBuffer + 0, 5, outBuffer + flIndex, outchannels);
if(frIndex != ~0)
cblas_scopy((int)count, inBuffer + 1, 5, outBuffer + frIndex, outchannels);
if(cIndex != ~0)
cblas_scopy((int)count, inBuffer + 2, 5, outBuffer + cIndex, outchannels);
if(blIndex != ~0)
cblas_scopy((int)count, inBuffer + 3, 5, outBuffer + blIndex, outchannels);
if(brIndex != ~0)
cblas_scopy((int)count, inBuffer + 4, 5, outBuffer + brIndex, outchannels);
} else if(inconfig == AudioConfig6Point1 && outchannels >= 8) {
uint32_t flIndex = [AudioChunk channelIndexFromConfig:outconfig forFlag:AudioChannelFrontLeft];
uint32_t frIndex = [AudioChunk channelIndexFromConfig:outconfig forFlag:AudioChannelFrontRight];
@ -249,58 +215,34 @@ static void upmix(const float *inBuffer, int inchannels, uint32_t inconfig, floa
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];
float lfe = inBuffer[i * 7 + 3];
float sl = inBuffer[i * 7 + 4];
float sr = inBuffer[i * 7 + 5];
float bc = inBuffer[i * 7 + 6];
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;
}
}
vDSP_vclr(outBuffer, 1, count * outchannels);
if(flIndex != ~0)
cblas_scopy((int)count, inBuffer + 0, 7, outBuffer + flIndex, outchannels);
if(frIndex != ~0)
cblas_scopy((int)count, inBuffer + 1, 7, outBuffer + frIndex, outchannels);
if(cIndex != ~0)
cblas_scopy((int)count, inBuffer + 2, 7, outBuffer + cIndex, outchannels);
if(lfeIndex != ~0)
cblas_scopy((int)count, inBuffer + 3, 7, outBuffer + lfeIndex, outchannels);
if(slIndex != ~0)
cblas_scopy((int)count, inBuffer + 4, 7, outBuffer + slIndex, outchannels);
if(srIndex != ~0)
cblas_scopy((int)count, inBuffer + 5, 7, outBuffer + srIndex, outchannels);
if(bcIndex != ~0)
cblas_scopy((int)count, inBuffer + 6, 7, outBuffer + bcIndex, outchannels);
else {
if(blIndex != ~0)
cblas_scopy((int)count, inBuffer + 6, 7, outBuffer + blIndex, outchannels);
if(brIndex != ~0)
cblas_scopy((int)count, inBuffer + 6, 7, outBuffer + brIndex, outchannels);
}
} else {
uint32_t outIndexes[inchannels];
vDSP_vclr(outBuffer, 1, count * outchannels);
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
memset(outBuffer + i * outchannels, 0, sizeof(float) * outchannels);
for(int j = 0; j < inchannels; ++j) {
if(outIndexes[j] != ~0) {
outBuffer[i * outchannels + outIndexes[j]] = inBuffer[i * inchannels + j];
}
}
uint32_t outIndex = [AudioChunk channelIndexFromConfig:outconfig forFlag:channelFlag];
if(outIndex != ~0)
cblas_scopy((int)count, inBuffer + i, inchannels, outBuffer + outIndex, outchannels);
}
}
}
@ -353,8 +295,8 @@ static void upmix(const float *inBuffer, int inchannels, uint32_t inconfig, floa
BOOL hVirt = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] boolForKey:@"headphoneVirtualization"];
if(hVirt &&
outputFormat.mChannelsPerFrame == 2 &&
outConfig == AudioConfigStereo &&
outputFormat.mChannelsPerFrame >= 2 &&
(outConfig & AudioConfigStereo) == AudioConfigStereo &&
inputFormat.mChannelsPerFrame >= 1 &&
(inConfig & (AudioConfig7Point1 | AudioChannelBackCenter)) != 0) {
NSString *userPreset = [[[NSUserDefaultsController sharedUserDefaultsController] defaults] stringForKey:@"hrirPath"];
@ -396,7 +338,18 @@ static void upmix(const float *inBuffer, int inchannels, uint32_t inconfig, floa
- (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];
uint32_t outChannels = outputFormat.mChannelsPerFrame;
if(outChannels > 2) {
float tempBuffer[frames * 2];
[hFilter process:(const float *)inBuffer sampleCount:frames toBuffer:&tempBuffer[0]];
cblas_scopy((int)frames, tempBuffer, 2, (float *)outBuffer, outChannels);
cblas_scopy((int)frames, tempBuffer + 1, 2, ((float *)outBuffer) + 1, outChannels);
for(size_t i = 2; i < outChannels; ++i) {
vDSP_vclr(((float *)outBuffer) + i, outChannels, (int)frames);
}
} else {
[hFilter process:(const float *)inBuffer sampleCount:frames toBuffer:(float *)outBuffer];
}
return;
}
}