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
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_SHFrameTD, 0, MAX_NUM_SH_SIGNALS*BEAMFORMER_FRAME_SIZE*sizeof(float));
86 memset(pData->prev_beamWeights, 0, MAX_NUM_BEAMS*MAX_NUM_SH_SIGNALS*sizeof(float));
87 for(ch=0; ch<MAX_NUM_BEAMS; ch++)
88 pData->recalc_beamWeights[ch] = 1;
89 for(i=1; i<=BEAMFORMER_FRAME_SIZE; i++){
90 pData->interpolator_fadeIn[i-1] = (float)i*1.0f/(float)BEAMFORMER_FRAME_SIZE;
91 pData->interpolator_fadeOut[i-1] = 1.0f - pData->interpolator_fadeIn[i-1];
92 }
93}
94
96(
97 void * const hBeam,
98 const float *const * inputs,
99 float* const* const outputs,
100 int nInputs,
101 int nOutputs,
102 int nSamples
103)
104{
105 beamformer_data *pData = (beamformer_data*)(hBeam);
106 int ch, i, bi, nSH, mixWithPreviousFLAG;
107 float c_n[MAX_SH_ORDER+1];
108
109 /* local copies of user parameters */
110 int nBeams, beamOrder;
111 NORM_TYPES norm;
112 CH_ORDER chOrdering;
113 beamOrder = pData->beamOrder;
114 nSH = ORDER2NSH(beamOrder);
115 nBeams = pData->nBeams;
116 norm = pData->norm;
117 chOrdering = pData->chOrdering;
118
119 /* Apply beamformer */
120 if(nSamples == BEAMFORMER_FRAME_SIZE) {
121 /* Load time-domain data */
122 for(i=0; i < SAF_MIN(nSH, nInputs); i++)
123 utility_svvcopy(inputs[i], BEAMFORMER_FRAME_SIZE, pData->SHFrameTD[i]);
124 for(; i<MAX_NUM_SH_SIGNALS; i++)
125 memset(pData->SHFrameTD[i], 0, BEAMFORMER_FRAME_SIZE * sizeof(float)); /* fill remaining channels with zeros */
126
127 /* account for input channel order convention */
128 switch(chOrdering){
129 case CH_ACN: /* already ACN, do nothing*/ break; /* Otherwise, convert to ACN... */
131 }
132
133 /* account for input normalisation scheme */
134 switch(norm){
135 case NORM_N3D: /* already in N3D, do nothing */ break; /* Otherwise, convert to N3D... */
138 }
139
140 /* Calculate beamforming coeffients */
141 mixWithPreviousFLAG = 0;
142 for(bi=0; bi<nBeams; bi++){
143 if(pData->recalc_beamWeights[bi]){
144 memset(pData->beamWeights[bi], 0, MAX_NUM_SH_SIGNALS*sizeof(float));
145 switch(pData->beamType){
146 case STATIC_BEAM_TYPE_CARDIOID: beamWeightsCardioid2Spherical(beamOrder, c_n); break;
148 case STATIC_BEAM_TYPE_MAX_EV: beamWeightsMaxEV(beamOrder, c_n); break;
149 }
150 rotateAxisCoeffsReal(beamOrder, (float*)c_n, SAF_PI/2.0f - pData->beam_dirs_deg[bi][1]*SAF_PI/180.0f,
151 pData->beam_dirs_deg[bi][0]*SAF_PI/180.0f, (float*)pData->beamWeights[bi]);
152 pData->recalc_beamWeights[bi] = 0;
153 mixWithPreviousFLAG = 1;
154 }
155 }
156
157 /* Apply beam weights */
158 cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nBeams, BEAMFORMER_FRAME_SIZE, nSH, 1.0f,
159 (const float*)pData->beamWeights, MAX_NUM_SH_SIGNALS,
160 (const float*)pData->prev_SHFrameTD, BEAMFORMER_FRAME_SIZE, 0.0f,
161 (float*)pData->outputFrameTD, BEAMFORMER_FRAME_SIZE);
162
163 /* Fade between (linearly inerpolate) the new weights and the previous weights (only if the new weights are different) */
164 if(mixWithPreviousFLAG){
165 cblas_sgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans, nBeams, BEAMFORMER_FRAME_SIZE, nSH, 1.0f,
166 (float*)pData->prev_beamWeights, MAX_NUM_SH_SIGNALS,
167 (float*)pData->prev_SHFrameTD, BEAMFORMER_FRAME_SIZE, 0.0f,
168 (float*)pData->tempFrame, BEAMFORMER_FRAME_SIZE);
169
170 /* Apply the linear interpolation */
171 for (i=0; i < nBeams; i++){
172 utility_svvmul((float*)pData->interpolator_fadeIn, (float*)pData->outputFrameTD[i], BEAMFORMER_FRAME_SIZE, (float*)pData->outputFrameTD_fadeIn[i]);
173 utility_svvmul((float*)pData->interpolator_fadeOut, (float*)pData->tempFrame[i], BEAMFORMER_FRAME_SIZE, (float*)pData->tempFrame_fadeOut[i]);
174 }
175 cblas_scopy(nBeams*BEAMFORMER_FRAME_SIZE, (float*)pData->outputFrameTD_fadeIn, 1, (float*)pData->outputFrameTD, 1);
176 cblas_saxpy(nBeams*BEAMFORMER_FRAME_SIZE, 1.0f, (float*)pData->tempFrame_fadeOut, 1, (float*)pData->outputFrameTD, 1);
177
178 /* for next frame */
179 utility_svvcopy((const float*)pData->beamWeights, MAX_NUM_BEAMS*MAX_NUM_SH_SIGNALS, (float*)pData->prev_beamWeights);
180 }
181
182 /* for next frame */
184
185 /* copy to output buffer */
186 for(ch = 0; ch < SAF_MIN(nBeams, nOutputs); ch++)
187 utility_svvcopy(pData->outputFrameTD[ch], BEAMFORMER_FRAME_SIZE, outputs[ch]);
188 for (; ch < nOutputs; ch++)
189 memset(outputs[ch], 0, BEAMFORMER_FRAME_SIZE*sizeof(float));
190 }
191 else
192 for (ch=0; ch < nOutputs; ch++)
193 memset(outputs[ch], 0, BEAMFORMER_FRAME_SIZE*sizeof(float));
194}
195
196
197/* Set Functions */
198
199void beamformer_refreshSettings(void* const hBeam)
200{
201 beamformer_data *pData = (beamformer_data*)(hBeam);
202 int ch;
203 for(ch=0; ch<MAX_NUM_BEAMS; ch++)
204 pData->recalc_beamWeights[ch] = 1;
205}
206
207void beamformer_setBeamOrder(void * const hBeam, int newValue)
208{
209 beamformer_data *pData = (beamformer_data*)(hBeam);
210 int ch;
211 pData->beamOrder = SAF_MIN(SAF_MAX(newValue,1), MAX_SH_ORDER);
212 for(ch=0; ch<MAX_NUM_BEAMS; ch++)
213 pData->recalc_beamWeights[ch] = 1;
214 /* FUMA only supports 1st order */
215 if(pData->beamOrder!=SH_ORDER_FIRST && pData->chOrdering == CH_FUMA)
216 pData->chOrdering = CH_ACN;
217 if(pData->beamOrder!=SH_ORDER_FIRST && pData->norm == NORM_FUMA)
218 pData->norm = NORM_SN3D;
219}
220
221void beamformer_setBeamAzi_deg(void* const hBeam, int index, float newAzi_deg)
222{
223 beamformer_data *pData = (beamformer_data*)(hBeam);
224 if(newAzi_deg>180.0f)
225 newAzi_deg = -360.0f + newAzi_deg;
226 newAzi_deg = SAF_MAX(newAzi_deg, -180.0f);
227 newAzi_deg = SAF_MIN(newAzi_deg, 180.0f);
228 pData->beam_dirs_deg[index][0] = newAzi_deg;
229 pData->recalc_beamWeights[index] = 1;
230}
231
232void beamformer_setBeamElev_deg(void* const hBeam, int index, float newElev_deg)
233{
234 beamformer_data *pData = (beamformer_data*)(hBeam);
235 newElev_deg = SAF_MAX(newElev_deg, -90.0f);
236 newElev_deg = SAF_MIN(newElev_deg, 90.0f);
237 pData->beam_dirs_deg[index][1] = newElev_deg;
238 pData->recalc_beamWeights[index] = 1;
239}
240
241void beamformer_setNumBeams(void* const hBeam, int new_nBeams)
242{
243 beamformer_data *pData = (beamformer_data*)(hBeam);
244 int ch;
245 if(pData->nBeams != new_nBeams){
246 pData->nBeams = new_nBeams;
247 for(ch=0; ch<MAX_NUM_BEAMS; ch++)
248 pData->recalc_beamWeights[ch] = 1;
249 }
250}
251
252void beamformer_setChOrder(void* const hBeam, int newOrder)
253{
254 beamformer_data *pData = (beamformer_data*)(hBeam);
255 if((CH_ORDER)newOrder != CH_FUMA || pData->beamOrder==SH_ORDER_FIRST)/* FUMA only supports 1st order */
256 pData->chOrdering = (CH_ORDER)newOrder;
257}
258
259void beamformer_setNormType(void* const hBeam, int newType)
260{
261 beamformer_data *pData = (beamformer_data*)(hBeam);
262 if((NORM_TYPES)newType != NORM_FUMA || pData->beamOrder==SH_ORDER_FIRST)/* FUMA only supports 1st order */
263 pData->norm = (NORM_TYPES)newType;
264}
265
266void beamformer_setBeamType(void* const hBeam, int newID)
267{
268 beamformer_data *pData = (beamformer_data*)(hBeam);
269 int ch;
270 pData->beamType = newID;
271 for(ch=0; ch<MAX_NUM_BEAMS; ch++)
272 pData->recalc_beamWeights[ch] = 1;
273}
274
275/* Get Functions */
276
278{
280}
281
282int beamformer_getBeamOrder(void * const hBeam)
283{
284 beamformer_data *pData = (beamformer_data*)(hBeam);
285 return pData->beamOrder;
286}
287
288float beamformer_getBeamAzi_deg(void* const hBeam, int index)
289{
290 beamformer_data *pData = (beamformer_data*)(hBeam);
291 return pData->beam_dirs_deg[index][0];
292}
293
294float beamformer_getBeamElev_deg(void* const hBeam, int index)
295{
296 beamformer_data *pData = (beamformer_data*)(hBeam);
297 return pData->beam_dirs_deg[index][1];
298}
299
300int beamformer_getNumBeams(void* const hBeam)
301{
302 beamformer_data *pData = (beamformer_data*)(hBeam);
303 return pData->nBeams;
304}
305
307{
308 return MAX_NUM_BEAMS;
309}
310
311int beamformer_getNSHrequired(void* const hBeam)
312{
313 beamformer_data *pData = (beamformer_data*)(hBeam);
314 return ORDER2NSH(pData->beamOrder);
315}
316
317int beamformer_getChOrder(void* const hBeam)
318{
319 beamformer_data *pData = (beamformer_data*)(hBeam);
320 return (int)pData->chOrdering;
321}
322
323int beamformer_getNormType(void* const hBeam)
324{
325 beamformer_data *pData = (beamformer_data*)(hBeam);
326 return (int)pData->norm;
327}
328
329int beamformer_getBeamType(void* const hBeam)
330{
331 beamformer_data *pData = (beamformer_data*)(hBeam);
332 return pData->beamType;
333}
334
339
340
#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:288
float beamformer_getBeamElev_deg(void *const hBeam, int index)
Returns the beamformer elevation direction of a given index, in DEGREES.
Definition beamformer.c:294
void beamformer_setBeamType(void *const hBeam, int newID)
Sets the beamforming approach to employ (see STATIC_BEAM_TYPES enum)
Definition beamformer.c:266
int beamformer_getChOrder(void *const hBeam)
Returns the Ambisonic channel ordering convention currently being used to decode with,...
Definition beamformer.c:317
int beamformer_getNumBeams(void *const hBeam)
Returns the number of beamformers being generated.
Definition beamformer.c:300
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:207
int beamformer_getMaxNumBeams()
Returns the maximum number of beamformers permitted.
Definition beamformer.c:306
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:252
int beamformer_getBeamType(void *const hBeam)
Returns the beamforming approach employed (see STATIC_BEAM_TYPES enum)
Definition beamformer.c:329
void beamformer_setNumBeams(void *const hBeam, int new_nBeams)
Sets the number of beamformers to generate.
Definition beamformer.c:241
int beamformer_getNSHrequired(void *const hBeam)
Returns the number of spherical harmonic signals required by the currently selected beamforming order...
Definition beamformer.c:311
int beamformer_getFrameSize(void)
Returns the processing framesize (i.e., number of samples processed with every _process() call )
Definition beamformer.c:277
void beamformer_destroy(void **const phBeam)
Destroys an instance of beamformer.
Definition beamformer.c:57
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:96
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:282
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:232
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:221
void beamformer_refreshSettings(void *const hBeam)
Sets all intialisation flags to 1; re-initialising all settings/variables as beamformer is currently ...
Definition beamformer.c:199
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:259
int beamformer_getProcessingDelay()
Returns the processing delay in samples (may be used for delay compensation features)
Definition beamformer.c:335
int beamformer_getNormType(void *const hBeam)
Returns the Ambisonic normalisation convention currently being usedto decode with,...
Definition beamformer.c:323
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)
float prev_SHFrameTD[MAX_NUM_SH_SIGNALS][BEAMFORMER_FRAME_SIZE]
Previous frame of SH signals.
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.