struct recognizer_data {
/** the available grammars */
switch_hash_t *grammars;
- /** the last grammar used (for pause/resume) */
- grammar_t *last_grammar;
+ /** the enabled grammars */
+ switch_hash_t *enabled_grammars;
/** recognize result */
char *result;
/** true, if voice has started */
static switch_status_t recog_asr_open(switch_asr_handle_t *ah, const char *codec, int rate, const char *dest, switch_asr_flag_t *flags);
static switch_status_t recog_asr_load_grammar(switch_asr_handle_t *ah, const char *grammar, const char *name);
static switch_status_t recog_asr_unload_grammar(switch_asr_handle_t *ah, const char *name);
+static switch_status_t recog_asr_enable_grammar(switch_asr_handle_t *ah, const char *name);
+static switch_status_t recog_asr_disable_grammar(switch_asr_handle_t *ah, const char *name);
+static switch_status_t recog_asr_disable_all_grammars(switch_asr_handle_t *ah);
static switch_status_t recog_asr_close(switch_asr_handle_t *ah, switch_asr_flag_t *flags);
static switch_status_t recog_asr_feed(switch_asr_handle_t *ah, void *data, unsigned int len, switch_asr_flag_t *flags);
#if 0
static apt_bool_t recog_stream_read(mpf_audio_stream_t *stream, mpf_frame_t *frame);
/* recognizer specific speech_channel_funcs */
-static switch_status_t recog_channel_start(speech_channel_t *schannel, const char *name);
+static switch_status_t recog_channel_start(speech_channel_t *schannel);
static switch_status_t recog_channel_load_grammar(speech_channel_t *schannel, const char *name, grammar_type_t type, const char *data);
static switch_status_t recog_channel_unload_grammar(speech_channel_t *schannel, const char *name);
+static switch_status_t recog_channel_enable_grammar(speech_channel_t *schannel, const char *name);
+static switch_status_t recog_channel_disable_grammar(speech_channel_t *schannel, const char *name);
+static switch_status_t recog_channel_disable_all_grammars(speech_channel_t *schannel);
static switch_status_t recog_channel_check_results(speech_channel_t *schannel);
static switch_status_t recog_channel_set_start_of_input(speech_channel_t *schannel);
static switch_status_t recog_channel_start_input_timers(speech_channel_t *schannel);
* Start RECOGNIZE request
*
* @param schannel the channel to start
- * @param name the name of the grammar to use or NULL if to reuse the last grammar
* @return SWITCH_STATUS_SUCCESS if successful
*/
-static switch_status_t recog_channel_start(speech_channel_t *schannel, const char *name)
+static switch_status_t recog_channel_start(speech_channel_t *schannel)
{
switch_status_t status = SWITCH_STATUS_SUCCESS;
+ switch_hash_index_t *egk;
mrcp_message_t *mrcp_message;
mrcp_recog_header_t *recog_header;
mrcp_generic_header_t *generic_header;
recognizer_data_t *r;
char *start_input_timers;
const char *mime_type;
- grammar_t *grammar = NULL;
+ char *key;
+ switch_size_t len;
+ grammar_t *grammar;
+ switch_size_t grammar_uri_count = 0;
+ switch_size_t grammar_uri_list_len = 0;
+ char *grammar_uri_list = NULL;
switch_mutex_lock(schannel->mutex);
if (schannel->state != SPEECH_CHANNEL_READY) {
start_input_timers = (char *) switch_core_hash_find(schannel->params, "start-input-timers");
r->timers_started = zstr(start_input_timers) || strcasecmp(start_input_timers, "false");
- /* get the cached grammar */
- if (zstr(name)) {
- grammar = r->last_grammar;
- } else {
- grammar = (grammar_t *) switch_core_hash_find(r->grammars, name);
- r->last_grammar = grammar;
- }
- if (grammar == NULL) {
- if (name) {
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "(%s) Undefined grammar, %s\n", schannel->name, name);
- } else {
- switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "(%s) No grammar specified\n", schannel->name);
+ /* count enabled grammars */
+ for (egk = switch_hash_first(NULL, r->enabled_grammars); egk; egk = switch_hash_next(egk)) {
+ // NOTE: This postponed type check is necessary to allow a non-URI-list grammar to execute alone
+ if (grammar_uri_count == 1 && grammar->type != GRAMMAR_TYPE_URI)
+ goto no_grammar_alone;
+ ++grammar_uri_count;
+ switch_hash_this(egk, (void *) &key, NULL, (void *) &grammar);
+ if (grammar->type != GRAMMAR_TYPE_URI && grammar_uri_count != 1) {
+ no_grammar_alone:
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "(%s) Grammar '%s' can only be used alone (not a URI list)\n", schannel->name, key);
+ status = SWITCH_STATUS_FALSE;
+ goto done;
}
+ len = strlen(grammar->data);
+ if (!len)
+ continue;
+ grammar_uri_list_len += len;
+ if (grammar->data[len - 1] != '\n')
+ grammar_uri_list_len += 2;
+ }
+
+ switch (grammar_uri_count) {
+ case 0:
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "(%s) No grammar specified\n", schannel->name);
status = SWITCH_STATUS_FALSE;
goto done;
+ case 1:
+ /* grammar should already be the unique grammar */
+ break;
+ default:
+ /* get the enabled grammars list */
+ grammar_uri_list = switch_core_alloc(schannel->memory_pool, grammar_uri_list_len + 1);
+ grammar_uri_list_len = 0;
+ for (egk = switch_hash_first(NULL, r->enabled_grammars); egk; egk = switch_hash_next(egk)) {
+ switch_hash_this(egk, (void *) &key, NULL, (void *) &grammar);
+ len = strlen(grammar->data);
+ if (!len)
+ continue;
+ memcpy(&(grammar_uri_list[grammar_uri_list_len]), grammar->data, len);
+ grammar_uri_list_len += len;
+ if (grammar_uri_list[grammar_uri_list_len - 1] != '\n')
+ {
+ grammar_uri_list_len += 2;
+ grammar_uri_list[grammar_uri_list_len - 2] = '\r';
+ grammar_uri_list[grammar_uri_list_len - 1] = '\n';
+ }
+ }
+ grammar_uri_list[grammar_uri_list_len++] = '\0';
+ grammar = NULL;
}
/* create MRCP message */
}
/* set Content-Type */
- mime_type = grammar_type_to_mime(grammar->type, schannel->profile);
+ mime_type = grammar_type_to_mime(grammar ? grammar->type : GRAMMAR_TYPE_URI, schannel->profile);
if (zstr(mime_type)) {
status = SWITCH_STATUS_FALSE;
goto done;
mrcp_generic_header_property_add(mrcp_message, GENERIC_HEADER_CONTENT_TYPE);
/* set Content-ID for inline grammars */
- if (grammar->type != GRAMMAR_TYPE_URI) {
+ if (grammar && grammar->type != GRAMMAR_TYPE_URI) {
apt_string_assign(&generic_header->content_id, grammar->name, mrcp_message->pool);
mrcp_generic_header_property_add(mrcp_message, GENERIC_HEADER_CONTENT_ID);
}
recog_channel_set_params(schannel, mrcp_message, generic_header, recog_header);
/* set message body */
- apt_string_assign(&mrcp_message->body, grammar->data, mrcp_message->pool);
+ apt_string_assign(&mrcp_message->body, grammar ? grammar->data : grammar_uri_list, mrcp_message->pool);
/* Empty audio queue and send RECOGNIZE to MRCP server */
audio_queue_clear(schannel->audio_queue);
} else {
recognizer_data_t *r = (recognizer_data_t *) schannel->data;
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "(%s) Unloading grammar %s\n", schannel->name, grammar_name);
+ switch_core_hash_delete(r->enabled_grammars, grammar_name);
switch_core_hash_delete(r->grammars, grammar_name);
}
return status;
}
+/**
+ * Enable speech recognition grammar
+ *
+ * @param schannel the recognizer channel
+ * @param grammar_name the name of the grammar to enable
+ * @return SWITCH_STATUS_SUCCESS if successful
+ */
+static switch_status_t recog_channel_enable_grammar(speech_channel_t *schannel, const char *grammar_name)
+{
+ switch_status_t status = SWITCH_STATUS_SUCCESS;
+
+ if (zstr(grammar_name)) {
+ status = SWITCH_STATUS_FALSE;
+ } else {
+ recognizer_data_t *r = (recognizer_data_t *) schannel->data;
+ grammar_t *grammar;
+ grammar = (grammar_t *) switch_core_hash_find(r->grammars, grammar_name);
+ if (grammar == NULL)
+ {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "(%s) Undefined grammar, %s\n", schannel->name, grammar_name);
+ status = SWITCH_STATUS_FALSE;
+ }
+ else {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "(%s) Enabling grammar %s\n", schannel->name, grammar_name);
+ switch_core_hash_insert(r->enabled_grammars, grammar_name, grammar);
+ }
+ }
+
+ return status;
+}
+
+/**
+ * Disable speech recognition grammar
+ *
+ * @param schannel the recognizer channel
+ * @param grammar_name the name of the grammar to disable
+ * @return SWITCH_STATUS_SUCCESS if successful
+ */
+static switch_status_t recog_channel_disable_grammar(speech_channel_t *schannel, const char *grammar_name)
+{
+ switch_status_t status = SWITCH_STATUS_SUCCESS;
+
+ if (zstr(grammar_name)) {
+ status = SWITCH_STATUS_FALSE;
+ } else {
+ recognizer_data_t *r = (recognizer_data_t *) schannel->data;
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "(%s) Disabling grammar %s\n", schannel->name, grammar_name);
+ switch_core_hash_delete(r->enabled_grammars, grammar_name);
+ }
+
+ return status;
+}
+
+/**
+ * Disable all speech recognition grammars
+ *
+ * @param schannel the recognizer channel
+ * @return SWITCH_STATUS_SUCCESS if successful
+ */
+static switch_status_t recog_channel_disable_all_grammars(speech_channel_t *schannel)
+{
+ switch_status_t status = SWITCH_STATUS_SUCCESS;
+
+ recognizer_data_t *r = (recognizer_data_t *) schannel->data;
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "(%s) Disabling all grammars\n", schannel->name);
+ switch_core_hash_destroy(&r->enabled_grammars);
+ switch_core_hash_init(&r->enabled_grammars, schannel->memory_pool);
+
+ return status;
+}
+
/**
* Check if recognition is complete
*
schannel->data = r;
memset(r, 0, sizeof(recognizer_data_t));
switch_core_hash_init(&r->grammars, ah->memory_pool);
+ switch_core_hash_init(&r->enabled_grammars, ah->memory_pool);
/* Open the channel */
if (zstr(profile_name)) {
start_recognize = (char *) switch_core_hash_find(schannel->params, "start-recognize");
if (zstr(start_recognize) || strcasecmp(start_recognize, "false"))
- status = recog_channel_start(schannel, name);
+ {
+ if (recog_channel_disable_all_grammars(schannel) != SWITCH_STATUS_SUCCESS) {
+ status = SWITCH_STATUS_FALSE;
+ goto done;
+ }
+ if (recog_channel_enable_grammar(schannel, name) != SWITCH_STATUS_SUCCESS) {
+ status = SWITCH_STATUS_FALSE;
+ goto done;
+ }
+ status = recog_channel_start(schannel);
+ }
done:
return status;
}
+/**
+ * Process asr_enable_grammar request from FreeSWITCH.
+ *
+ * FreeSWITCH sends this request to enable recognition on this grammar.
+ * @param ah the FreeSWITCH speech recognition handle
+ * @param name the grammar name.
+ */
+static switch_status_t recog_asr_enable_grammar(switch_asr_handle_t *ah, const char *name)
+{
+ switch_status_t status = SWITCH_STATUS_SUCCESS;
+ speech_channel_t *schannel = (speech_channel_t *) ah->private_info;
+ if (zstr(name) || speech_channel_stop(schannel) != SWITCH_STATUS_SUCCESS || recog_channel_enable_grammar(schannel, name) != SWITCH_STATUS_SUCCESS) {
+ status = SWITCH_STATUS_FALSE;
+ }
+ return status;
+}
+
+/**
+ * Process asr_disable_grammar request from FreeSWITCH.
+ *
+ * FreeSWITCH sends this request to disable recognition on this grammar.
+ * @param ah the FreeSWITCH speech recognition handle
+ * @param name the grammar name.
+ */
+static switch_status_t recog_asr_disable_grammar(switch_asr_handle_t *ah, const char *name)
+{
+ switch_status_t status = SWITCH_STATUS_SUCCESS;
+ speech_channel_t *schannel = (speech_channel_t *) ah->private_info;
+ if (zstr(name) || speech_channel_stop(schannel) != SWITCH_STATUS_SUCCESS || recog_channel_disable_grammar(schannel, name) != SWITCH_STATUS_SUCCESS) {
+ status = SWITCH_STATUS_FALSE;
+ }
+ return status;
+}
+
+/**
+ * Process asr_disable_all_grammars request from FreeSWITCH.
+ *
+ * FreeSWITCH sends this request to disable recognition of all grammars.
+ * @param ah the FreeSWITCH speech recognition handle
+ * @param name the grammar name.
+ */
+static switch_status_t recog_asr_disable_all_grammars(switch_asr_handle_t *ah)
+{
+ switch_status_t status = SWITCH_STATUS_SUCCESS;
+ speech_channel_t *schannel = (speech_channel_t *) ah->private_info;
+ if (speech_channel_stop(schannel) != SWITCH_STATUS_SUCCESS || recog_channel_disable_all_grammars(schannel) != SWITCH_STATUS_SUCCESS) {
+ status = SWITCH_STATUS_FALSE;
+ }
+ return status;
+}
+
/**
* Process asr_close request from FreeSWITCH
*
speech_channel_stop(schannel);
speech_channel_destroy(schannel);
switch_core_hash_destroy(&r->grammars);
+ switch_core_hash_destroy(&r->enabled_grammars);
/* this lets FreeSWITCH's speech_thread know the handle is closed */
switch_set_flag(ah, SWITCH_ASR_FLAG_CLOSED);
/**
* Process asr_start request from FreeSWITCH
* @param ah the FreeSWITCH speech recognition handle
- * @param name name of the grammar to use
* @return SWITCH_STATUS_SUCCESS if successful
*/
-static switch_status_t recog_asr_start(switch_asr_handle_t *ah, const char *name)
+static switch_status_t recog_asr_start(switch_asr_handle_t *ah)
{
switch_status_t status;
speech_channel_t *schannel = (speech_channel_t *) ah->private_info;
- status = recog_channel_start(schannel, name);
+ status = recog_channel_start(schannel);
return status;
}
#endif
static switch_status_t recog_asr_resume(switch_asr_handle_t *ah)
{
speech_channel_t *schannel = (speech_channel_t *) ah->private_info;
- return recog_channel_start(schannel, NULL);
+ return recog_channel_start(schannel);
}
/**
asr_interface->asr_open = recog_asr_open;
asr_interface->asr_load_grammar = recog_asr_load_grammar;
asr_interface->asr_unload_grammar = recog_asr_unload_grammar;
+ asr_interface->asr_enable_grammar = recog_asr_enable_grammar;
+ asr_interface->asr_disable_grammar = recog_asr_disable_grammar;
+ asr_interface->asr_disable_all_grammars = recog_asr_disable_all_grammars;
asr_interface->asr_close = recog_asr_close;
asr_interface->asr_feed = recog_asr_feed;
#if 0