1 /**\r
2 * @file mss.c\r
3 * @brief Contains external API for Multi-Source Selection (MSS) module \r
4 * instantiation.\r
5 *\r
6 * (C) Copyright 2011, Texas Instruments, Inc.\r
7 */\r
8 \r
9 #include <xdc/std.h>\r
10 #include <ti/mas/types/types.h>\r
11 #include <ti/mas/fract/fract.h>\r
12 #include <ti/mas/vpe/svd.h>\r
13 \r
14 //#include <ti/mas/aer/mss.h>\r
15 //#include <ti/mas/aer/src/mssloc.h>\r
16 #include "../mss.h"\r
17 #include "mssloc.h"\r
18 \r
19 #define MSS_DEF_SWITCH_WAIT_TIME 250 /* msec */\r
20 #define MSS_DEF_SWITCH_DURATION 500 /* msec */\r
21 #define MSS_DEF_SWITCH_HANGOVER 1000 /* msec */\r
22 #define MSS_DEF_FADE_PHA_INC_8KHZ 268435L /* Q31 phase increment of crossfade for \r
23 switch duration of 1 second at 8kHz:\r
24 2^31/8000, where 2^31 corresponds to pi. */\r
25 /* MSS memory buffer descriptors */\r
26 #define MSS_NUM_BUFS 1\r
27 #define MSS_INSTANCE_BUF 0\r
28 ecomemBuffer_t mssBufs[MSS_NUM_BUFS] = { \r
29 /* CLASS, LOG2 ALIGNMENT, BUFFER SIZE, VOLATILITY, BASE */\r
30 ecomem_CLASS_EXTERNAL, TYP_STRUCT_LOG2ALIGN, 0, FALSE, NULL\r
31 };\r
32 \r
33 /******************************************************************************\r
34 * FUNCTION PURPOSE: Return buffer sizes for the MSS module\r
35 ******************************************************************************\r
36 * DESCRIPTION: Calculate the worst case buffer sizes for the MSS. Also specify\r
37 * alignment and volatility requirements.\r
38 *\r
39 * tint mssGetSizes (\r
40 * tint *nbufs, - address to store number of buffers\r
41 * const ecomemBuffer_t **bufs, - address to store buffer descriptors\r
42 * mssSizeConfig_t *cfg) - information to be used in calculating the\r
43 * worst case buffer sizes\r
44 *****************************************************************************/\r
45 tint mssGetSizes(tint *nbufs, const ecomemBuffer_t **bufs, mssSizeConfig_t *cfg)\r
46 {\r
47 tint svd_size, mss_size, mss_inst_size, num_src;\r
48 \r
49 /* Get memory requirement of SVD */\r
50 svdGetSizes (&svd_size);\r
51 \r
52 /* Calculate total memory of MSS: instance + all SVDs*/\r
53 svd_size = mss_SIZE_ALIGN(svd_size,TYP_STRUCT_LOG2ALIGN);\r
54 mss_inst_size = sizeof(mssInst_t);\r
55 mss_inst_size = mss_SIZE_ALIGN(mss_inst_size,TYP_STRUCT_LOG2ALIGN);\r
56 num_src = cfg->max_num_mic_fixed + cfg->max_num_mic_remote\r
57 + cfg->max_num_mic_clean + cfg->max_num_beam;\r
58 mss_size = svd_size*num_src + mss_inst_size;\r
59 mssBufs[MSS_INSTANCE_BUF].size = mss_size;\r
60 \r
61 /* Return number of buffers required by MSS */ \r
62 *nbufs = MSS_NUM_BUFS;\r
63 \r
64 /* Return buffer specifications */\r
65 *bufs = &mssBufs[0]; \r
66 \r
67 return(mss_NOERR);\r
68 \r
69 } /* mssGetSizes */\r
70 \r
71 /******************************************************************************\r
72 * FUNCTION PURPOSE: Create an instance of the MSS module.\r
73 ******************************************************************************\r
74 * DESCRIPTION: Creates an instance of the MSS and initializes memory buffers.\r
75 *\r
76 * tint mssNew (\r
77 * void **mssInst, - address of the memory location that will\r
78 * receive a pointer to instance structure\r
79 * tint nbufs, - number of memory buffers\r
80 * ecomemBuffer_t *bufs, - a vector of buffer descriptors\r
81 * mssNewConfig_t *cfg) - a pointer to configuration structure\r
82 *\r
83 * Return value: \r
84 * mss_NOERR - success\r
85 * mss_ERR_INVALIDPAR - *mssInst is not NULL or nbufs is not correct \r
86 * mss_ERR_NOMEMORY - MSS instance pointer is not NULL, or\r
87 * number of buffers is not correct, or\r
88 * buffer properties are not correct\r
89 *****************************************************************************/\r
90 tint mssNew (void **mssInst, tint nbufs, ecomemBuffer_t *bufs, mssNewConfig_t *cfg)\r
91 {\r
92 mssInst_t *inst;\r
93 ecomemBuffer_t *bufp;\r
94 tint svd_size, mss_size, mss_inst_size, num_src;\r
95 int i;\r
96 \r
97 /* Test instance pointer (must be NULL) */\r
98 if ((*mssInst != NULL) || (nbufs != MSS_NUM_BUFS)) {\r
99 return(mss_ERR_INVALIDPAR);\r
100 }\r
101 \r
102 /* Check all dynamic buffers (base address != NULL) */\r
103 for (i=0, bufp=&bufs[0]; i<MSS_NUM_BUFS; i++, bufp++) {\r
104 if ((bufp->size>0) && (bufp->base==NULL)) {\r
105 return(mss_ERR_NOMEMORY);\r
106 }\r
107 }\r
108 \r
109 /* Get the instance buffer descriptor returned by the user */\r
110 bufp = &bufs[MSS_INSTANCE_BUF];\r
111 \r
112 /* Calculate total memory of MSS: instance + all SVDs */\r
113 svdGetSizes (&svd_size);\r
114 svd_size = mss_SIZE_ALIGN(svd_size,TYP_STRUCT_LOG2ALIGN);\r
115 mss_inst_size = sizeof(mssInst_t);\r
116 mss_inst_size = mss_SIZE_ALIGN(mss_inst_size,TYP_STRUCT_LOG2ALIGN);\r
117 num_src = cfg->sizeCfg.max_num_mic_fixed + cfg->sizeCfg.max_num_mic_remote\r
118 + cfg->sizeCfg.max_num_mic_clean + cfg->sizeCfg.max_num_beam;\r
119 mss_size = svd_size*num_src + mss_inst_size;\r
120 \r
121 /* Check if the provided memory is good: size, alignment, and volatility */\r
122 if ( (bufp->size < mss_size) \r
123 || (!typChkAlign(bufp->base, bufp->log2align)) || bufp->volat) {\r
124 return(mss_ERR_NOMEMORY);\r
125 }\r
126 \r
127 /* Assign instance memory and initialize instance */\r
128 inst = (mssInst_t *)bufp->base;\r
129 inst->max_sampling_rate = cfg->sizeCfg.max_sampling_rate;\r
130 inst->svd = (tword *)bufp->base + mss_inst_size; /* SVD address for 1st src */\r
131 inst->svd_size = svd_size;\r
132 inst->handle = cfg->handle; /* init handle in instance */\r
133 inst->state = MSS_CLOSED; /* set MSS state to CLOSED */\r
134 \r
135 /* Return instance address */\r
136 *mssInst = (void *)inst;\r
137 \r
138 return(mss_NOERR);\r
139 \r
140 } /* mssNew() */\r
141 \r
142 /******************************************************************************\r
143 * FUNCTION PURPOSE: Control of MSS\r
144 ******************************************************************************\r
145 * DESCRIPTION: Controls the operation and parameters of the MSS.\r
146 *\r
147 * tint mssControl (\r
148 * void *mssInst, - a pointer to MSS instance\r
149 * tint mssControl *ctl) - a pointer to the control code structure \r
150 *\r
151 * Return value: mss_NOERR - success\r
152 * mss_ERR_NOTOPENED - MSS not opened\r
153 * mss_ERR_INVALIDSRC - the manually selected source is invalid \r
154 * mss_ERR_INVALIDPAR - parameter is invalid \r
155 *****************************************************************************/\r
156 tint mssControl (void *mssInst, mssControl_t *ctl)\r
157 {\r
158 mssInst_t *inst = (mssInst_t *)mssInst;\r
159 tint ret_val;\r
160 \r
161 /* Make sure that MSS is open */\r
162 if (inst->state != MSS_OPEN) {\r
163 return (mss_ERR_NOTOPENED);\r
164 } \r
165 \r
166 ret_val = mss_NOERR;\r
167 \r
168 /* Change the operation modes bitfield */\r
169 if(mss_chkbit(ctl->valid_bitfield, mss_CTL_MODES)) {\r
170 inst->modes_bf = (inst->modes_bf & (~ctl->modes.mask))\r
171 | (ctl->modes.value & ctl->modes.mask);\r
172 } /* mss_CTL_MODES */\r
173 \r
174 /* Manually select source */\r
175 if(mss_chkbit(ctl->valid_bitfield, mss_CTL_SELECT_SRC)) {\r
176 /* Check if selected source is valid:\r
177 - source group must be one of the groups defined in mss.h, \r
178 - source index must be less than the number of sources in the group. */\r
179 if( (ctl->source.group > mss_SRC_MAX)\r
180 || (ctl->source.group < mss_SRC_MIC_FIXED) ){\r
181 ret_val = mss_ERR_INVALIDSRC;\r
182 } \r
183 else if (ctl->source.index > inst->num_src_per_group[ctl->source.group]) {\r
184 ret_val = mss_ERR_INVALIDSRC;\r
185 }\r
186 else if ( (ctl->source.group != inst->cur_src.group) \r
187 || (ctl->source.index != inst->cur_src.index) ) { \r
188 /* Prepare to switch source if selected source is valid. If in the middle of a \r
189 source switch initiated by MSS internally, internal switch will be stopped. */\r
190 inst->switch_src_cnt = 0;\r
191 inst->crossfade_phase = MSS_FADE_PHA_INIT;\r
192 inst->switch_hang_cnt = inst->params.switch_hangover;\r
193 inst->new_src = ctl->source;\r
194 mss_setbit(inst->state_bf, MSS_STATE_BIT_SWITCH_SRC);\r
195 }\r
196 } /* mss_CTL_SELECT_SRC */\r
197 \r
198 /* Change the threshold of source switch */\r
199 if(mss_chkbit(ctl->valid_bitfield, mss_CTL_SWITCH_THRESH)) {\r
200 /* Convert parameter from msec to # of samples */\r
201 inst->params.switch_threshold = MSS_NUM_SAMP_1MS_8KHZ * inst->sampling_rate\r
202 * ctl->switch_thresh;\r
203 }\r
204 \r
205 /* Change the duration of source switch */\r
206 if(mss_chkbit(ctl->valid_bitfield, mss_CTL_SWITCH_DURATION)) {\r
207 /* Convert parameter from msec to # of samples */\r
208 inst->params.switch_duration = MSS_NUM_SAMP_1MS_8KHZ * inst->sampling_rate\r
209 * ctl->switch_duration;\r
210 /* Calculate per sample Q31 phase increment for crossfade implementation: \r
211 2^31/duration, where 2^31 corresponds to pi. */ \r
212 inst->crossfade_phase_inc = frct_LFRCT_MAX/inst->params.switch_duration; \r
213 } /* mss_CTL_SWITCH_DURATION */\r
214 \r
215 /* Change hangover of source switch */\r
216 if(mss_chkbit(ctl->valid_bitfield, mss_CTL_SWITCH_HNAGOVER)) {\r
217 /* Convert parameter from msec to # of samples */\r
218 inst->params.switch_hangover = MSS_NUM_SAMP_1MS_8KHZ * inst->sampling_rate\r
219 * ctl->switch_hangover;\r
220 } /* mss_CTL_SWITCH_HNAGOVER */\r
221 \r
222 return ret_val;\r
223 } /* mssControl */\r
224 \r
225 /******************************************************************************\r
226 * FUNCTION PURPOSE: Open and configure an MSS instance.\r
227 ******************************************************************************\r
228 * DESCRIPTION: This function configures an instance of the MSS.\r
229 *\r
230 * tint mssOpen (\r
231 * void *mssInst, - a pointer to MSS instance\r
232 * mssConfig_t *cfg) - a pointer to MSS configuration structure\r
233 *\r
234 *****************************************************************************/\r
235 tint mssOpen (void *mssInst, mssConfig_t *cfg)\r
236 {\r
237 mssInst_t * inst = (mssInst_t *)mssInst;\r
238 tword * svd_ptr;\r
239 tint num_src, svd_size;\r
240 int i;\r
241 \r
242 /* Return error if state is not CLOSED */ \r
243 if(inst->state != MSS_CLOSED) {\r
244 return(mss_ERR_NOTCLOSED);\r
245 }\r
246 \r
247 /* Return error if no configuration parameters */\r
248 if(cfg == NULL){\r
249 return(mss_ERR_INVALIDPAR);\r
250 }\r
251 \r
252 /* Read configuration */\r
253 num_src = cfg->num_mic_fixed+cfg->num_mic_remote+cfg->num_mic_clean+cfg->num_beam;\r
254 if(num_src <= 0) {\r
255 return(mss_ERR_INVALIDPAR);\r
256 }\r
257 \r
258 inst->sampling_rate = cfg->sampling_rate;\r
259 inst->num_src_per_group[mss_SRC_MIC_FIXED] = cfg->num_mic_fixed;\r
260 inst->num_src_per_group[mss_SRC_MIC_REMOTE] = cfg->num_mic_remote;\r
261 inst->num_src_per_group[mss_SRC_MIC_CLEAN] = cfg->num_mic_clean;\r
262 inst->num_src_per_group[mss_SRC_BEAM] = cfg->num_beam;\r
263 inst->num_mic_array = cfg->num_mic_array; /* not considered as source to select */\r
264 \r
265 /* Init SVDs */\r
266 svd_size = inst->svd_size;\r
267 svd_ptr = (tword *)inst->svd;\r
268 for(i=0; i<num_src; i++) {\r
269 if (!typChkAlign(svd_ptr, TYP_STRUCT_LOG2ALIGN)) {\r
270 return(mss_ERR_NOMEMORY);\r
271 }\r
272 svdInit((void *)svd_ptr, inst->sampling_rate);\r
273 svd_ptr += svd_size;\r
274 } \r
275 \r
276 /* Set parameters to default */\r
277 inst->params.switch_threshold = MSS_DEF_SWITCH_WAIT_TIME * MSS_NUM_SAMP_1MS_8KHZ\r
278 * inst->sampling_rate; /* convert msec to # of samples */\r
279 inst->params.switch_duration = MSS_DEF_SWITCH_DURATION * MSS_NUM_SAMP_1MS_8KHZ\r
280 * inst->sampling_rate; /* convert msec to # of samples */\r
281 inst->crossfade_phase_inc = MSS_DEF_FADE_PHA_INC_8KHZ / inst->sampling_rate;\r
282 inst->params.switch_hangover = MSS_DEF_SWITCH_HANGOVER * MSS_NUM_SAMP_1MS_8KHZ \r
283 * inst->sampling_rate; /* convert msec to # of samples */\r
284 inst->modes_bf = mss_CTL_MODES_DEFAULT;\r
285 \r
286 /* Set initially selected source - the first valid source */\r
287 if(cfg->num_mic_fixed > 0) {\r
288 inst->cur_src.group = mss_SRC_MIC_FIXED;\r
289 inst->cur_src.index = 0;\r
290 }\r
291 else if(cfg->num_mic_remote > 0) {\r
292 inst->cur_src.group = mss_SRC_MIC_REMOTE;\r
293 inst->cur_src.index = 0;\r
294 }\r
295 else if(cfg->num_mic_clean > 0) {\r
296 inst->cur_src.group = mss_SRC_MIC_CLEAN;\r
297 inst->cur_src.index = 0;\r
298 }\r
299 else {\r
300 inst->cur_src.group = mss_SRC_BEAM;\r
301 inst->cur_src.index = 0;\r
302 }\r
303 \r
304 /* Initialize instance */ \r
305 inst->frame_size = inst->sampling_rate * MSS_FRAME_SIZE_8KHZ;\r
306 inst->switch_hang_cnt = inst->params.switch_hangover;\r
307 inst->crossfade_cnt = 0;\r
308 inst->state_bf = 0;\r
309 \r
310 /* Declare the instance OPEN */\r
311 inst->state = MSS_OPEN; \r
312 \r
313 return(mss_NOERR);\r
314 } /* mssOpen */\r
315 \r
316 /******************************************************************************\r
317 * FUNCTION PURPOSE: Returns debuging information for an MSS instance.\r
318 ******************************************************************************\r
319 * DESCRIPTION: This function returns debuging information.\r
320 *\r
321 * tint mssDebugStat (\r
322 * void *mssInst, - a pointer to MSS instance\r
323 * mssDebugStat_t *mssDbg) - a pointer to MSS debug structure\r
324 *\r
325 *****************************************************************************/\r
326 tint mssDebugStat(void *mssInst, mssDebugStat_t *mssDbg)\r
327 {\r
328 tint svd_decision;\r
329 tword *svd_ptr;\r
330 LFract sig_pow;\r
331 Fract spch_db, noise_db;\r
332 UFract svd_thresh;\r
333 mssInst_t *inst = (mssInst_t *)mssInst;\r
334 \r
335 /* Make sure that MSS is open */\r
336 if (inst->state != MSS_OPEN) {\r
337 return (mss_ERR_NOTOPENED);\r
338 } \r
339 \r
340 mssDbg->cur_src = inst->cur_src;\r
341 mssDbg->new_src = inst->new_src;\r
342 mssDbg->states[0] = (tuint)(inst->crossfade_phase>>16);\r
343 mssDbg->states[1] = (tuint)inst->gain_in;\r
344 mssDbg->states[2] = (tuint)inst->gain_out;\r
345 mssDbg->states[3] = (tuint)inst->switch_src_cnt; \r
346 mssDbg->states[4] = (tuint)inst->state_bf;\r
347 \r
348 /* only debug fixed mics for now */\r
349 if(inst->num_src_per_group[mss_SRC_MIC_FIXED] > 1) {\r
350 svd_ptr = inst->svd;\r
351 svd_decision = svdGetDecision(svd_ptr, &sig_pow);\r
352 mssDbg->states[5] = (tuint)svd_decision;\r
353 \r
354 svdGetLevels (svd_ptr, &spch_db, &noise_db, &svd_thresh);\r
355 mssDbg->states[6] = (tuint)spch_db;\r
356 mssDbg->states[7] = (tuint)noise_db;\r
357 \r
358 svd_ptr += inst->svd_size;\r
359 svd_decision = svdGetDecision(svd_ptr, &sig_pow);\r
360 mssDbg->states[8] = (tuint)svd_decision;\r
361 \r
362 svdGetLevels (svd_ptr, &spch_db, &noise_db, &svd_thresh);\r
363 mssDbg->states[9] = (tuint)spch_db;\r
364 mssDbg->states[10] = (tuint)noise_db;\r
365 }\r
366 \r
367 return(mss_NOERR);\r
368 \r
369 } /* mssDebugStat */\r
370 \r
371 /******************************************************************************\r
372 * FUNCTION PURPOSE: Close an instance of MSS.\r
373 ******************************************************************************\r
374 * DESCRIPTION: This function closes an instance of MSS by simply changing\r
375 * the state to CLOSED.\r
376 *\r
377 * tint mssClose (\r
378 * void *mssInst) - a pointer to MSS instance\r
379 *\r
380 *****************************************************************************/\r
381 tint mssClose (void *mssInst)\r
382 {\r
383 mssInst_t *inst = (mssInst_t *)mssInst;\r
384 \r
385 if(inst->state != MSS_OPEN) {\r
386 return(mss_ERR_NOTOPENED);\r
387 }\r
388 \r
389 /*Change the MSS state to CLOSED */\r
390 inst->state = MSS_CLOSED; \r
391 \r
392 return(mss_NOERR);\r
393 } /* mssClose */\r
394 \r
395 /******************************************************************************\r
396 * FUNCTION PURPOSE: Delete an MSS instance.\r
397 ******************************************************************************\r
398 * DESCRIPTION: Deletes an instance of MSS. Clears the instance pointer.\r
399 * Actual "deallocation" of instance should be done elsewhere\r
400 * after this function is called. Hence, one has to save the\r
401 * buffer descriptors to make "deallocation" possible.\r
402 *\r
403 * tint mssDelete (\r
404 * void **mssInst, - an address of memory location that contains\r
405 * a pointer to instance structure\r
406 * tint nbufs, - number of memory buffers\r
407 * ecomemBuffer_t *bufs) - vector of buffer descriptors\r
408 *\r
409 *****************************************************************************/\r
410 tint mssDelete (void **mssInst, tint nbufs, ecomemBuffer_t *bufs)\r
411 {\r
412 mssInst_t *inst = (mssInst_t *)*mssInst;\r
413 \r
414 /* check if instance is already closed */\r
415 if (inst->state != MSS_CLOSED) { \r
416 return (mss_ERR_NOTCLOSED);\r
417 }\r
418 \r
419 /* check if enough descriptors are provided to store all buffer addresses */\r
420 if (nbufs < MSS_NUM_BUFS) { \r
421 return (mss_ERR_NOMEMORY);\r
422 }\r
423 \r
424 /* return buffer addresses */\r
425 bufs[MSS_INSTANCE_BUF].base = inst; /* instance structure */\r
426 \r
427 /* clear the instance pointer */\r
428 *mssInst = NULL; \r
429 \r
430 return(mss_NOERR);\r
431 } /* mssDelete */\r
432 \r
433 /******************************************************************************\r
434 * FUNCTION PURPOSE: Validates the input sources to MSS.\r
435 ******************************************************************************\r
436 * DESCRIPTION: This function examines the input sources to MSS to make sure\r
437 * all sources are valid.\r
438 *\r
439 *****************************************************************************/\r
440 \r
441 tint mss_src_validation(mssInst_t *inst, void *rx_out_sync, void *mic_fix[],\r
442 void *mic_rem[], void *mic_cln[], void *mic_arr[], void *beam[])\r
443 { \r
444 int i;\r
445 \r
446 /* Return error if synchronized Rx out doesn't have valid data (NOT USED!!!) */\r
447 /*\r
448 * if ( rx_out_sync == NULL ) { \r
449 * return (mss_ERR_INVALIDSRC); \r
450 * }\r
451 */\r
452 \r
453 /* Check if all sources have valid data.\r
454 Currently MSS only supports fixed mic, remote mic, and clean mic.\r
455 */\r
456 if(inst->num_src_per_group[mss_SRC_MIC_FIXED] > 0) {\r
457 if(mic_fix == NULL) {\r
458 return (mss_ERR_INVALIDSRC);\r
459 }\r
460 else {\r
461 for (i=0; i<inst->num_src_per_group[mss_SRC_MIC_FIXED]; i++) {\r
462 if(mic_fix[i] == NULL) {\r
463 return (mss_ERR_INVALIDSRC);\r
464 }\r
465 }\r
466 }\r
467 } /* mss_SRC_MIC_FIXED */\r
468 \r
469 if(inst->num_src_per_group[mss_SRC_MIC_REMOTE] > 0) {\r
470 if(mic_rem == NULL) {\r
471 return (mss_ERR_INVALIDSRC);\r
472 }\r
473 else {\r
474 for (i=0; i<inst->num_src_per_group[mss_SRC_MIC_REMOTE]; i++) {\r
475 if(mic_rem[i] == NULL) {\r
476 return (mss_ERR_INVALIDSRC);\r
477 }\r
478 }\r
479 }\r
480 } /* mss_SRC_MIC_REMOTE */\r
481 \r
482 if(inst->num_src_per_group[mss_SRC_MIC_CLEAN] > 0) {\r
483 if(mic_cln == NULL) {\r
484 return (mss_ERR_INVALIDSRC);\r
485 }\r
486 else {\r
487 for (i=0; i<inst->num_src_per_group[mss_SRC_MIC_CLEAN]; i++) {\r
488 if(mic_cln[i] == NULL) {\r
489 return (mss_ERR_INVALIDSRC);\r
490 }\r
491 }\r
492 }\r
493 } /* mss_SRC_MIC_CLEAN */\r
494 \r
495 return (mss_NOERR); \r
496 } /* mss_src_validation */\r
497 \r
498 /* Nothing past this point */\r