28 int i, j, k, nLS, order, nSH;
29 float* ls_dirs_deg, *amp, *en;
30 float** ls_dirs_rad, **decMtx_SAD, **decMtx_MMD, **decMtx_EPAD, **decMtx_AllRAD, **Ysrc, **LSout;
33 const float acceptedTolerance = 0.00001f;
35 int testOrders[10] = {1,2,3,4,5,6,7,8,9,10};
38 for(i=0; i<nTestOrders; i++) {
39 order = testOrders[i];
45 ls_dirs_rad = (
float**)
malloc2d(nLS, 2,
sizeof(
float));
47 ls_dirs_rad[j][0] = ls_dirs_deg[j*2] *
SAF_PI/180.0f;
48 ls_dirs_rad[j][1] =
SAF_PI/2.0f - ls_dirs_deg[j*2+1] *
SAF_PI/180.0f;
52 decMtx_SAD = (
float**)
malloc2d(nLS, nSH,
sizeof(
float));
53 decMtx_MMD = (
float**)
malloc2d(nLS, nSH,
sizeof(
float));
54 decMtx_EPAD = (
float**)
malloc2d(nLS, nSH,
sizeof(
float));
55 decMtx_AllRAD = (
float**)
malloc2d(nLS, nSH,
sizeof(
float));
64 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, decMtx_SAD[j][k], decMtx_MMD[j][k]);
67 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, decMtx_SAD[j][k], decMtx_EPAD[j][k]);
70 Ysrc = (
float**)
malloc2d(nSH, nLS,
sizeof(
float));
72 LSout = (
float**)
malloc2d(nLS, nLS,
sizeof(
float));
73 cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nLS, nLS, nSH, 1.0f,
79 amp = (
float*)
calloc1d(nLS,
sizeof(
float));
80 en = (
float*)
calloc1d(nLS,
sizeof(
float));
81 for (
int idxSrc=0; idxSrc<nLS; idxSrc++) {
82 for (
int idxLS=0; idxLS<nLS; idxLS++) {
83 amp[idxSrc] += LSout[idxLS][idxSrc];
84 en[idxSrc] += LSout[idxLS][idxSrc] * LSout[idxLS][idxSrc];
88 for (
int idxSrc=0; idxSrc<nLS; idxSrc++) {
89 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, amp[idxSrc], 1.0);
90 TEST_ASSERT_FLOAT_WITHIN(acceptedTolerance, en[idxSrc], (
float)nSH / (
float)nLS);
109 float *w_n, *gain, *gainDB;
112 const int order_truncated = 4;
113 const int order_target = 42;
114 const float softThreshold = 12.0f;
115 const int enableMaxRE = 1;
116 const double fs = 48000;
117 const int nBands = 128;
118 kr =
malloc1d(nBands *
sizeof(
double));
119 const double r = 0.085;
120 const double c = 343.;
121 w_n =
calloc1d((order_truncated+1),
sizeof(
float));
122 gain =
malloc1d(nBands *
sizeof(
float));
125 double* freqVector =
malloc1d(nBands*
sizeof(
double));
126 for (
int k=0; k<nBands; k++)
128 freqVector[k] = (double)k * fs/(2.0*((
double)nBands-1));
129 kr[k] = 2.0*
SAF_PId / c * freqVector[k] * r;
133 float *maxRECoeffs =
malloc1d((order_truncated+1) *
sizeof(
float));
135 for (
int idx_n=0; idx_n<order_truncated+1; idx_n++) {
136 w_n[idx_n] = maxRECoeffs[idx_n];
137 w_n[idx_n] /= sqrtf((
float)(2*idx_n+1) / (
FOURPI));
140 for (
int idx_n=0; idx_n<order_truncated+1; idx_n++)
146 for (
int idx_n=0; idx_n<order_truncated+1; idx_n++)
150 truncationEQ(w_n, order_truncated, order_target, kr, nBands, softThreshold, gain);
153 TEST_ASSERT_TRUE(gain[0]-1.0 < 2.0e-6);
156 gainDB =
malloc1d(nBands *
sizeof(
double));
157 for (
int idxBand=0; idxBand<nBands; idxBand++){
158 gainDB[idxBand] = 20.0f*log10f(gain[idxBand]);
159 TEST_ASSERT_TRUE(gainDB[idxBand] > 0-2.0e-6);
160 TEST_ASSERT_TRUE(gainDB[idxBand] < softThreshold + 6.0 + 0-2.0e-6);
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 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 ...
@ 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_EPAD
Energy-Preserving Ambisonic Decoder (EPAD) [1].
@ LOUDSPEAKER_DECODER_MMD
Mode-Matching Decoder (MMD): pseudo-inverse of the loudspeaker spherical harmonic matrix.
#define ORDER2NSH(order)
Converts spherical harmonic order to number of spherical harmonic components i.e: (order+1)^2.
void beamWeightsMaxEV(int N, float *b_n)
Generates beamweights in the SHD for maximum energy-vector beampatterns.
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.
#define SAF_PI
pi constant (single precision)
const float * __HANDLES_Tdesign_dirs_deg[21]
minimum T-design HANDLES (up to degree 21 only).
#define SAF_PId
pi constant (double precision)
#define FOURPI
4pi (single precision)
const int __Tdesign_nPoints_per_degree[21]
Number of points in each t-design (up to degree 21 only).
void ** malloc2d(size_t dim1, size_t dim2, size_t data_size)
2-D malloc (contiguously allocated, so use free() as usual to deallocate)
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)
#define FLATTEN2D(A)
Use this macro when passing a 2-D dynamic multi-dimensional array to memset, memcpy or any other func...
Unit test program for the Spatial_Audio_Framework.
void test__truncationEQ(void)
Testing the truncation EQ.
void test__getLoudspeakerDecoderMtx(void)
Testing to assure that (given a uniform loudspeaker layout), the SAD, MMD and EPAD decoders are all e...