HRTF DSP: Support resampling impulses

This prepares the filter to be the same as the rest of the filters, in
that they support flexible sample rates to match the output device.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
This commit is contained in:
Christopher Snowhill 2025-02-12 20:57:22 -08:00
parent d17388ee95
commit b39882168b
2 changed files with 32 additions and 11 deletions

View file

@ -18,6 +18,7 @@
int bufferSize; int bufferSize;
int paddedBufferSize; int paddedBufferSize;
double sampleRate;
int channelCount; int channelCount;
uint32_t config; uint32_t config;

View file

@ -14,7 +14,7 @@
#import <fstream> #import <fstream>
#import "rsstate.h" #import <soxr.h>
#import "HrtfData.h" #import "HrtfData.h"
@ -125,7 +125,7 @@ static void transformPosition(float &elevation, float &azimuth, const simd_float
HrtfData *data; HrtfData *data;
} }
+ (impulseSetCache *)sharedController; + (impulseSetCache *)sharedController;
- (void)getImpulse:(NSURL *)url outImpulse:(float **)outImpulse outSampleCount:(int *)outSampleCount channelCount:(int)channelCount channelConfig:(uint32_t)channelConfig withMatrix:(simd_float4x4)matrix; - (void)getImpulse:(NSURL *)url outImpulse:(float **)outImpulse outSampleCount:(int *)outSampleCount sampleRate:(double)sampleRate channelCount:(int)channelCount channelConfig:(uint32_t)channelConfig withMatrix:(simd_float4x4)matrix;
@end @end
@implementation impulseSetCache @implementation impulseSetCache
@ -152,7 +152,7 @@ static impulseSetCache *_sharedController = nil;
delete data; delete data;
} }
- (void)getImpulse:(NSURL *)url outImpulse:(float **)outImpulse outSampleCount:(int *)outSampleCount channelCount:(int)channelCount channelConfig:(uint32_t)channelConfig withMatrix:(simd_float4x4)matrix { - (void)getImpulse:(NSURL *)url outImpulse:(float **)outImpulse outSampleCount:(int *)outSampleCount sampleRate:(double)sampleRate channelCount:(int)channelCount channelConfig:(uint32_t)channelConfig withMatrix:(simd_float4x4)matrix {
double sampleRateOfSource = 0; double sampleRateOfSource = 0;
int sampleCount = 0; int sampleCount = 0;
@ -174,13 +174,27 @@ static impulseSetCache *_sharedController = nil;
} }
try { try {
sampleRateOfSource = data->get_sample_rate(); soxr_quality_spec_t q_spec = soxr_quality_spec(SOXR_HQ, 0);
soxr_io_spec_t io_spec = soxr_io_spec(SOXR_FLOAT32_I, SOXR_FLOAT32_I);
soxr_runtime_spec_t runtime_spec = soxr_runtime_spec(0);
bool resampling;
sampleRateOfSource = data->get_sample_rate();
resampling = !!(fabs(sampleRateOfSource - sampleRate) > 1e-6);
uint32_t sampleCountResampled;
uint32_t sampleCountExact = data->get_response_length(); uint32_t sampleCountExact = data->get_response_length();
sampleCount = sampleCountExact + ((data->get_longest_delay() + 2) >> 2); sampleCount = sampleCountExact + ((data->get_longest_delay() + 2) >> 2);
sampleCount = (sampleCount + 15) & ~15;
*outImpulse = (float *)calloc(sizeof(float), sampleCount * channelCount * 2); uint32_t actualSampleCount = sampleCount;
if(resampling) {
sampleCountResampled = (uint32_t)(((double)sampleCountExact) * sampleRate / sampleRateOfSource);
actualSampleCount = (uint32_t)(((double)actualSampleCount) * sampleRate / sampleRateOfSource);
}
actualSampleCount = (actualSampleCount + 15) & ~15;
*outImpulse = (float *)calloc(sizeof(float), actualSampleCount * channelCount * 2);
if(!*outImpulse) { if(!*outImpulse) {
throw std::bad_alloc(); throw std::bad_alloc();
} }
@ -202,12 +216,17 @@ static impulseSetCache *_sharedController = nil;
data->get_direction_data(elevation, azimuth, speaker.distance, hrtfLeft, hrtfRight); data->get_direction_data(elevation, azimuth, speaker.distance, hrtfLeft, hrtfRight);
cblas_scopy(sampleCountExact, &hrtfLeft.impulse_response[0], 1, &hrtfData[((hrtfLeft.delay + 2) >> 2) + sampleCount * i * 2], 1); if(resampling) {
cblas_scopy(sampleCountExact, &hrtfRight.impulse_response[0], 1, &hrtfData[((hrtfLeft.delay + 2) >> 2) + sampleCount * (i * 2 + 1)], 1); soxr_oneshot(sampleRateOfSource, sampleRate, 1, &hrtfLeft.impulse_response[0], sampleCountExact, NULL, &hrtfData[((hrtfLeft.delay + 2) >> 2) + actualSampleCount * i * 2], sampleCountResampled, NULL, &io_spec, &q_spec, &runtime_spec);
soxr_oneshot(sampleRateOfSource, sampleRate, 1, &hrtfRight.impulse_response[0], sampleCountExact, NULL, &hrtfData[((hrtfRight.delay + 2) >> 2) + actualSampleCount * (i * 2 + 1)], sampleCountResampled, NULL, &io_spec, &q_spec, &runtime_spec);
} else {
cblas_scopy(sampleCountExact, &hrtfLeft.impulse_response[0], 1, &hrtfData[((hrtfLeft.delay + 2) >> 2) + actualSampleCount * i * 2], 1);
cblas_scopy(sampleCountExact, &hrtfRight.impulse_response[0], 1, &hrtfData[((hrtfRight.delay + 2) >> 2) + actualSampleCount * (i * 2 + 1)], 1);
}
} }
} }
*outSampleCount = sampleCount; *outSampleCount = actualSampleCount;
} catch(std::exception &e) { } catch(std::exception &e) {
ALog(@"Exception caught: %s", e.what()); ALog(@"Exception caught: %s", e.what());
} }
@ -242,12 +261,13 @@ static impulseSetCache *_sharedController = nil;
if(self) { if(self) {
URL = url; URL = url;
self->sampleRate = sampleRate;
channelCount = channels; channelCount = channels;
self->config = config; self->config = config;
float *impulseBuffer = NULL; float *impulseBuffer = NULL;
int sampleCount = 0; int sampleCount = 0;
[[impulseSetCache sharedController] getImpulse:url outImpulse:&impulseBuffer outSampleCount:&sampleCount channelCount:channels channelConfig:config withMatrix:matrix]; [[impulseSetCache sharedController] getImpulse:url outImpulse:&impulseBuffer outSampleCount:&sampleCount sampleRate:sampleRate channelCount:channels channelConfig:config withMatrix:matrix];
if(!impulseBuffer) { if(!impulseBuffer) {
return nil; return nil;
} }
@ -315,7 +335,7 @@ static impulseSetCache *_sharedController = nil;
float *impulseBuffer = NULL; float *impulseBuffer = NULL;
int sampleCount = 0; int sampleCount = 0;
[[impulseSetCache sharedController] getImpulse:URL outImpulse:&impulseBuffer outSampleCount:&sampleCount channelCount:channelCount channelConfig:config withMatrix:matrix]; [[impulseSetCache sharedController] getImpulse:URL outImpulse:&impulseBuffer outSampleCount:&sampleCount sampleRate:sampleRate channelCount:channelCount channelConfig:config withMatrix:matrix];
for(int i = 0; i < channelCount * 2; ++i) { for(int i = 0; i < channelCount * 2; ++i) {
mirroredImpulseResponses[i] = &impulseBuffer[sampleCount * i]; mirroredImpulseResponses[i] = &impulseBuffer[sampleCount * i];