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