diff options
author | Chris Robinson <[email protected]> | 2017-07-25 16:17:46 -0700 |
---|---|---|
committer | Chris Robinson <[email protected]> | 2017-07-25 16:17:46 -0700 |
commit | 75841642bf311e18b906dc5f48ce08bc8699a47b (patch) | |
tree | c3797da2ac49cd1e6b8cc9fc5e3c2ae7cb9c8035 | |
parent | 1ab082caaff3075e2d9d633b75ff82b82a2f69e9 (diff) |
Update makehrtf to use a larger FFT by default
Also fixes DC offset removal and increases the max IR size.
-rw-r--r-- | Alc/hrtf.c | 2 | ||||
-rw-r--r-- | utils/makehrtf.c | 134 |
2 files changed, 61 insertions, 75 deletions
@@ -37,7 +37,7 @@ /* Current data set limits defined by the makehrtf utility. */ #define MIN_IR_SIZE (8) -#define MAX_IR_SIZE (128) +#define MAX_IR_SIZE (512) #define MOD_IR_SIZE (8) #define MIN_EV_COUNT (5) diff --git a/utils/makehrtf.c b/utils/makehrtf.c index 704bf622..547c80db 100644 --- a/utils/makehrtf.c +++ b/utils/makehrtf.c @@ -2,7 +2,7 @@ * HRTF utility for producing and demonstrating the process of creating an * OpenAL Soft compatible HRIR data set. * - * Copyright (C) 2011-2014 Christopher Fitzgerald + * Copyright (C) 2011-2017 Christopher Fitzgerald * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -83,7 +83,7 @@ #endif // The epsilon used to maintain signal stability. -#define EPSILON (1e-15) +#define EPSILON (1e-9) // Constants for accessing the token reader's ring buffer. #define TR_RING_BITS (16) @@ -145,16 +145,16 @@ #define MAX_ASCII_BITS (32) // The limits to the FFT window size override on the command line. -#define MIN_FFTSIZE (512) -#define MAX_FFTSIZE (16384) +#define MIN_FFTSIZE (65536) +#define MAX_FFTSIZE (131072) // The limits to the equalization range limit on the command line. #define MIN_LIMIT (2.0) #define MAX_LIMIT (120.0) // The limits to the truncation window size on the command line. -#define MIN_TRUNCSIZE (8) -#define MAX_TRUNCSIZE (128) +#define MIN_TRUNCSIZE (16) +#define MAX_TRUNCSIZE (512) // The limits to the custom head radius on the command line. #define MIN_CUSTOM_RADIUS (0.05) @@ -165,6 +165,7 @@ #define MOD_TRUNCSIZE (8) // The defaults for the command line options. +#define DEFAULT_FFTSIZE (65536) #define DEFAULT_EQUALIZE (1) #define DEFAULT_SURFACE (1) #define DEFAULT_LIMIT (24.0) @@ -985,10 +986,10 @@ static void FftInverse(const uint n, const double *inR, const double *inI, doubl } } -/* Calculate the complex helical sequence (or discrete-time analytical - * signal) of the given input using the Hilbert transform. Given the - * negative natural logarithm of a signal's magnitude response, the imaginary - * components can be used as the angles for minimum-phase reconstruction. +/* Calculate the complex helical sequence (or discrete-time analytical signal) + * of the given input using the Hilbert transform. Given the natural logarithm + * of a signal's magnitude response, the imaginary components can be used as + * the angles for minimum-phase reconstruction. */ static void Hilbert(const uint n, const double *in, double *outR, double *outI) { @@ -1009,24 +1010,20 @@ static void Hilbert(const uint n, const double *in, double *outR, double *outI) outI[i] = 0.0; } } - FftForward(n, outR, outI, outR, outI); - /* Currently the Fourier routines operate only on point counts that are - * powers of two. If that changes and n is odd, the following conditional - * should be: i < (n + 1) / 2. - */ - for(i = 1;i < (n/2);i++) + FftInverse(n, outR, outI, outR, outI); + for(i = 1;i < (n+1)/2;i++) { outR[i] *= 2.0; outI[i] *= 2.0; } - // If n is odd, the following increment should be skipped. - i++; + /* Increment i if n is even. */ + i += (n&1)^1; for(;i < n;i++) { outR[i] = 0.0; outI[i] = 0.0; } - FftInverse(n, outR, outI, outR, outI); + FftForward(n, outR, outI, outR, outI); } /* Calculate the magnitude response of the given input. This is used in @@ -1080,15 +1077,15 @@ static void LimitMagnitudeResponse(const uint n, const double limit, const doubl static void MinimumPhase(const uint n, const double *in, double *outR, double *outI) { const uint m = 1 + (n / 2); - double aR, aI; double *mags; + double aR, aI; uint i; mags = CreateArray(n); for(i = 0;i < m;i++) { - mags[i] = fmax(in[i], EPSILON); - outR[i] = -log(mags[i]); + mags[i] = fmax(EPSILON, in[i]); + outR[i] = log(mags[i]); } for(;i < n;i++) { @@ -1097,9 +1094,8 @@ static void MinimumPhase(const uint n, const double *in, double *outR, double *o } Hilbert(n, outR, outR, outI); // Remove any DC offset the filter has. - outR[0] = 0.0; - outI[0] = 0.0; - for(i = 1;i < n;i++) + mags[0] = EPSILON; + for(i = 0;i < n;i++) { ComplexExp(0.0, outI[i], &aR, &aI); ComplexMul(mags[i], 0.0, aR, aI, &outR[i], &outI[i]); @@ -1272,13 +1268,13 @@ static void ResamplerSetup(ResamplerT *rs, const uint srcRate, const uint dstRat */ if(rs->mP > rs->mQ) { - cutoff = 0.45 / rs->mP; - width = 0.1 / rs->mP; + cutoff = 0.475 / rs->mP; + width = 0.05 / rs->mP; } else { - cutoff = 0.45 / rs->mQ; - width = 0.1 / rs->mQ; + cutoff = 0.475 / rs->mQ; + width = 0.05 / rs->mQ; } // A rejection of -180 dB is used for the stop band. l = CalcKaiserOrder(180.0, width) / 2; @@ -1923,12 +1919,13 @@ static int StoreMhr(const HrirDataT *hData, const char *filename) // Calculate the onset time of an HRIR and average it with any existing // timing for its elevation and azimuth. -static void AverageHrirOnset(const double *hrir, const uint n, const double f, const uint ei, const uint ai, const HrirDataT *hData) +static void AverageHrirOnset(const double *hrir, const double f, const uint ei, const uint ai, const HrirDataT *hData) { double mag; - uint i, j; + uint n, i, j; mag = 0.0; + n = hData->mIrPoints; for(i = 0;i < n;i++) mag = fmax(fabs(hrir[i]), mag); mag *= 0.15; @@ -1943,7 +1940,7 @@ static void AverageHrirOnset(const double *hrir, const uint n, const double f, c // Calculate the magnitude response of an HRIR and average it with any // existing responses for its elevation and azimuth. -static void AverageHrirMagnitude(const double *hrir, const uint npoints, const double f, const uint ei, const uint ai, const HrirDataT *hData) +static void AverageHrirMagnitude(const double *hrir, const double f, const uint ei, const uint ai, const HrirDataT *hData) { double *re, *im; uint n, m, i, j; @@ -1951,7 +1948,7 @@ static void AverageHrirMagnitude(const double *hrir, const uint npoints, const d n = hData->mFftSize; re = CreateArray(n); im = CreateArray(n); - for(i = 0;i < npoints;i++) + for(i = 0;i < hData->mIrPoints;i++) { re[i] = hrir[i]; im[i] = 0.0; @@ -2107,6 +2104,23 @@ static void ReconstructHrirs(const HrirDataT *hData) DestroyArray (re); } +// Resamples the HRIRs for use at the given sampling rate. +static void ResampleHrirs(const uint rate, HrirDataT *hData) +{ + uint n, step, start, end, j; + ResamplerT rs; + + ResamplerSetup(&rs, hData->mIrRate, rate); + n = hData->mIrPoints; + step = hData->mIrSize; + start = hData->mEvOffset[hData->mEvStart] * step; + end = hData->mIrCount * step; + for(j = start;j < end;j += step) + ResamplerRun(&rs, n, &hData->mHrirs[j], n, &hData->mHrirs[j]); + ResamplerClear(&rs); + hData->mIrRate = rate; +} + /* Given an elevation index and an azimuth, calculate the indices of the two * HRIRs that bound the coordinate along with a factor for calculating the * continous HRIR using interpolation. @@ -2343,14 +2357,10 @@ static int ProcessMetrics(TokenReaderT *tr, const uint fftSize, const uint trunc return 0; } hData->mIrPoints = points; - hData->mFftSize = fftSize; if(fftSize <= 0) { - points = 1; - while(points < (4 * hData->mIrPoints)) - points <<= 1; - hData->mFftSize = points; - hData->mIrSize = 1 + (points / 2); + hData->mFftSize = DEFAULT_FFTSIZE; + hData->mIrSize = 1 + (DEFAULT_FFTSIZE / 2); } else { @@ -2596,37 +2606,17 @@ static int ReadSourceRef(TokenReaderT *tr, SourceRefT *src) } // Process the list of sources in the data set definition. -static int ProcessSources(const HeadModelT model, const uint dstRate, TokenReaderT *tr, HrirDataT *hData) +static int ProcessSources(const HeadModelT model, TokenReaderT *tr, HrirDataT *hData) { uint *setCount, *setFlag; uint line, col, ei, ai; - uint res_points; SourceRefT src; double factor; double *hrir; - ResamplerT rs; - - ResamplerSetup(&rs, hData->mIrRate, dstRate); - /* Scale the number of IR points for resampling. This could be improved by - * also including space for the resampler build-up and fall-off (rs.mL*2), - * instead of clipping them off, but that could affect the HRTDs. It's not - * a big deal to exclude them for sources that aren't already minimum- - * phase). - */ - res_points = (uint)(((uint64)hData->mIrPoints*dstRate + hData->mIrRate-1) / - hData->mIrRate); - /* Clamp to the IR size to prevent overflow, and don't go less than the - * original point count. - */ - if(res_points > hData->mIrSize) - res_points = hData->mIrSize; - else if(res_points < hData->mIrPoints) - res_points = hData->mIrPoints; setCount = (uint*)calloc(hData->mEvCount, sizeof(uint)); setFlag = (uint*)calloc(hData->mIrCount, sizeof(uint)); - hrir = CreateArray(res_points); - + hrir = CreateArray(hData->mIrPoints); while(TrIsOperator(tr, "[")) { TrIndication(tr, & line, & col); @@ -2651,13 +2641,9 @@ static int ProcessSources(const HeadModelT model, const uint dstRate, TokenReade if(!LoadSource(&src, hData->mIrRate, hData->mIrPoints, hrir)) goto error; - if(hData->mIrRate != dstRate) - ResamplerRun(&rs, hData->mIrPoints, hrir, - res_points, hrir); - if(model == HM_DATASET) - AverageHrirOnset(hrir, res_points, 1.0 / factor, ei, ai, hData); - AverageHrirMagnitude(hrir, res_points, 1.0 / factor, ei, ai, hData); + AverageHrirOnset(hrir, 1.0 / factor, ei, ai, hData); + AverageHrirMagnitude(hrir, 1.0 / factor, ei, ai, hData); factor += 1.0; if(!TrIsOperator(tr, "+")) break; @@ -2666,8 +2652,6 @@ static int ProcessSources(const HeadModelT model, const uint dstRate, TokenReade setFlag[hData->mEvOffset[ei] + ai] = 1; setCount[ei]++; } - hData->mIrPoints = res_points; - hData->mIrRate = dstRate; ei = 0; while(ei < hData->mEvCount && setCount[ei] < 1) @@ -2681,7 +2665,6 @@ static int ProcessSources(const HeadModelT model, const uint dstRate, TokenReade { if(!TrLoad(tr)) { - ResamplerClear(&rs); DestroyArray(hrir); free(setFlag); free(setCount); @@ -2696,7 +2679,6 @@ static int ProcessSources(const HeadModelT model, const uint dstRate, TokenReade TrError(tr, "Missing source references.\n"); error: - ResamplerClear(&rs); DestroyArray(hrir); free(setFlag); free(setCount); @@ -2747,7 +2729,7 @@ static int ProcessDefinition(const char *inName, const uint outRate, const uint } hData.mHrirs = CreateArray(hData.mIrCount * hData.mIrSize); hData.mHrtds = CreateArray(hData.mIrCount); - if(!ProcessSources(model, outRate ? outRate : hData.mIrRate, &tr, &hData)) + if(!ProcessSources(model, &tr, &hData)) { DestroyArray(hData.mHrtds); DestroyArray(hData.mHrirs); @@ -2768,6 +2750,11 @@ static int ProcessDefinition(const char *inName, const uint outRate, const uint } fprintf(stdout, "Performing minimum phase reconstruction...\n"); ReconstructHrirs(&hData); + if(outRate != 0 && outRate != hData.mIrRate) + { + fprintf(stdout, "Resampling HRIRs...\n"); + ResampleHrirs(outRate, &hData); + } fprintf(stdout, "Truncating minimum-phase HRIRs...\n"); hData.mIrPoints = truncSize; fprintf(stdout, "Synthesizing missing elevations...\n"); @@ -2809,8 +2796,7 @@ static void PrintHelp(const char *argv0, FILE *ofile) fprintf(ofile, "Options:\n"); fprintf(ofile, " -r=<rate> Change the data set sample rate to the specified value and\n"); fprintf(ofile, " resample the HRIRs accordingly.\n"); - fprintf(ofile, " -f=<points> Override the FFT window size (defaults to the first power-\n"); - fprintf(ofile, " of-two that fits four times the number of HRIR points).\n"); + fprintf(ofile, " -f=<points> Override the FFT window size (default: %u).\n", DEFAULT_FFTSIZE); fprintf(ofile, " -e={on|off} Toggle diffuse-field equalization (default: %s).\n", (DEFAULT_EQUALIZE ? "on" : "off")); fprintf(ofile, " -s={on|off} Toggle surface-weighted diffuse-field average (default: %s).\n", (DEFAULT_SURFACE ? "on" : "off")); fprintf(ofile, " -l={<dB>|none} Specify a limit to the magnitude range of the diffuse-field\n"); |