SAF
Loading...
Searching...
No Matches
beamformer.c
Go to the documentation of this file.
1/*
2 * Copyright 2019 Leo McCormack
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10 * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
13 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14 * PERFORMANCE OF THIS SOFTWARE.
15 */
16
26
27#include "beamformer_internal.h"
28
30(
31 void ** const phBeam
32)
33{
35 *phBeam = (void*)pData;
36 int i, ch;
37
38 /* default user parameters */
39 pData->beamOrder = 1;
40 for(i=0; i<MAX_NUM_BEAMS; i++){
41 pData->beam_dirs_deg[i][0] = __default_LScoords128_deg[i][0];
42 pData->beam_dirs_deg[i][1] = (__default_LScoords128_deg[i][1]*(SAF_PI/180.0f) - SAF_PI/2.0f) < -SAF_PI/2.0f ?
43 (SAF_PI/2.0f + __default_LScoords128_deg[i][1]*(SAF_PI/180.0f)) : (__default_LScoords128_deg[i][1]*(SAF_PI/180.0f) - SAF_PI/2.0f);
44 pData->beam_dirs_deg[i][1] *= 180.0f/SAF_PI;
45 }
46 pData->nBeams = 1;
48 pData->chOrdering = CH_ACN;
49 pData->norm = NORM_SN3D;
50
51 /* flags */
52 for(ch=0; ch<MAX_NUM_BEAMS; ch++)
53 pData->recalc_beamWeights[ch] = 1;
54}
55
57(
58 void ** const phBeam
59)
60{
61 beamformer_data *pData = (beamformer_data*)(*phBeam);
62
63 if (pData != NULL) {
64
65 free(pData);
66 pData = NULL;
67 *phBeam = NULL;
68 }
69}
70
72(
73 void * const hBeam,
74 int sampleRate
75)
76{
77 beamformer_data *pData = (beamformer_data*)(hBeam);
78 int i, ch;
79
80 /* define frequency vector */
81 pData->fs = sampleRate;
82
83 /* defaults */
84 memset(pData->beamWeights, 0, MAX_NUM_BEAMS*MAX_NUM_SH_SIGNALS*sizeof(float));
85 memset(pData->prev_beamWeights, 0, MAX_NUM_BEAMS*MAX_NUM_SH_SIGNALS*sizeof(float));
86 for(ch=0; ch<MAX_NUM_BEAMS; ch++)
87 pData->recalc_beamWeights[ch] = 1;
88 for(i=1; i<=BEAMFORMER_FRAME_SIZE; i++){
89 pData->interpolator_fadeIn[i-1] = (float)i*1.0f/(float)BEAMFORMER_FRAME_SIZE;
90 pData->interpolator_fadeOut[i-1] = 1.0f - pData->interpolator_fadeIn[i-1];
91 }
92}
93
95(
96 void * const hBeam,
97 const float *const * inputs,
98 float* const* const outputs,
99 int nInputs,
100 int nOutputs,
101 int nSamples
102)
103{
104 beamformer_data *pData = (beamformer_data*)(hBeam);
105 int ch, i, bi, nSH, mixWithPreviousFLAG;
106 float c_n[MAX_SH_ORDER+1];
107
108 /* local copies of user parameters */
109 int nBeams, beamOrder;
110 NORM_TYPES norm;
111 CH_ORDER chOrdering;
112 beamOrder = pData->beamOrder;
113 nSH = ORDER2NSH(beamOrder);
114 nBeams = pData->nBeams;
115 norm = pData->norm;
116 chOrdering = pData->chOrdering;
117
118 /* Apply beamformer */
119 if(nSamples == BEAMFORMER_FRAME_SIZE) {
120 /* Load time-domain data */
121 for(i=0; i < SAF_MIN(nSH, nInputs); i++)
122 utility_svvcopy(inputs[i], BEAMFORMER_FRAME_SIZE, pData->SHFrameTD[i]);
123 for(; i<MAX_NUM_SH_SIGNALS; i++)
124 memset(pData->SHFrameTD[i], 0, BEAMFORMER_FRAME_SIZE * sizeof(float)); /* fill remaining channels with zeros */
125
126 /* account for input channel order convention */
127 switch(chOrdering){
128 case CH_ACN: /* already ACN, do nothing*/ break; /* Otherwise, convert to ACN... */
130 }
131
132 /* account for input normalisation scheme */
133 switch(norm){
134 case NORM_N3D: /* already in N3D, do nothing */ break; /* Otherwise, convert to N3D... */
137 }
138
139 /* Calculate beamforming coeffients */
140 mixWithPreviousFLAG = 0;
141 for(bi=0; bi<nBeams; bi++){
142 if(pData->recalc_beamWeights[bi]){
143 memset(pData->beamWeights[bi], 0, MAX_NUM_SH_SIGNALS*sizeof(float));
144 switch(pData->beamType){
145 case STATIC_BEAM_TYPE_CARDIOID: beamWeightsCardioid2Spherical(beamOrder, c_n); break;
147 case STATIC_BEAM_TYPE_MAX_EV: beamWeightsMaxEV(beamOrder, c_n); break;
148 }
149 rotateAxisCoeffsReal(beamOrder, (float*)c_n, SAF_PI/2.0f - pData->beam_dirs_deg[bi][1]*SAF_PI/180.0f,
150 pData->beam_dirs_deg[bi][0]*SAF_PI/180.0f, (float*)pData->beamWeights[bi]);
151 pData->recalc_beamWeights[bi] = 0;
152 mixWithPreviousFLAG = 1;
153 }
154 }
155
156 /* Apply beam weights */
157 cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nBeams, BEAMFORMER_FRAME_SIZE, nSH, 1.0f,
158 (const float*)pData->beamWeights, MAX_NUM_SH_SIGNALS,
159 (const float*)pData->SHFrameTD, BEAMFORMER_FRAME_SIZE, 0.0f,
160 (float*)pData->outputFrameTD, BEAMFORMER_FRAME_SIZE);
161
162 /* Fade between (linearly inerpolate) the new weights and the previous weights (only if the new weights are different) */
163 if(mixWithPreviousFLAG){
164 cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nBeams, BEAMFORMER_FRAME_SIZE, nSH, 1.0f,
165 (float*)pData->prev_beamWeights, MAX_NUM_SH_SIGNALS,
166 (float*)pData->SHFrameTD, BEAMFORMER_FRAME_SIZE, 0.0f,
167 (float*)pData->tempFrame, BEAMFORMER_FRAME_SIZE);
168
169 /* Apply the linear interpolation */
170 for (i=0; i < nBeams; i++){
171 utility_svvmul((float*)pData->interpolator_fadeIn, (float*)pData->outputFrameTD[i], BEAMFORMER_FRAME_SIZE, (float*)pData->outputFrameTD_fadeIn[i]);
172 utility_svvmul((float*)pData->interpolator_fadeOut, (float*)pData->tempFrame[i], BEAMFORMER_FRAME_SIZE, (float*)pData->tempFrame_fadeOut[i]);
173 }
174 cblas_scopy(nBeams*BEAMFORMER_FRAME_SIZE, (float*)pData->outputFrameTD_fadeIn, 1, (float*)pData->outputFrameTD, 1);
175 cblas_saxpy(nBeams*BEAMFORMER_FRAME_SIZE, 1.0f, (float*)pData->tempFrame_fadeOut, 1, (float*)pData->outputFrameTD, 1);
176
177 /* for next frame */
178 utility_svvcopy((const float*)pData->beamWeights, MAX_NUM_BEAMS*MAX_NUM_SH_SIGNALS, (float*)pData->prev_beamWeights);
179 }
180
181 /* copy to output buffer */
182 for(ch = 0; ch < SAF_MIN(nBeams, nOutputs); ch++)
183 utility_svvcopy(pData->outputFrameTD[ch], BEAMFORMER_FRAME_SIZE, outputs[ch]);
184 for (; ch < nOutputs; ch++)
185 memset(outputs[ch], 0, BEAMFORMER_FRAME_SIZE*sizeof(float));
186 }
187 else
188 for (ch=0; ch < nOutputs; ch++)
189 memset(outputs[ch], 0, BEAMFORMER_FRAME_SIZE*sizeof(float));
190}
191
192
193/* Set Functions */
194
195void beamformer_refreshSettings(void* const hBeam)
196{
197 beamformer_data *pData = (beamformer_data*)(hBeam);
198 int ch;
199 for(ch=0; ch<MAX_NUM_BEAMS; ch++)
200 pData->recalc_beamWeights[ch] = 1;
201}
202
203void beamformer_setBeamOrder(void * const hBeam, int newValue)
204{
205 beamformer_data *pData = (beamformer_data*)(hBeam);
206 int ch;
207 pData->beamOrder = SAF_MIN(SAF_MAX(newValue,1), MAX_SH_ORDER);
208 for(ch=0; ch<MAX_NUM_BEAMS; ch++)
209 pData->recalc_beamWeights[ch] = 1;
210 /* FUMA only supports 1st order */
211 if(pData->beamOrder!=SH_ORDER_FIRST && pData->chOrdering == CH_FUMA)
212 pData->chOrdering = CH_ACN;
213 if(pData->beamOrder!=SH_ORDER_FIRST && pData->norm == NORM_FUMA)
214 pData->norm = NORM_SN3D;
215}
216
217void beamformer_setBeamAzi_deg(void* const hBeam, int index, float newAzi_deg)
218{
219 beamformer_data *pData = (beamformer_data*)(hBeam);
220 if(newAzi_deg>180.0f)
221 newAzi_deg = -360.0f + newAzi_deg;
222 newAzi_deg = SAF_MAX(newAzi_deg, -180.0f);
223 newAzi_deg = SAF_MIN(newAzi_deg, 180.0f);
224 pData->beam_dirs_deg[index][0] = newAzi_deg;
225 pData->recalc_beamWeights[index] = 1;
226}
227
228void beamformer_setBeamElev_deg(void* const hBeam, int index, float newElev_deg)
229{
230 beamformer_data *pData = (beamformer_data*)(hBeam);
231 newElev_deg = SAF_MAX(newElev_deg, -90.0f);
232 newElev_deg = SAF_MIN(newElev_deg, 90.0f);
233 pData->beam_dirs_deg[index][1] = newElev_deg;
234 pData->recalc_beamWeights[index] = 1;
235}
236
237void beamformer_setNumBeams(void* const hBeam, int new_nBeams)
238{
239 beamformer_data *pData = (beamformer_data*)(hBeam);
240 int ch;
241 if(pData->nBeams != new_nBeams){
242 pData->nBeams = new_nBeams;
243 for(ch=0; ch<MAX_NUM_BEAMS; ch++)
244 pData->recalc_beamWeights[ch] = 1;
245 }
246}
247
248void beamformer_setChOrder(void* const hBeam, int newOrder)
249{
250 beamformer_data *pData = (beamformer_data*)(hBeam);
251 if((CH_ORDER)newOrder != CH_FUMA || pData->beamOrder==SH_ORDER_FIRST)/* FUMA only supports 1st order */
252 pData->chOrdering = (CH_ORDER)newOrder;
253}
254
255void beamformer_setNormType(void* const hBeam, int newType)
256{
257 beamformer_data *pData = (beamformer_data*)(hBeam);
258 if((NORM_TYPES)newType != NORM_FUMA || pData->beamOrder==SH_ORDER_FIRST)/* FUMA only supports 1st order */
259 pData->norm = (NORM_TYPES)newType;
260}
261
262void beamformer_setBeamType(void* const hBeam, int newID)
263{
264 beamformer_data *pData = (beamformer_data*)(hBeam);
265 int ch;
266 pData->beamType = newID;
267 for(ch=0; ch<MAX_NUM_BEAMS; ch++)
268 pData->recalc_beamWeights[ch] = 1;
269}
270
271/* Get Functions */
272
274{
276}
277
278int beamformer_getBeamOrder(void * const hBeam)
279{
280 beamformer_data *pData = (beamformer_data*)(hBeam);
281 return pData->beamOrder;
282}
283
284float beamformer_getBeamAzi_deg(void* const hBeam, int index)
285{
286 beamformer_data *pData = (beamformer_data*)(hBeam);
287 return pData->beam_dirs_deg[index][0];
288}
289
290float beamformer_getBeamElev_deg(void* const hBeam, int index)
291{
292 beamformer_data *pData = (beamformer_data*)(hBeam);
293 return pData->beam_dirs_deg[index][1];
294}
295
296int beamformer_getNumBeams(void* const hBeam)
297{
298 beamformer_data *pData = (beamformer_data*)(hBeam);
299 return pData->nBeams;
300}
301
303{
304 return MAX_NUM_BEAMS;
305}
306
307int beamformer_getNSHrequired(void* const hBeam)
308{
309 beamformer_data *pData = (beamformer_data*)(hBeam);
310 return ORDER2NSH(pData->beamOrder);
311}
312
313int beamformer_getChOrder(void* const hBeam)
314{
315 beamformer_data *pData = (beamformer_data*)(hBeam);
316 return (int)pData->chOrdering;
317}
318
319int beamformer_getNormType(void* const hBeam)
320{
321 beamformer_data *pData = (beamformer_data*)(hBeam);
322 return (int)pData->norm;
323}
324
325int beamformer_getBeamType(void* const hBeam)
326{
327 beamformer_data *pData = (beamformer_data*)(hBeam);
328 return pData->beamType;
329}
330
335
336
#define MAX_SH_ORDER
Maximum supported Ambisonic order.
Definition _common.h:52
@ STATIC_BEAM_TYPE_MAX_EV
hyper-cardioid with max_rE weighting
Definition _common.h:171
@ STATIC_BEAM_TYPE_HYPERCARDIOID
hyper-cardioid
Definition _common.h:170
@ STATIC_BEAM_TYPE_CARDIOID
cardioid
Definition _common.h:169
NORM_TYPES
Available Ambisonic normalisation conventions.
Definition _common.h:74
@ NORM_SN3D
Schmidt semi-normalisation (SN3D)
Definition _common.h:76
@ NORM_FUMA
(Legacy) Furse-Malham scaling
Definition _common.h:77
@ NORM_N3D
orthonormalised (N3D)
Definition _common.h:75
CH_ORDER
Available Ambisonic channel ordering conventions.
Definition _common.h:59
@ CH_ACN
Ambisonic Channel Numbering (ACN)
Definition _common.h:60
@ CH_FUMA
(Legacy) Furse-Malham/B-format (WXYZ)
Definition _common.h:61
#define MAX_NUM_SH_SIGNALS
Maximum number of spherical harmonic components/signals supported.
Definition _common.h:239
@ SH_ORDER_FIRST
First-order (4 channels)
Definition _common.h:39
float beamformer_getBeamAzi_deg(void *const hBeam, int index)
Returns the beamformer azimuth direction of a given index h, in DEGREES.
Definition beamformer.c:284
float beamformer_getBeamElev_deg(void *const hBeam, int index)
Returns the beamformer elevation direction of a given index, in DEGREES.
Definition beamformer.c:290
void beamformer_setBeamType(void *const hBeam, int newID)
Sets the beamforming approach to employ (see STATIC_BEAM_TYPES enum)
Definition beamformer.c:262
int beamformer_getChOrder(void *const hBeam)
Returns the Ambisonic channel ordering convention currently being used to decode with,...
Definition beamformer.c:313
int beamformer_getProcessingDelay(void)
Returns the processing delay in samples (may be used for delay compensation features)
Definition beamformer.c:331
int beamformer_getNumBeams(void *const hBeam)
Returns the number of beamformers being generated.
Definition beamformer.c:296
void beamformer_create(void **const phBeam)
Creates an instance of beamformer.
Definition beamformer.c:30
void beamformer_setBeamOrder(void *const hBeam, int newValue)
Sets the beamforming order (see SH_ORDERS enum)
Definition beamformer.c:203
void beamformer_setChOrder(void *const hBeam, int newOrder)
Sets the Ambisonic channel ordering convention to decode with, in order to match the convention emplo...
Definition beamformer.c:248
int beamformer_getBeamType(void *const hBeam)
Returns the beamforming approach employed (see STATIC_BEAM_TYPES enum)
Definition beamformer.c:325
void beamformer_setNumBeams(void *const hBeam, int new_nBeams)
Sets the number of beamformers to generate.
Definition beamformer.c:237
int beamformer_getNSHrequired(void *const hBeam)
Returns the number of spherical harmonic signals required by the currently selected beamforming order...
Definition beamformer.c:307
int beamformer_getFrameSize(void)
Returns the processing framesize (i.e., number of samples processed with every _process() call )
Definition beamformer.c:273
void beamformer_destroy(void **const phBeam)
Destroys an instance of beamformer.
Definition beamformer.c:57
int beamformer_getMaxNumBeams(void)
Returns the maximum number of beamformers permitted.
Definition beamformer.c:302
void beamformer_process(void *const hBeam, const float *const *inputs, float *const *const outputs, int nInputs, int nOutputs, int nSamples)
Generates beamformers/virtual microphones in the specified directions.
Definition beamformer.c:95
void beamformer_init(void *const hBeam, int sampleRate)
Initialises an instance of beamformer with default settings.
Definition beamformer.c:72
int beamformer_getBeamOrder(void *const hBeam)
Returns the beamforming order (see SH_ORDERS enum)
Definition beamformer.c:278
void beamformer_setBeamElev_deg(void *const hBeam, int index, float newElev_deg)
Sets a beamformer elevation direction for a given index, in DEGREES.
Definition beamformer.c:228
void beamformer_setBeamAzi_deg(void *const hBeam, int index, float newAzi_deg)
Sets a beamformer azimuth direction of a given index, in DEGREES.
Definition beamformer.c:217
void beamformer_refreshSettings(void *const hBeam)
Sets all intialisation flags to 1; re-initialising all settings/variables as beamformer is currently ...
Definition beamformer.c:195
void beamformer_setNormType(void *const hBeam, int newType)
Sets the Ambisonic normalisation convention to decode with, in order to match with the convention emp...
Definition beamformer.c:255
int beamformer_getNormType(void *const hBeam)
Returns the Ambisonic normalisation convention currently being usedto decode with,...
Definition beamformer.c:319
Generates beamformers/virtual microphones in arbitrary directions with several different beam pattern...
#define BEAMFORMER_FRAME_SIZE
Framesize, in time-domain samples.
#define MAX_NUM_BEAMS
Maximum permitted number of beams/output channels.
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.
Definition saf_hoa.c:41
void convertHOANormConvention(float *insig, int order, int signalLength, HOA_NORM inConvention, HOA_NORM outConvention)
Converts an Ambisonic signal from one normalisation convention to another.
Definition saf_hoa.c:73
@ HOA_CH_ORDER_FUMA
Furse-Malham (FuMa) convention, often used by older recordings.
Definition saf_hoa.h:187
@ HOA_CH_ORDER_ACN
Ambisonic Channel numbering (ACN) convention, which is employed by all spherical harmonic related fun...
Definition saf_hoa.h:184
@ HOA_NORM_FUMA
Furse-Malham (FuMa) convention.
Definition saf_hoa.h:208
@ HOA_NORM_SN3D
Schmidt semi-normalisation (SN3D) convention, as used by the AmbiX standard.
Definition saf_hoa.h:206
@ HOA_NORM_N3D
Orthonormalised (N3D) convention, which is the default convention used by SAF.
Definition saf_hoa.h:204
#define ORDER2NSH(order)
Converts spherical harmonic order to number of spherical harmonic components i.e: (order+1)^2.
Definition saf_sh.h:51
void beamWeightsCardioid2Spherical(int N, float *b_n)
Generates spherical coefficients for generating cardioid beampatterns.
Definition saf_sh.c:823
void beamWeightsMaxEV(int N, float *b_n)
Generates beamweights in the SHD for maximum energy-vector beampatterns.
Definition saf_sh.c:858
void rotateAxisCoeffsReal(int order, float *c_n, float theta_0, float phi_0, float *c_nm)
Generates spherical coefficients for a rotated axisymmetric pattern (REAL)
Definition saf_sh.c:946
void beamWeightsHypercardioid2Spherical(int N, float *b_n)
Generates beamweights in the SHD for hypercardioid beampatterns.
Definition saf_sh.c:840
#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.
const float __default_LScoords128_deg[128][2]
Default Loudspeaker directions [azimuth, Elevation] - to replace above!
void utility_svvcopy(const float *a, const int len, float *c)
Single-precision, vector-vector copy, i.e.
#define SAF_MIN(a, b)
Returns the minimum of the two values.
void * malloc1d(size_t dim1_data_size)
1-D malloc (same as malloc, but with error checking)
Definition md_malloc.c:59
Main structure for beamformer.
CH_ORDER chOrdering
Ambisonic channel order convention (see CH_ORDER)
float outputFrameTD_fadeIn[MAX_NUM_SH_SIGNALS][BEAMFORMER_FRAME_SIZE]
Output frame of beam signals with linear interpolation (fade-in) applied.
float tempFrame[MAX_NUM_BEAMS][BEAMFORMER_FRAME_SIZE]
Temporary frame.
int nBeams
number of loudspeakers/virtual loudspeakers
float outputFrameTD[MAX_NUM_BEAMS][BEAMFORMER_FRAME_SIZE]
Output frame of beam signals.
NORM_TYPES norm
Ambisonic normalisation convention (see NORM_TYPES)
int recalc_beamWeights[MAX_NUM_BEAMS]
0: no init required, 1: init required
float prev_beamWeights[MAX_NUM_BEAMS][MAX_NUM_SH_SIGNALS]
Previous beamforming weights.
float beam_dirs_deg[MAX_NUM_BEAMS][2]
beam directions in degrees [azi, elev]
float tempFrame_fadeOut[MAX_NUM_SH_SIGNALS][BEAMFORMER_FRAME_SIZE]
Temporary frame with linear interpolation (fade-out) applied.
int fs
Host sampling rate, in Hz.
float beamWeights[MAX_NUM_BEAMS][MAX_NUM_SH_SIGNALS]
Current beamforming weights.
float interpolator_fadeOut[BEAMFORMER_FRAME_SIZE]
Linear Interpolator (fade-out)
STATIC_BEAM_TYPES beamType
see STATIC_BEAM_TYPES enum
float interpolator_fadeIn[BEAMFORMER_FRAME_SIZE]
Linear Interpolator (fade-in)
int beamOrder
beam order
float SHFrameTD[MAX_NUM_SH_SIGNALS][BEAMFORMER_FRAME_SIZE]
Input frame of SH signals.