54 if(order==0 || inConvention == outConvention)
59 cblas_sswap(signalLength, &insig[1*signalLength], 1, &insig[3*signalLength], 1);
60 cblas_sswap(signalLength, &insig[1*signalLength], 1, &insig[2*signalLength], 1);
64 cblas_sswap(signalLength, &insig[1*signalLength], 1, &insig[2*signalLength], 1);
65 cblas_sswap(signalLength, &insig[1*signalLength], 1, &insig[3*signalLength], 1);
69 memset(&insig[i*signalLength], 0, signalLength *
sizeof(
float));
83 if(order==0 || inConvention == outConvention)
88 for (n = 0; n<order+1; n++)
90 cblas_sscal(signalLength, 1.0f/sqrtf(2.0f*(
float)n+1.0f), &insig[ch*signalLength], 1);
93 cblas_sscal(signalLength, 1.0f/sqrtf(2.0f), insig, 1);
94 for (ch = 1; ch<4 ; ch++)
95 cblas_sscal(signalLength, 1.0f/sqrtf(3.0f), &insig[ch*signalLength], 1);
100 for (n = 0; n<order+1; n++)
102 cblas_sscal(signalLength, sqrtf(2.0f*(
float)n+1.0f), &insig[ch*signalLength], 1);
105 cblas_sscal(signalLength, 1.0f/sqrtf(2.0f), insig, 1);
109 cblas_sscal(signalLength, sqrtf(2.0f), insig, 1);
110 for (ch = 1; ch<4 ; ch++)
111 cblas_sscal(signalLength, sqrtf(3.0f), &insig[ch*signalLength], 1);
114 cblas_sscal(signalLength, sqrtf(2.0f), insig, 1);
134 scale = sqrtf(4.0f*
SAF_PI);
137 dirs_rad =
malloc1d(nDirs*2*
sizeof(
float));
138 for(i=0; i<nDirs; i++){
139 dirs_rad[i*2+0] = dirs_deg[i*2+0] *
SAF_PI/180.0f;
140 dirs_rad[i*2+1] =
SAF_PI/2.0f - (dirs_deg[i*2+1] *
SAF_PI/180.0f);
160 int n, m, i, dir, index_n;
162 float sleg_n[11], sleg_n_1[11], sleg_n_2[11], ssin_el, sfactorials_n[21];
163 float* leg_n, *leg_n_1, *leg_n_2, *sin_el, *factorials_n;
168 if(N<=10 && nDirs == 1){
174 factorials_n = sfactorials_n;
177 factorials_n =
malloc1d((2*N+1)*
sizeof(
float));
178 leg_n =
malloc1d((N+1)*nDirs *
sizeof(
float));
179 leg_n_1 =
malloc1d((N+1)*nDirs *
sizeof(
float));
180 leg_n_2 =
malloc1d((N+1)*nDirs *
sizeof(
float));
181 sin_el =
malloc1d(nDirs *
sizeof(
float));
186 for (i = 0; i < 2*N+1; i++)
190 for (dir = 0; dir<nDirs; dir++)
191 sin_el[dir] = sinf(dirs_deg[dir*2+1] *
SAF_PI/180.0f);
194 for (n = 0; n<N+1; n++) {
196 for (dir = 0; dir<nDirs; dir++)
197 Y[n*nDirs+dir] = 1.0f;
203 Nn0 = sqrtf(2.0f*(
float)n+1.0f);
204 for (dir = 0; dir<nDirs; dir++){
205 for (m = 0; m<n+1; m++) {
207 Y[(index_n+n)*nDirs+dir] = Nn0 * leg_n[m*nDirs+dir];
209 Nnm = Nn0* sqrtf( 2.0f * factorials_n[n-m]/factorials_n[n+m] );
210 Y[(index_n+n-m)*nDirs+dir] = Nnm * leg_n[m*nDirs+dir] * sinf((
float)m * (dirs_deg[dir*2])*
SAF_PI/180.0f);
211 Y[(index_n+n+m)*nDirs+dir] = Nnm * leg_n[m*nDirs+dir] * cosf((
float)m * (dirs_deg[dir*2])*
SAF_PI/180.0f);
221 if(N>10 || nDirs > 1){
246 x = cosf(137.9f*(
SAF_PI/180.0f)/((
float)order+1.51f));
249 memset(a_n, 0, nSH*nSH*
sizeof(
float));
251 memset(a_n, 0, nSH*
sizeof(
float));
252 ppm =
calloc1d((order+1),
sizeof(
double));
254 for(n=0; n<=order; n++){
258 for(i = 0; i<2*n+1; i++){
260 a_n[(idx+i)*nSH + (idx+i)] = (float)ppm[0];
262 a_n[(idx+i)] = (
float)ppm[0];
286 double_complex* b_n_target =
calloc1d(nBands*(order_target+1),
sizeof(double_complex));
287 double_complex* b_n_truncated =
calloc1d(nBands*(order_truncated+1),
sizeof(double_complex));
288 double* p_target =
calloc1d(nBands,
sizeof(
double));
289 double* p_truncated =
calloc1d(nBands,
sizeof(
double));
295 for (
int idxBand=0; idxBand<nBands; idxBand++)
296 for (
int idxOrder=0; idxOrder<=order_target; idxOrder++)
297 p_target[idxBand] += (2.0*idxOrder + 1.0) * pow(cabs(b_n_target[idxBand*(order_target+1) + idxOrder]), 2.0);
299 for (
int idxBand=0; idxBand<nBands; idxBand++)
300 for (
int idxOrder=0; idxOrder<=order_truncated; idxOrder++)
301 p_truncated[idxBand] += w_n[idxOrder] * (2.0*idxOrder + 1.0) * pow(cabs(b_n_truncated[idxBand*(order_truncated+1) + idxOrder]), 2.0);
304 for (
int idxBand=0; idxBand < nBands; idxBand++) {
305 p_target[idxBand] = 1.0/(4.0*
SAF_PI) * sqrt(p_target[idxBand]);
306 p_truncated[idxBand] = 1.0/(4.0*
SAF_PI) * sqrt(p_truncated[idxBand]);
307 gain[idxBand] = (float)(p_target[idxBand] / (p_truncated[idxBand]+2.23e-13));
311 float clipFactor = powf(10.0f, softThreshold/20.0f);
312 for (
int idxBand=0; idxBand<nBands; idxBand++){
313 gain[idxBand] = gain[idxBand]/clipFactor;
314 if (gain[idxBand] > 1.0f)
315 gain[idxBand] = 1.0f + tanhf(gain[idxBand] - 1.0f);
316 gain[idxBand] = gain[idxBand] * clipFactor;
332 int enableMaxReWeighting,
338 float* Y_ls, *a_n, *decMtx_maxrE;
350 Y_ls =
malloc1d(nSH*nLS*
sizeof(
float));
351 getRSH(order, ls_dirs_deg, nLS, Y_ls);
352 cblas_sscal(nLS*nSH, scale, Y_ls, 1);
355 decMtx[i*nSH+j] = (4.0f*
SAF_PI) * Y_ls[j*nLS + i]/(float)nLS;
362 Y_ls =
malloc1d(nSH*nLS*
sizeof(
float));
363 getRSH(order, ls_dirs_deg, nLS, Y_ls);
364 cblas_sscal(nLS*nSH, scale, Y_ls, 1);
370 getEPAD(order, ls_dirs_deg, nLS, decMtx);
374 getAllRAD(order, ls_dirs_deg, nLS, decMtx);
379 if(enableMaxReWeighting){
380 a_n =
malloc1d(nSH*nSH*
sizeof(
float));
382 decMtx_maxrE =
malloc1d(nLS * nSH *
sizeof(
float));
383 cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nLS, nSH, nSH, 1.0f,
387 memcpy(decMtx, decMtx_maxrE, nLS*nSH*
sizeof(
float));
396 float_complex* hrtfs,
397 float* hrtf_dirs_deg,
405 int enableDiffCovMatching,
406 int enableMaxReWeighting,
407 float_complex* decMtx
412 float_complex* a_n, *decMtx_rE;
413 const float_complex calpha = cmplxf(1.0f, 0.0f), cbeta = cmplxf(0.0f, 0.0f);
428 if(enableMaxReWeighting){
429 tmp =
malloc1d(nSH*nSH*
sizeof(
float));
430 a_n =
malloc1d(nSH*nSH*
sizeof(float_complex));
433 for(i=0; i<nSH*nSH; i++)
434 a_n[i] = cmplxf(tmp[i], 0.0f);
435 for(k=0; k<N_bands; k++){
436 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans,
NUM_EARS, nSH, nSH, &calpha,
440 memcpy(&decMtx[k*
NUM_EARS*nSH], decMtx_rE,
NUM_EARS*nSH*
sizeof(float_complex));
448 if(enableDiffCovMatching)
454 float_complex* hrtfs,
455 float* hrtf_dirs_deg,
463 int enableDiffCovMatching,
464 int enableMaxReWeighting,
468 int i, j, k, nBins, nSH;
470 float_complex* decMtx, *decMtx_bins;
474 nBins = fftSize/2 + 1;
475 freqVector =
malloc1d(nBins*
sizeof(
float));
482 order, freqVector, itd_s, weights, enableDiffCovMatching,
483 enableMaxReWeighting, decMtx);
486 decMtx_bins =
malloc1d(nBins*
sizeof(float_complex));
489 for(j=0; j<nSH; j++){
490 for(k=0; k<nBins; k++)
491 decMtx_bins[k] = decMtx[k*
NUM_EARS*nSH + i*nSH + j];
504 float_complex* hrtfs,
505 float* hrtf_dirs_deg,
510 float_complex* decMtx
515 float_complex* W, *Y_na, *H_W, *H_ambi, *decMtx_diffMatched;
521 const float_complex calpha = cmplxf(1.0f, 0.0f), cbeta = cmplxf(0.0f, 0.0f);
526 W =
calloc1d(N_dirs*N_dirs,
sizeof(float_complex));
528 for(i=0; i<N_dirs; i++)
529 W[i*N_dirs+i] = cmplxf(weights[i], 0.0f);
531 for(i=0; i<N_dirs; i++)
532 W[i*N_dirs+i] = cmplxf(1.0f/(
float)N_dirs, 0.0f);
535 Y_tmp =
malloc1d(nSH*N_dirs*
sizeof(
float));
536 Y_na =
malloc1d(nSH*N_dirs*
sizeof(float_complex));
537 getRSH(order, hrtf_dirs_deg, N_dirs, Y_tmp);
538 for(i=0; i<nSH*N_dirs; i++)
539 Y_na[i] = cmplxf(Y_tmp[i], 0.0f);
546 for(band=0; band<N_bands-1 ; band++){
548 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans,
NUM_EARS, N_dirs, N_dirs, &calpha,
549 &hrtfs[band*
NUM_EARS*N_dirs], N_dirs,
552 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasConjTrans,
NUM_EARS,
NUM_EARS, N_dirs, &calpha,
554 &hrtfs[band*
NUM_EARS*N_dirs], N_dirs, &cbeta,
557 C_ref[i][i] = cmplxf(crealf(C_ref[i][i]), 0.0f);
559 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans,
NUM_EARS, N_dirs, nSH, &calpha,
561 Y_na, N_dirs, &cbeta,
563 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans,
NUM_EARS, N_dirs, N_dirs, &calpha,
567 cblas_cgemm(CblasRowMajor, CblasNoTrans, CblasConjTrans,
NUM_EARS,
NUM_EARS, N_dirs, &calpha,
569 H_ambi, N_dirs, &cbeta,
572 C_ambi[i][i] = cmplxf(crealf(C_ambi[i][i]), 0.0f);
578 (float_complex*)X,
NUM_EARS, &cbeta,
579 (float_complex*)XH_Xambi,
NUM_EARS);
585 (float_complex*)X,
NUM_EARS, &cbeta,
589 (float_complex*)UX,
NUM_EARS, &cbeta,
592 cblas_cgemm(CblasRowMajor, CblasConjTrans, CblasNoTrans,
NUM_EARS, nSH,
NUM_EARS, &calpha,
594 &decMtx[band*
NUM_EARS*nSH], nSH, &cbeta,
595 decMtx_diffMatched, nSH);
596 memcpy(&decMtx[band*
NUM_EARS*nSH], decMtx_diffMatched,
NUM_EARS*nSH*
sizeof(float_complex));
603 free(decMtx_diffMatched);
void applyDiffCovMatching(float_complex *hrtfs, float *hrtf_dirs_deg, int N_dirs, int N_bands, int order, float *weights, float_complex *decMtx)
Imposes a diffuse-field covariance constraint on a given binaural decoding matrix,...
void getBinauralAmbiDecoderMtx(float_complex *hrtfs, float *hrtf_dirs_deg, int N_dirs, int N_bands, BINAURAL_AMBI_DECODER_METHODS method, int order, float *freqVector, float *itd_s, float *weights, int enableDiffCovMatching, int enableMaxReWeighting, float_complex *decMtx)
Computes binaural ambisonic decoding matrices (one per frequency) at a specific order,...
void convertHOAChannelConvention(float *insig, int order, int signalLength, HOA_CH_ORDER inConvention, HOA_CH_ORDER outConvention)
Converts an Ambisonic signal from one channel ordering convention to another.
HOA_CH_ORDER
Available Ambisonic channel ordering conventions.
void getLoudspeakerDecoderMtx(float *ls_dirs_deg, int nLS, LOUDSPEAKER_AMBI_DECODER_METHODS method, int order, int enableMaxReWeighting, float *decMtx)
Computes an ambisonic decoding matrix of a specific order, for a given loudspeaker layout.
void getBinauralAmbiDecoderFilters(float_complex *hrtfs, float *hrtf_dirs_deg, int N_dirs, int fftSize, float fs, BINAURAL_AMBI_DECODER_METHODS method, int order, float *itd_s, float *weights, int enableDiffCovMatching, int enableMaxReWeighting, float *decFilters)
Computes binaural ambisonic decoding filters for a given HRTF set.
LOUDSPEAKER_AMBI_DECODER_METHODS
Ambisonic decoding options for loudspeaker playback.
void getRSH(int N, float *dirs_deg, int nDirs, float *Y)
Computes real-valued spherical harmonics [1] for each given direction on the unit sphere.
HOA_NORM
Available Ambisonic normalisation conventions.
void convertHOANormConvention(float *insig, int order, int signalLength, HOA_NORM inConvention, HOA_NORM outConvention)
Converts an Ambisonic signal from one normalisation convention to another.
BINAURAL_AMBI_DECODER_METHODS
Ambisonic decoding options for binaural/headphone playback.
void getRSH_recur(int N, float *dirs_deg, int nDirs, float *Y)
Computes real-valued spherical harmonics [1] for each given direction on the unit sphere.
void getMaxREweights(int order, int diagMtxFlag, float *a_n)
Computes the weights required to manipulate a hyper-cardioid beam-pattern, such that it has maximum e...
void truncationEQ(float *w_n, int order_truncated, int order_target, double *kr, int nBands, float softThreshold, float *gain)
Filter that equalises the high frequency roll-off due to SH truncation and tapering; as described in ...
@ HOA_CH_ORDER_FUMA
Furse-Malham (FuMa) convention, often used by older recordings.
@ HOA_CH_ORDER_ACN
Ambisonic Channel numbering (ACN) convention, which is employed by all spherical harmonic related fun...
@ LOUDSPEAKER_DECODER_ALLRAD
All-Round Ambisonic Decoder (AllRAD): SAD decoding to a t-design, panned for the target loudspeaker d...
@ LOUDSPEAKER_DECODER_SAD
Sampling Ambisonic Decoder (SAD): transpose of the loudspeaker spherical harmonic matrix,...
@ LOUDSPEAKER_DECODER_DEFAULT
The default decoder is LOUDSPEAKER_DECODER_SAD.
@ LOUDSPEAKER_DECODER_EPAD
Energy-Preserving Ambisonic Decoder (EPAD) [1].
@ LOUDSPEAKER_DECODER_MMD
Mode-Matching Decoder (MMD): pseudo-inverse of the loudspeaker spherical harmonic matrix.
@ HOA_NORM_FUMA
Furse-Malham (FuMa) convention.
@ HOA_NORM_SN3D
Schmidt semi-normalisation (SN3D) convention, as used by the AmbiX standard.
@ HOA_NORM_N3D
Orthonormalised (N3D) convention, which is the default convention used by SAF.
@ BINAURAL_DECODER_MAGLS
Magnitude least-squares decoder [3].
@ BINAURAL_DECODER_LS
Least-squares (LS) decoder.
@ BINAURAL_DECODER_SPR
Spatial resampling decoder (on the same lines as the virtual loudspeaker approach) [4].
@ BINAURAL_DECODER_DEFAULT
The default decoder is BINAURAL_DECODER_LS.
@ BINAURAL_DECODER_TA
Time-alignment decoder [2].
@ BINAURAL_DECODER_LSDIFFEQ
Least-squares (LS) decoder with diffuse-field spectral equalisation [1].
void unnorm_legendreP(int n, double *x, int lenX, double *y)
Calculates unnormalised Legendre polynomials up to order N, for all values in vector x [1].
#define ORDER2NSH(order)
Converts spherical harmonic order to number of spherical harmonic components i.e: (order+1)^2.
void unnorm_legendreP_recur(int n, float *x, int lenX, float *Pnm_minus1, float *Pnm_minus2, float *Pnm)
Calculates unnormalised Legendre polynomials up to order N, for all values in vector x.
void sphModalCoeffs(int order, double *kr, int nBands, ARRAY_CONSTRUCTION_TYPES arrayType, double dirCoeff, double_complex *b_N)
Calculates the modal coefficients for open/rigid spherical arrays.
void getSHreal(int order, float *dirs_rad, int nDirs, float *Y)
Computes real-valued spherical harmonics [1] for each given direction on the unit sphere.
@ ARRAY_CONSTRUCTION_RIGID
Rigid baffle, omni-directional sensors.
#define SAF_PI
pi constant (single precision)
void utility_cglslv(void *const hWork, const float_complex *A, const int dim, float_complex *B, int nCol, float_complex *X)
General linear solver: single precision complex, i.e.
#define NUM_EARS
2 (true for most humans)
void utility_svvcopy(const float *a, const int len, float *c)
Single-precision, vector-vector copy, i.e.
void saf_rfft_create(void **const phFFT, int N)
Creates an instance of saf_rfft; real<->half-complex (conjugate-symmetric) FFT.
void utility_spinv(void *const hWork, const float *inM, const int dim1, const int dim2, float *outM)
General matrix pseudo-inverse (the svd way): single precision, i.e.
void utility_csvd(void *const hWork, const float_complex *A, const int dim1, const int dim2, float_complex *U, float_complex *S, float_complex *V, float *sing)
Singular value decomposition: single precision complex, i.e.
void getUniformFreqVector(int fftSize, float fs, float *freqVector)
Calculates the frequencies (in Hz) of uniformly spaced bins, for a given FFT size and sampling rate.
long double factorial(int n)
Factorial, accurate up to n<=25.
void saf_rfft_backward(void *const hFFT, float_complex *inputFD, float *outputTD)
Performs the backward-FFT operation; use for complex (conjugate symmetric) to real transformations.
void saf_rfft_destroy(void **const phFFT)
Destroys an instance of saf_rfft.
void utility_cchol(void *const hWork, const float_complex *A, const int dim, float_complex *X)
Cholesky factorisation of a hermitian positive-definate matrix: single precision complex,...
#define SQRT4PI
sqrt(4pi) (single precision)
void utility_svsmul(float *a, const float *s, const int len, float *c)
Single-precision, multiplies each element in vector 'a' with a scalar 's', i.e.
void * malloc1d(size_t dim1_data_size)
1-D malloc (same as malloc, but with error checking)
void * calloc1d(size_t dim1, size_t data_size)
1-D calloc (same as calloc, but with error checking)
Main header for the higher-order Ambisonics module (SAF_HOA_MODULE)
void getBinDecoder_TA(float_complex *hrtfs, float *hrtf_dirs_deg, int N_dirs, int N_bands, int order, float *freqVector, float *itd_s, float *weights, float_complex *decMtx)
Computes a binaural ambisonic decoder based on the time-alignment (TA) method described in [1].
void getEPAD(int order, float *ls_dirs_deg, int nLS, float *decMtx)
Computes the Energy preserving Ambisonic decoder (EPAD), as detailed in [1].
void getBinDecoder_LSDIFFEQ(float_complex *hrtfs, float *hrtf_dirs_deg, int N_dirs, int N_bands, int order, float *weights, float_complex *decMtx)
Computes a least-squares (LS) binaural ambisonic decoder with diffuse-field equalisation [1].
void getBinDecoder_SPR(float_complex *hrtfs, float *hrtf_dirs_deg, int N_dirs, int N_bands, int order, float *weights, float_complex *decMtx)
Computes a binaural ambisonic decoder based on spatial resampling (i.e, virtual loudspeaker decoding)...
void getBinDecoder_LS(float_complex *hrtfs, float *hrtf_dirs_deg, int N_dirs, int N_bands, int order, float *weights, float_complex *decMtx)
Computes a standard least-squares (LS) binaural ambisonic decoder.
void getBinDecoder_MAGLS(float_complex *hrtfs, float *hrtf_dirs_deg, int N_dirs, int N_bands, int order, float *freqVector, float *weights, float_complex *decMtx)
Computes a binaural ambisonic decoder based on the magnitude least-squares (MagLS) method,...
void getAllRAD(int order, float *ls_dirs_deg, int nLS, float *decMtx)
Computes the All-round Ambisonics decoder (AllRAD), as detailed in [1], which is essentially a spheri...
Internal header for the higher-order Ambisonics module (SAF_HOA_MODULE)