1 /*
2 * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
3 * Copyright (C) 2005-2018, Anthony Minessale II <anthm@freeswitch.org>
4 *
5 * Version: MPL 1.1
6 *
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
11 *
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
16 *
17 * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
18 *
19 * The Initial Developer of the Original Code is
20 * Anthony Minessale II <anthm@freeswitch.org>
21 * Portions created by the Initial Developer are Copyright (C)
22 * the Initial Developer. All Rights Reserved.
23 *
24 * Contributor(s):
25 * Chris Rienzo <chris@signalwire.com>
26 *
27 *
28 * mod_test.c -- mock module interfaces for testing
29 *
30 */
31
32 #include <switch.h>
33
34
35 SWITCH_MODULE_LOAD_FUNCTION(mod_test_load);
36 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_test_shutdown);
37 SWITCH_MODULE_RUNTIME_FUNCTION(mod_test_runtime);
38 SWITCH_MODULE_DEFINITION(mod_test, mod_test_load, mod_test_shutdown, mod_test_runtime);
39
40
41 typedef enum {
42 ASRFLAG_READY = (1 << 0),
43 ASRFLAG_INPUT_TIMERS = (1 << 1),
44 ASRFLAG_START_OF_SPEECH = (1 << 2),
45 ASRFLAG_RETURNED_START_OF_SPEECH = (1 << 3),
46 ASRFLAG_NOINPUT_TIMEOUT = (1 << 4),
47 ASRFLAG_RESULT = (1 << 5),
48 ASRFLAG_RETURNED_RESULT = (1 << 6)
49 } test_asr_flag_t;
50
51 typedef struct {
52 uint32_t flags;
53 const char *result_text;
54 double result_confidence;
55 uint32_t thresh;
56 uint32_t silence_ms;
57 uint32_t voice_ms;
58 int no_input_timeout;
59 int speech_timeout;
60 switch_bool_t start_input_timers;
61 switch_time_t no_input_time;
62 switch_time_t speech_time;
63 char *grammar;
64 char *channel_uuid;
65 switch_vad_t *vad;
66 } test_asr_t;
67
68
test_asr_reset(test_asr_t * context)69 static void test_asr_reset(test_asr_t *context)
70 {
71 if (context->vad) {
72 switch_vad_reset(context->vad);
73 }
74 context->flags = 0;
75 context->result_text = "agent";
76 context->result_confidence = 87.3;
77 switch_set_flag(context, ASRFLAG_READY);
78 context->no_input_time = switch_micro_time_now();
79 if (context->start_input_timers) {
80 switch_set_flag(context, ASRFLAG_INPUT_TIMERS);
81 }
82 }
83
test_asr_open(switch_asr_handle_t * ah,const char * codec,int rate,const char * dest,switch_asr_flag_t * flags)84 static switch_status_t test_asr_open(switch_asr_handle_t *ah, const char *codec, int rate, const char *dest, switch_asr_flag_t *flags)
85 {
86 test_asr_t *context;
87
88 if (switch_test_flag(ah, SWITCH_ASR_FLAG_CLOSED)) {
89 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "asr_open attempt on CLOSED asr handle\n");
90 return SWITCH_STATUS_FALSE;
91 }
92
93 if (!(context = (test_asr_t *) switch_core_alloc(ah->memory_pool, sizeof(*context)))) {
94 return SWITCH_STATUS_MEMERR;
95 }
96
97 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "codec = %s, rate = %d, dest = %s\n", codec, rate, dest);
98
99 ah->private_info = context;
100 ah->codec = switch_core_strdup(ah->memory_pool, codec);
101
102 if (rate > 16000) {
103 ah->native_rate = 16000;
104 }
105
106 context->thresh = 400;
107 context->silence_ms = 700;
108 context->voice_ms = 60;
109 context->start_input_timers = 1;
110 context->no_input_timeout = 5000;
111 context->speech_timeout = 10000;
112
113 context->vad = switch_vad_init(ah->native_rate, 1);
114 switch_vad_set_mode(context->vad, -1);
115 switch_vad_set_param(context->vad, "thresh", context->thresh);
116 switch_vad_set_param(context->vad, "silence_ms", context->silence_ms);
117 switch_vad_set_param(context->vad, "voice_ms", context->voice_ms);
118 switch_vad_set_param(context->vad, "debug", 0);
119
120 test_asr_reset(context);
121
122 return SWITCH_STATUS_SUCCESS;
123 }
124
test_asr_load_grammar(switch_asr_handle_t * ah,const char * grammar,const char * name)125 static switch_status_t test_asr_load_grammar(switch_asr_handle_t *ah, const char *grammar, const char *name)
126 {
127 test_asr_t *context = (test_asr_t *)ah->private_info;
128
129 if (switch_test_flag(ah, SWITCH_ASR_FLAG_CLOSED)) {
130 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "asr_open attempt on CLOSED asr handle\n");
131 return SWITCH_STATUS_FALSE;
132 }
133
134 switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_INFO, "load grammar %s\n", grammar);
135 context->grammar = switch_core_strdup(ah->memory_pool, grammar);
136 return SWITCH_STATUS_SUCCESS;
137 }
138
test_asr_unload_grammar(switch_asr_handle_t * ah,const char * name)139 static switch_status_t test_asr_unload_grammar(switch_asr_handle_t *ah, const char *name)
140 {
141 return SWITCH_STATUS_SUCCESS;
142 }
143
test_asr_close(switch_asr_handle_t * ah,switch_asr_flag_t * flags)144 static switch_status_t test_asr_close(switch_asr_handle_t *ah, switch_asr_flag_t *flags)
145 {
146 test_asr_t *context = (test_asr_t *)ah->private_info;
147 switch_status_t status = SWITCH_STATUS_SUCCESS;
148
149 if (switch_test_flag(ah, SWITCH_ASR_FLAG_CLOSED)) {
150 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Double ASR close!\n");
151 return SWITCH_STATUS_FALSE;
152 }
153
154 switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_NOTICE, "ASR closing ...\n");
155
156 if (context->vad) {
157 switch_vad_destroy(&context->vad);
158 }
159
160 switch_set_flag(ah, SWITCH_ASR_FLAG_CLOSED);
161 return status;
162 }
163
test_asr_feed(switch_asr_handle_t * ah,void * data,unsigned int len,switch_asr_flag_t * flags)164 static switch_status_t test_asr_feed(switch_asr_handle_t *ah, void *data, unsigned int len, switch_asr_flag_t *flags)
165 {
166 test_asr_t *context = (test_asr_t *) ah->private_info;
167 switch_status_t status = SWITCH_STATUS_SUCCESS;
168 switch_vad_state_t vad_state;
169
170 if (switch_test_flag(ah, SWITCH_ASR_FLAG_CLOSED)) {
171 return SWITCH_STATUS_BREAK;
172 }
173
174 if (switch_test_flag(context, ASRFLAG_RETURNED_RESULT) && switch_test_flag(ah, SWITCH_ASR_FLAG_AUTO_RESUME)) {
175 switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_DEBUG, "Auto Resuming\n");
176 test_asr_reset(context);
177 }
178
179 if (switch_test_flag(context, ASRFLAG_READY)) {
180 vad_state = switch_vad_process(context->vad, (int16_t *)data, len / sizeof(uint16_t));
181 if (vad_state == SWITCH_VAD_STATE_STOP_TALKING) {
182 switch_set_flag(context, ASRFLAG_RESULT);
183 switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_INFO, "Talking stopped, have result.\n");
184 switch_vad_reset(context->vad);
185 switch_clear_flag(context, ASRFLAG_READY);
186 } else if (vad_state == SWITCH_VAD_STATE_START_TALKING) {
187 switch_set_flag(context, ASRFLAG_START_OF_SPEECH);
188 context->speech_time = switch_micro_time_now();
189 }
190 }
191
192 return status;
193 }
194
test_asr_pause(switch_asr_handle_t * ah)195 static switch_status_t test_asr_pause(switch_asr_handle_t *ah)
196 {
197 test_asr_t *context = (test_asr_t *) ah->private_info;
198
199 if (switch_test_flag(ah, SWITCH_ASR_FLAG_CLOSED)) {
200 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "asr_pause attempt on CLOSED asr handle\n");
201 return SWITCH_STATUS_FALSE;
202 }
203
204 switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_DEBUG, "Pausing\n");
205 context->flags = 0;
206
207 return SWITCH_STATUS_SUCCESS;
208 }
209
test_asr_resume(switch_asr_handle_t * ah)210 static switch_status_t test_asr_resume(switch_asr_handle_t *ah)
211 {
212 test_asr_t *context = (test_asr_t *) ah->private_info;
213
214 if (switch_test_flag(ah, SWITCH_ASR_FLAG_CLOSED)) {
215 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "asr_resume attempt on CLOSED asr handle\n");
216 return SWITCH_STATUS_FALSE;
217 }
218
219 switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_DEBUG, "Resuming\n");
220 test_asr_reset(context);
221
222 return SWITCH_STATUS_SUCCESS;
223 }
224
test_asr_check_results(switch_asr_handle_t * ah,switch_asr_flag_t * flags)225 static switch_status_t test_asr_check_results(switch_asr_handle_t *ah, switch_asr_flag_t *flags)
226 {
227 test_asr_t *context = (test_asr_t *) ah->private_info;
228
229 if (switch_test_flag(context, ASRFLAG_RETURNED_RESULT) || switch_test_flag(ah, SWITCH_ASR_FLAG_CLOSED)) {
230 return SWITCH_STATUS_BREAK;
231 }
232
233 if (!switch_test_flag(context, ASRFLAG_RETURNED_START_OF_SPEECH) && switch_test_flag(context, ASRFLAG_START_OF_SPEECH)) {
234 return SWITCH_STATUS_SUCCESS;
235 }
236
237 if ((!switch_test_flag(context, ASRFLAG_RESULT)) && (!switch_test_flag(context, ASRFLAG_NOINPUT_TIMEOUT))) {
238 if (switch_test_flag(context, ASRFLAG_INPUT_TIMERS) && !(switch_test_flag(context, ASRFLAG_START_OF_SPEECH)) &&
239 context->no_input_timeout >= 0 &&
240 (switch_micro_time_now() - context->no_input_time) / 1000 >= context->no_input_timeout) {
241 switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_DEBUG, "NO INPUT TIMEOUT %" SWITCH_TIME_T_FMT "ms\n", (switch_micro_time_now() - context->no_input_time) / 1000);
242 switch_set_flag(context, ASRFLAG_NOINPUT_TIMEOUT);
243 } else if (switch_test_flag(context, ASRFLAG_START_OF_SPEECH) && context->speech_timeout > 0 && (switch_micro_time_now() - context->speech_time) / 1000 >= context->speech_timeout) {
244 switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_DEBUG, "SPEECH TIMEOUT %" SWITCH_TIME_T_FMT "ms\n", (switch_micro_time_now() - context->speech_time) / 1000);
245 if (switch_test_flag(context, ASRFLAG_START_OF_SPEECH)) {
246 switch_set_flag(context, ASRFLAG_RESULT);
247 } else {
248 switch_set_flag(context, ASRFLAG_NOINPUT_TIMEOUT);
249 }
250 }
251 }
252
253 return switch_test_flag(context, ASRFLAG_RESULT) || switch_test_flag(context, ASRFLAG_NOINPUT_TIMEOUT) ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_BREAK;
254 }
255
test_asr_get_results(switch_asr_handle_t * ah,char ** resultstr,switch_asr_flag_t * flags)256 static switch_status_t test_asr_get_results(switch_asr_handle_t *ah, char **resultstr, switch_asr_flag_t *flags)
257 {
258 test_asr_t *context = (test_asr_t *) ah->private_info;
259 switch_status_t status = SWITCH_STATUS_SUCCESS;
260
261 if (switch_test_flag(context, ASRFLAG_RETURNED_RESULT) || switch_test_flag(ah, SWITCH_ASR_FLAG_CLOSED)) {
262 return SWITCH_STATUS_FALSE;
263 }
264
265 if (switch_test_flag(context, ASRFLAG_RESULT)) {
266
267 *resultstr = switch_mprintf("{\"grammar\": \"%s\", \"text\": \"%s\", \"confidence\": %f}", context->grammar, context->result_text, context->result_confidence);
268
269 switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_ERROR, "Result: %s\n", *resultstr);
270
271 status = SWITCH_STATUS_SUCCESS;
272 } else if (switch_test_flag(context, ASRFLAG_NOINPUT_TIMEOUT)) {
273 switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_DEBUG, "Result: NO INPUT\n");
274
275 *resultstr = switch_mprintf("{\"grammar\": \"%s\", \"text\": \"\", \"confidence\": 0, \"error\": \"no_input\"}", context->grammar);
276
277 status = SWITCH_STATUS_SUCCESS;
278 } else if (!switch_test_flag(context, ASRFLAG_RETURNED_START_OF_SPEECH) && switch_test_flag(context, ASRFLAG_START_OF_SPEECH)) {
279 switch_set_flag(context, ASRFLAG_RETURNED_START_OF_SPEECH);
280 switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_DEBUG, "Result: START OF SPEECH\n");
281 status = SWITCH_STATUS_BREAK;
282 } else {
283 switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_ERROR, "Unexpected call to asr_get_results - no results to return!\n");
284 status = SWITCH_STATUS_FALSE;
285 }
286
287 if (status == SWITCH_STATUS_SUCCESS) {
288 switch_set_flag(context, ASRFLAG_RETURNED_RESULT);
289 switch_clear_flag(context, ASRFLAG_READY);
290 }
291
292 return status;
293 }
294
test_asr_start_input_timers(switch_asr_handle_t * ah)295 static switch_status_t test_asr_start_input_timers(switch_asr_handle_t *ah)
296 {
297 test_asr_t *context = (test_asr_t *) ah->private_info;
298
299 if (switch_test_flag(ah, SWITCH_ASR_FLAG_CLOSED)) {
300 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "asr_start_input_timers attempt on CLOSED asr handle\n");
301 return SWITCH_STATUS_FALSE;
302 }
303
304 switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_DEBUG, "start_input_timers\n");
305
306 if (!switch_test_flag(context, ASRFLAG_INPUT_TIMERS)) {
307 switch_set_flag(context, ASRFLAG_INPUT_TIMERS);
308 context->no_input_time = switch_micro_time_now();
309 } else {
310 switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_INFO, "Input timers already started\n");
311 }
312
313 return SWITCH_STATUS_SUCCESS;
314 }
315
test_asr_text_param(switch_asr_handle_t * ah,char * param,const char * val)316 static void test_asr_text_param(switch_asr_handle_t *ah, char *param, const char *val)
317 {
318 test_asr_t *context = (test_asr_t *) ah->private_info;
319
320 if (!zstr(param) && !zstr(val)) {
321 int nval = atoi(val);
322 double fval = atof(val);
323
324 if (!strcasecmp("no-input-timeout", param) && switch_is_number(val)) {
325 context->no_input_timeout = nval;
326 switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_DEBUG, "no-input-timeout = %d\n", context->no_input_timeout);
327 } else if (!strcasecmp("speech-timeout", param) && switch_is_number(val)) {
328 context->speech_timeout = nval;
329 switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_DEBUG, "speech-timeout = %d\n", context->speech_timeout);
330 } else if (!strcasecmp("start-input-timers", param)) {
331 context->start_input_timers = switch_true(val);
332 if (context->start_input_timers) {
333 switch_set_flag(context, ASRFLAG_INPUT_TIMERS);
334 } else {
335 switch_clear_flag(context, ASRFLAG_INPUT_TIMERS);
336 }
337 switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_DEBUG, "start-input-timers = %d\n", context->start_input_timers);
338 } else if (!strcasecmp("vad-mode", param)) {
339 switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_DEBUG, "vad-mode = %s\n", val);
340 if (context->vad) switch_vad_set_mode(context->vad, nval);
341 } else if (!strcasecmp("vad-voice-ms", param) && nval > 0) {
342 context->voice_ms = nval;
343 switch_vad_set_param(context->vad, "voice_ms", nval);
344 } else if (!strcasecmp("vad-silence-ms", param) && nval > 0) {
345 context->silence_ms = nval;
346 switch_vad_set_param(context->vad, "silence_ms", nval);
347 } else if (!strcasecmp("vad-thresh", param) && nval > 0) {
348 context->thresh = nval;
349 switch_vad_set_param(context->vad, "thresh", nval);
350 } else if (!strcasecmp("channel-uuid", param)) {
351 context->channel_uuid = switch_core_strdup(ah->memory_pool, val);
352 switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_DEBUG, "channel-uuid = %s\n", val);
353 } else if (!strcasecmp("result", param)) {
354 context->result_text = switch_core_strdup(ah->memory_pool, val);
355 switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_DEBUG, "result = %s\n", val);
356 } else if (!strcasecmp("confidence", param) && fval >= 0.0) {
357 context->result_confidence = fval;
358 switch_log_printf(SWITCH_CHANNEL_UUID_LOG(context->channel_uuid), SWITCH_LOG_DEBUG, "confidence = %f\n", fval);
359 }
360 }
361 }
362
SWITCH_MODULE_LOAD_FUNCTION(mod_test_load)363 SWITCH_MODULE_LOAD_FUNCTION(mod_test_load)
364 {
365 switch_asr_interface_t *asr_interface;
366
367 *module_interface = switch_loadable_module_create_module_interface(pool, modname);
368
369 asr_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_ASR_INTERFACE);
370 asr_interface->interface_name = "test";
371 asr_interface->asr_open = test_asr_open;
372 asr_interface->asr_load_grammar = test_asr_load_grammar;
373 asr_interface->asr_unload_grammar = test_asr_unload_grammar;
374 asr_interface->asr_close = test_asr_close;
375 asr_interface->asr_feed = test_asr_feed;
376 asr_interface->asr_resume = test_asr_resume;
377 asr_interface->asr_pause = test_asr_pause;
378 asr_interface->asr_check_results = test_asr_check_results;
379 asr_interface->asr_get_results = test_asr_get_results;
380 asr_interface->asr_start_input_timers = test_asr_start_input_timers;
381 asr_interface->asr_text_param = test_asr_text_param;
382
383 return SWITCH_STATUS_SUCCESS;
384 }
385
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_test_shutdown)386 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_test_shutdown)
387 {
388 return SWITCH_STATUS_SUCCESS;
389 }
390
SWITCH_MODULE_RUNTIME_FUNCTION(mod_test_runtime)391 SWITCH_MODULE_RUNTIME_FUNCTION(mod_test_runtime)
392 {
393 return SWITCH_STATUS_TERM;
394 }
395
396 /* For Emacs:
397 * Local Variables:
398 * mode:c
399 * indent-tabs-mode:t
400 * tab-width:4
401 * c-basic-offset:4
402 * End:
403 * For VIM:
404 * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
405 */
406