SAF
Loading...
Searching...
No Matches
saf_hades_internal.c
Go to the documentation of this file.
1/*
2 * This file is part of the saf_hades module.
3 * Copyright (c) 2021 - Leo McCormack & Janani Fernandez
4 *
5 * The saf_hades module is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version.
9 *
10 * The saf_hades module is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * See <http://www.gnu.org/licenses/> for a copy of the GNU General Public
16 * License.
17 */
18
38#include "saf_hades_internal.h"
39
40#ifdef SAF_ENABLE_HADES_MODULE
41
43(
44 hades_analysis_handle const hAna,
45 HADES_HRTF_INTERP_OPTIONS interpOption,
46 hades_binaural_config* binConfig,
47 float* target_dirs_deg,
48 int nTargetDirs,
49 float_complex* hrtf_interp
50)
51{
53 int band, i, j, ntable, ntri;
54 int* idx;
55 float* itds_s, *interpTable, *w;
56 float_complex*** hrtf_fb; /* nBands x NUM_EARS x N_dirs */
57
58 /* Pass HRIRs through the filterbank */
59 hrtf_fb = (float_complex***)malloc3d(a->nBands, NUM_EARS, binConfig->nHRIR, sizeof(float_complex));
60 switch(a->fbOpt){
61 case HADES_USE_AFSTFT_LD: HRIRs2HRTFs_afSTFT(binConfig->hrirs, binConfig->nHRIR, binConfig->lHRIR, a->hopsize, 1, a->hybridmode, FLATTEN3D(hrtf_fb)); break;
62 case HADES_USE_AFSTFT: HRIRs2HRTFs_afSTFT(binConfig->hrirs, binConfig->nHRIR, binConfig->lHRIR, a->hopsize, 0, a->hybridmode, FLATTEN3D(hrtf_fb)); break;
63 }
64
65 /* Integration weights */
66 if (cblas_sasum(nTargetDirs, target_dirs_deg+1, 2)/(float)nTargetDirs<0.0001)
67 w = NULL;
68 else{
69 w = malloc1d(nTargetDirs*sizeof(float));
70 getVoronoiWeights(target_dirs_deg, nTargetDirs, 0, w);
71 }
72
73 /* estimate the ITDs for each HRIR */
74 itds_s = malloc1d(binConfig->nHRIR*sizeof(float));
75 estimateITDs(binConfig->hrirs, binConfig->nHRIR, binConfig->lHRIR, binConfig->hrir_fs, itds_s);
76
77 /* Apply HRTF interpolation */
78 switch(interpOption){
80 /* Quantise to nearest hrir direction */
81 idx = malloc1d(nTargetDirs*sizeof(int));
82 findClosestGridPoints(binConfig->hrir_dirs_deg, binConfig->nHRIR, target_dirs_deg, nTargetDirs, 1, idx, NULL, NULL);
83 for(band=0; band<a->nBands; band++)
84 for(i=0; i<NUM_EARS; i++)
85 for(j=0; j<nTargetDirs; j++)
86 hrtf_interp[band*NUM_EARS*nTargetDirs + i*nTargetDirs + j] = hrtf_fb[band][i][idx[j]];
87
88 /* Diffuse-field EQ without phase-simplification */
89 diffuseFieldEqualiseHRTFs(nTargetDirs, NULL, NULL, a->nBands, w, 1, 0, hrtf_interp);
90 free(idx);
91 break;
92
94 /* Diffuse-field EQ with phase-simplification */
95 diffuseFieldEqualiseHRTFs(binConfig->nHRIR, itds_s, a->freqVector, a->nBands, w, 1, 1, FLATTEN3D(hrtf_fb));
96
97 /* Interpolation table */
98 interpTable = NULL;
99 generateVBAPgainTable3D_srcs(target_dirs_deg, nTargetDirs, binConfig->hrir_dirs_deg, binConfig->nHRIR, 0, 0, 0.0f, &interpTable, &ntable, &ntri);
100 VBAPgainTable2InterpTable(interpTable, nTargetDirs, binConfig->nHRIR);
101
102 /* Interpolate */
103 interpHRTFs(FLATTEN3D(hrtf_fb), itds_s, a->freqVector, interpTable, binConfig->nHRIR, a->nBands, nTargetDirs, hrtf_interp);
104
105 /* Clean-up */
106 free(interpTable);
107 break;
108 }
109
110 /* Clean-up */
111 free(itds_s);
112 free(hrtf_fb);
113 free(w);
114}
115
117typedef struct _hades_sdMUSIC_data {
118 int nMics, nDirs;
119 float_complex* VnA;
120 float* grid_dirs_xyz;
121 float* abs_VnA;
122 float* pSpec;
123 float* pSpecInv;
124 float* P_minus_peak;
125 float* VM_mask;
126
128
130(
131 void ** const phMUSIC,
132 int nMics,
133 float* grid_dirs_deg,
134 int nDirs
135)
136{
137 *phMUSIC = malloc1d(sizeof(hades_sdMUSIC_data));
138 hades_sdMUSIC_data *h = (hades_sdMUSIC_data*)(*phMUSIC);
139
140 h->nMics = nMics;
141 h->nDirs = nDirs;
142
143 /* store cartesian coords of scanning directions (for optional peak finding) */
144 h->grid_dirs_xyz = malloc1d(h->nDirs * 3 * sizeof(float));
145 unitSph2cart(grid_dirs_deg, h->nDirs, 1, h->grid_dirs_xyz);
146
147 /* for run-time */
148 h->VnA = malloc1d(h->nMics * (h->nDirs) * sizeof(float_complex));
149 h->abs_VnA = malloc1d(h->nMics * (h->nDirs) * sizeof(float));
150 h->pSpec = malloc1d(h->nDirs*sizeof(float));
151 h->pSpecInv = malloc1d(h->nDirs*sizeof(float));
152 h->P_minus_peak = malloc1d(h->nDirs*sizeof(float));
153 h->VM_mask = malloc1d(h->nDirs*sizeof(float));
154}
155
157(
158 void ** const phMUSIC
159)
160{
161 hades_sdMUSIC_data *h = (hades_sdMUSIC_data*)(*phMUSIC);
162
163 if (h != NULL) {
164 free(h->grid_dirs_xyz);
165 free(h->VnA);
166 free(h->abs_VnA);
167 free(h->pSpec);
168 free(h->pSpecInv);
169 free(h->P_minus_peak);
170 free(h->VM_mask);
171 free(h);
172 h = NULL;
173 *phMUSIC = NULL;
174 }
175}
176
178(
179 void* const hMUSIC,
180 float_complex* A_grid, /* nMics x nGrid */
181 float_complex *Vn, /* nMics x (nMics - nSrcs) */
182 int nSrcs,
183 float* P_music,
184 int* peak_inds
185)
186{
188 int i, k, VnD2, peak_idx;
189 float kappa, scale;
190 float VM_mean[3];
191 const float_complex calpha = cmplxf(1.0f, 0.0f); const float_complex cbeta = cmplxf(0.0f, 0.0f);
192
193 VnD2 = h->nMics - nSrcs; /* noise subspace second dimension length */
194
195 /* derive the pseudo-spectrum value for each grid direction */
196 cblas_cgemm(CblasRowMajor, CblasConjTrans, CblasNoTrans, h->nDirs, VnD2, h->nMics, &calpha,
197 A_grid, h->nDirs,
198 Vn, VnD2, &cbeta,
199 h->VnA, VnD2);
200 utility_cvabs(h->VnA, (h->nDirs)*VnD2, h->abs_VnA);
201 for (i = 0; i < (h->nDirs); i++)
202 h->pSpecInv[i] = cblas_sdot(VnD2, &(h->abs_VnA[i*VnD2]), 1, &(h->abs_VnA[i*VnD2]), 1);
203 //h->pSpec[i] = 1.0f / (h->pSpecInv[i] + 2.23e-10f);
204 utility_svrecip(h->pSpecInv, h->nDirs, h->pSpec);
205
206 /* Output pseudo-spectrum */
207 if(P_music!=NULL)
208 cblas_scopy(h->nDirs, h->pSpec, 1, P_music, 1);
209
210 /* Peak-finding */
211 if(peak_inds!=NULL){
212 kappa = 50.0f;
213 scale = kappa/(2.0f*SAF_PI*expf(kappa)-expf(-kappa));
214 cblas_scopy(h->nDirs, h->pSpec, 1, h->P_minus_peak, 1);
215
216 /* Loop over the number of sources */
217 for(k=0; k<nSrcs; k++){
218 utility_simaxv(h->P_minus_peak, h->nDirs, &peak_idx);
219 peak_inds[k] = peak_idx;
220 if(k==nSrcs-1)
221 break;
222 VM_mean[0] = h->grid_dirs_xyz[peak_idx*3];
223 VM_mean[1] = h->grid_dirs_xyz[peak_idx*3+1];
224 VM_mean[2] = h->grid_dirs_xyz[peak_idx*3+2];
225
226 /* Apply mask for next iteration */
227 cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasTrans, h->nDirs, 1, 3, 1.0f,
228 h->grid_dirs_xyz, 3,
229 (const float*)VM_mean, 3, 0.0f,
230 h->VM_mask, 1);
231 cblas_sscal(h->nDirs, kappa, h->VM_mask, 1);
232 for(i=0; i<h->nDirs; i++)
233 h->VM_mask[i] = expf(h->VM_mask[i]); /* VM distribution */
234 cblas_sscal(h->nDirs, scale, h->VM_mask, 1);
235 for(i=0; i<h->nDirs; i++)
236 h->VM_mask[i] = 1.0f/(0.00001f+(h->VM_mask[i])); /* inverse VM distribution */
237 utility_svvmul(h->P_minus_peak, h->VM_mask, h->nDirs, h->P_minus_peak);
238 }
239 }
240}
241
243(
244 float* lambda,
245 int N
246)
247{
248 int i;
249 float Nord, sum, g_0, mean_ev, g, sumAbsDiff;
250
251 Nord = sqrtf((float)N)-1.0f;
252 sum = 0.0f;
253 for(i=0; i<N; i++)
254 sum += lambda[i];
255 if(sum < 0.0001f) /* FLT_EPS*FLT_MIN -/+ range */
256 return 1.0f;
257 else{
258 g_0 = 2.0f*(powf(Nord+1.0f,2.0f)-1.0f);
259 mean_ev = (1.0f/(powf(Nord+1.0f,2.0f)))*sum;
260 sumAbsDiff = 0.0f;
261 for(i=0; i<N; i++)
262 sumAbsDiff += fabsf(lambda[i]-mean_ev);
263 g = (1.0f/mean_ev)*sumAbsDiff;
264 /* due to numerical error small (10e-7) negative numbers were occuring
265 * sometimes for the single plane-wave case; hence bounding it to >=0 */
266 return SAF_MAX(1.0f-g/g_0, 0.0f);
267 }
268}
269
270#endif /* SAF_ENABLE_HADES_MODULE */
void diffuseFieldEqualiseHRTFs(int N_dirs, float *itds_s, float *centreFreq, int N_bands, float *weights, int applyEQ, int applyPhase, float_complex *hrtfs)
Applies pre-processing to a set of HRTFs, which can either be diffuse-field EQ of an (optionally weig...
Definition saf_hrir.c:174
void interpHRTFs(float_complex *hrtfs, float *itds, float *freqVector, float *interp_table, int N_hrtf_dirs, int N_bands, int N_interp_dirs, float_complex *hrtfs_interp)
Interpolates a set of HRTFs based on a specified interpolation table.
Definition saf_hrir.c:242
void estimateITDs(float *hrirs, int N_dirs, int hrir_len, int fs, float *itds_s)
Estimates the interaural time-differences (ITDs) for each HRIR based on the cross-correlation between...
Definition saf_hrir.c:41
void HRIRs2HRTFs_afSTFT(float *hrirs, int N_dirs, int hrir_len, int hopsize, int LDmode, int hybridmode, float_complex *hrtf_fb)
Passes zero padded HRIRs through the afSTFT filterbank.
Definition saf_hrir.c:111
#define SAF_PI
pi constant (single precision)
void utility_svvmul(const float *a, const float *b, const int len, float *c)
Single-precision, element-wise vector-vector multiplication i.e.
#define SAF_MAX(a, b)
Returns the maximum of the two values.
#define NUM_EARS
2 (true for most humans)
void getVoronoiWeights(float *dirs_deg, int nDirs, int diagFLAG, float *weights)
Computes the integration weights, based on the areas of each face of the corresponding Voronoi diagra...
void utility_cvabs(const float_complex *a, const int len, float *c)
Single-precision, complex, absolute values of vector elements, i.e.
void unitSph2cart(float *dirs, int nDirs, int anglesInDegreesFLAG, float *dirs_xyz)
Converts spherical coordinates to Cartesian coordinates of unit length.
void utility_svrecip(const float *a, const int len, float *c)
Single-precision, vector-reciprocal/inversion, i.e.
void findClosestGridPoints(float *grid_dirs, int nGrid, float *target_dirs, int nTarget, int degFLAG, int *idx_closest, float *dirs_closest, float *angle_diff)
Finds indicies into "grid_dirs" that are the closest to "target dirs".
void utility_simaxv(const float *a, const int len, int *index)
Single-precision, index of maximum absolute value in a vector, i.e.
void generateVBAPgainTable3D_srcs(float *src_dirs_deg, int S, float *ls_dirs_deg, int L, int omitLargeTriangles, int enableDummies, float spread, float **gtable, int *N_gtable, int *nTriangles)
Generates a 3-D VBAP [1] gain table based on specified source and loudspeaker directions,...
Definition saf_vbap.c:53
void VBAPgainTable2InterpTable(float *vbap_gtable, int nTable, int nDirs)
Renormalises a VBAP gain table (in-place) so it may be utilised for interpolation of data (for exampl...
Definition saf_vbap.c:370
void * malloc1d(size_t dim1_data_size)
1-D malloc (same as malloc, but with error checking)
Definition md_malloc.c:59
void *** malloc3d(size_t dim1, size_t dim2, size_t dim3, size_t data_size)
3-D malloc (contiguously allocated, so use free() as usual to deallocate)
Definition md_malloc.c:151
#define FLATTEN3D(A)
Use this macro when passing a 3-D dynamic multi-dimensional array to memset, memcpy or any other func...
Definition md_malloc.h:72
struct _hades_analysis_data * hades_analysis_handle
Handle for the hades analysis data.
@ HADES_USE_AFSTFT
Alias-free STFT filterbank.
@ HADES_USE_AFSTFT_LD
Alias-free STFT filterbank (low delay)
void hades_sdMUSIC_destroy(void **const phMUSIC)
Destroys an instance of the spherical harmonic domain MUSIC implementation, which may be used for com...
void hades_sdMUSIC_create(void **const phMUSIC, int nMics, float *grid_dirs_deg, int nDirs)
Creates an instance of the space-domain MUSIC implementation.
void hades_getInterpolatedHRTFs(hades_analysis_handle const hAna, HADES_HRTF_INTERP_OPTIONS interpOption, hades_binaural_config *binConfig, float *target_dirs_deg, int nTargetDirs, float_complex *hrtf_interp)
Binaural filter interpolator.
void hades_sdMUSIC_compute(void *const hMUSIC, float_complex *A_grid, float_complex *Vn, int nSrcs, float *P_music, int *peak_inds)
Computes a pseudo-spectrum based on the MUSIC algorithm optionally returning the grid indices corresp...
float hades_comedie(float *lambda, int N)
Returns an estimate of the diffuseness, based on [1].
Internal header for the HADES module (SAF_HADES_MODULE)
HADES_HRTF_INTERP_OPTIONS
HRTF interpolation options for hades_synthesis.
@ HADES_HRTF_INTERP_NEAREST
Quantise to nearest measurement.
@ HADES_HRTF_INTERP_TRIANGULAR
Triangular interpolation.
Main structure for hades analysis.
int hybridmode
Optionally, the lowest TF bands may be subdivided to improve low-freq resolution.
float * freqVector
Centre frequencies; nBands x 1.
int hopsize
Filterbank hop size (blocksize must be divisable by this.
HADES_FILTERBANKS fbOpt
see HADES_FILTERBANKS
int nBands
Number of frequency bands.
Binaural configuration struct.
int nHRIR
Number of HRIRs.
int hrir_fs
HRIR sample rate.
int lHRIR
Length of HRIRs in samples.
float * hrir_dirs_deg
HRTF directions in [azimuth elevation] format, in degrees; FLAT: nHRIR x 2.
float * hrirs
Matrix of HRIR data; FLAT: nHRIR x NUM_EARS x lHRIR.
Internal data structure for sdMUSIC.