提交 255ffd46 authored 作者: Chris Rienzo's avatar Chris Rienzo

mod_rayo: add some hacky support for SISR in DTMF recognizer

上级 6476adc4
...@@ -397,9 +397,10 @@ static int isdtmf(const char digit) ...@@ -397,9 +397,10 @@ static int isdtmf(const char digit)
/** /**
* Construct an NLSML result for digit match * Construct an NLSML result for digit match
* @param digits the matching digits * @param digits the matching digits
* @param interpretation the optional digit interpretation
* @return the NLSML <result> * @return the NLSML <result>
*/ */
iks *nlsml_create_dtmf_match(const char *digits) iks *nlsml_create_dtmf_match(const char *digits, const char *interpretation)
{ {
iks *result = iks_new("result"); iks *result = iks_new("result");
iks_insert_attrib(result, "xmlns", NLSML_NS); iks_insert_attrib(result, "xmlns", NLSML_NS);
...@@ -410,10 +411,11 @@ iks *nlsml_create_dtmf_match(const char *digits) ...@@ -410,10 +411,11 @@ iks *nlsml_create_dtmf_match(const char *digits)
int num_digits = strlen(digits); int num_digits = strlen(digits);
switch_stream_handle_t stream = { 0 }; switch_stream_handle_t stream = { 0 };
iks *interpretation = iks_insert(result, "interpretation"); iks *interpretation_node = iks_insert(result, "interpretation");
iks *input = iks_insert(interpretation, "input"); iks *input_node = iks_insert(interpretation_node, "input");
iks_insert_attrib(input, "mode", "dtmf"); iks *instance_node = iks_insert(interpretation_node, "instance");
iks_insert_attrib(input, "confidence", "100"); iks_insert_attrib(input_node, "mode", "dtmf");
iks_insert_attrib(input_node, "confidence", "100");
SWITCH_STANDARD_STREAM(stream); SWITCH_STANDARD_STREAM(stream);
for (i = 0; i < num_digits; i++) { for (i = 0; i < num_digits; i++) {
...@@ -426,7 +428,13 @@ iks *nlsml_create_dtmf_match(const char *digits) ...@@ -426,7 +428,13 @@ iks *nlsml_create_dtmf_match(const char *digits)
} }
} }
} }
iks_insert_cdata(input, stream.data, strlen(stream.data)); iks_insert_cdata(input_node, stream.data, strlen(stream.data));
if (zstr(interpretation)) {
iks_insert_cdata(instance_node, stream.data, strlen(stream.data));
} else {
iks_insert_cdata(instance_node, interpretation, strlen(interpretation));
}
switch_safe_free(stream.data); switch_safe_free(stream.data);
} }
return result; return result;
......
...@@ -42,7 +42,7 @@ enum nlsml_match_type { ...@@ -42,7 +42,7 @@ enum nlsml_match_type {
extern int nlsml_init(void); extern int nlsml_init(void);
enum nlsml_match_type nlsml_parse(const char *result, const char *uuid); enum nlsml_match_type nlsml_parse(const char *result, const char *uuid);
iks *nlsml_normalize(const char *result); iks *nlsml_normalize(const char *result);
extern iks *nlsml_create_dtmf_match(const char *digits); extern iks *nlsml_create_dtmf_match(const char *digits, const char *interpretation);
#endif #endif
......
...@@ -157,6 +157,7 @@ static switch_status_t input_component_on_dtmf(switch_core_session_t *session, c ...@@ -157,6 +157,7 @@ static switch_status_t input_component_on_dtmf(switch_core_session_t *session, c
int is_term_digit = 0; int is_term_digit = 0;
struct input_component *component; struct input_component *component;
enum srgs_match_type match; enum srgs_match_type match;
const char *interpretation = NULL;
switch_mutex_lock(handler->mutex); switch_mutex_lock(handler->mutex);
...@@ -179,7 +180,7 @@ static switch_status_t input_component_on_dtmf(switch_core_session_t *session, c ...@@ -179,7 +180,7 @@ static switch_status_t input_component_on_dtmf(switch_core_session_t *session, c
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Collected term digit = \"%c\"\n", dtmf->digit); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Collected term digit = \"%c\"\n", dtmf->digit);
} }
match = srgs_grammar_match(component->grammar, component->digits); match = srgs_grammar_match(component->grammar, component->digits, &interpretation);
/* adjust result if terminating digit was pressed */ /* adjust result if terminating digit was pressed */
if (is_term_digit) { if (is_term_digit) {
...@@ -208,7 +209,7 @@ static switch_status_t input_component_on_dtmf(switch_core_session_t *session, c ...@@ -208,7 +209,7 @@ static switch_status_t input_component_on_dtmf(switch_core_session_t *session, c
break; break;
} }
case SMT_MATCH_END: { case SMT_MATCH_END: {
iks *result = nlsml_create_dtmf_match(component->digits); iks *result = nlsml_create_dtmf_match(component->digits, interpretation);
/* notify of match and remove input component */ /* notify of match and remove input component */
handler->dtmf_component = NULL; handler->dtmf_component = NULL;
switch_core_media_bug_remove(session, &handler->bug); switch_core_media_bug_remove(session, &handler->bug);
...@@ -248,13 +249,14 @@ static switch_bool_t input_component_bug_callback(switch_media_bug_t *bug, void ...@@ -248,13 +249,14 @@ static switch_bool_t input_component_bug_callback(switch_media_bug_t *bug, void
int elapsed_ms = (switch_micro_time_now() - component->last_digit_time) / 1000; int elapsed_ms = (switch_micro_time_now() - component->last_digit_time) / 1000;
if (component->num_digits && component->inter_digit_timeout > 0 && elapsed_ms > component->inter_digit_timeout) { if (component->num_digits && component->inter_digit_timeout > 0 && elapsed_ms > component->inter_digit_timeout) {
enum srgs_match_type match; enum srgs_match_type match;
const char *interpretation = NULL;
handler->dtmf_component = NULL; handler->dtmf_component = NULL;
switch_core_media_bug_set_flag(bug, SMBF_PRUNE); switch_core_media_bug_set_flag(bug, SMBF_PRUNE);
/* we got some input, check for match */ /* we got some input, check for match */
match = srgs_grammar_match(component->grammar, component->digits); match = srgs_grammar_match(component->grammar, component->digits, &interpretation);
if (match == SMT_MATCH || match == SMT_MATCH_END) { if (match == SMT_MATCH || match == SMT_MATCH_END) {
iks *result = nlsml_create_dtmf_match(component->digits); iks *result = nlsml_create_dtmf_match(component->digits, interpretation);
/* notify of match */ /* notify of match */
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "MATCH = %s\n", component->digits); switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "MATCH = %s\n", component->digits);
send_match_event(RAYO_COMPONENT(component), result); send_match_event(RAYO_COMPONENT(component), result);
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include "srgs.h" #include "srgs.h"
#define MAX_RECURSION 100 #define MAX_RECURSION 100
#define MAX_TAGS 30
/** function to handle tag attributes */ /** function to handle tag attributes */
typedef int (* tag_attribs_fn)(struct srgs_grammar *, char **); typedef int (* tag_attribs_fn)(struct srgs_grammar *, char **);
...@@ -111,6 +112,7 @@ struct item_value { ...@@ -111,6 +112,7 @@ struct item_value {
int repeat_min; int repeat_min;
int repeat_max; int repeat_max;
const char *weight; const char *weight;
char *tag;
}; };
/** /**
...@@ -161,6 +163,10 @@ struct srgs_grammar { ...@@ -161,6 +163,10 @@ struct srgs_grammar {
struct srgs_node *cur; struct srgs_node *cur;
/** rule names mapped to node */ /** rule names mapped to node */
switch_hash_t *rules; switch_hash_t *rules;
/** possible matching tags */
const char *tags[MAX_TAGS];
/** number of tags */
int tag_count;
/** grammar encoding */ /** grammar encoding */
char *encoding; char *encoding;
/** grammar language */ /** grammar language */
...@@ -708,6 +714,30 @@ static int tag_hook(void *user_data, char *name, char **atts, int type) ...@@ -708,6 +714,30 @@ static int tag_hook(void *user_data, char *name, char **atts, int type)
return result; return result;
} }
/**
* Process <tag> CDATA
* @param grammar the grammar
* @param data the CDATA
* @param len the CDATA length
* @return IKS_OK
*/
static int process_cdata_tag(struct srgs_grammar *grammar, char *data, size_t len)
{
struct srgs_node *item = grammar->cur->parent;
if (item && item->type == SNT_ITEM) {
item->value.item.tag = switch_core_alloc(grammar->pool, sizeof(char) * (len + 1));
item->value.item.tag[len] = '\0';
strncpy(item->value.item.tag, data, len);
if (grammar->tag_count < MAX_TAGS) {
grammar->tags[grammar->tag_count++] = item->value.item.tag;
} else {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "too many <tag>s\n");
return IKS_BADXML;
}
}
return IKS_OK;
}
/** /**
* Process CDATA grammar tokens * Process CDATA grammar tokens
* @param grammar the grammar * @param grammar the grammar
...@@ -956,8 +986,12 @@ static int create_regexes(struct srgs_grammar *grammar, struct srgs_node *node, ...@@ -956,8 +986,12 @@ static int create_regexes(struct srgs_grammar *grammar, struct srgs_node *node,
case SNT_ITEM: case SNT_ITEM:
if (node->child) { if (node->child) {
struct srgs_node *item = node->child; struct srgs_node *item = node->child;
if (node->value.item.repeat_min != 1 || node->value.item.repeat_max != 1) { if (node->value.item.repeat_min != 1 || node->value.item.repeat_max != 1 || !zstr(node->value.item.tag)) {
if (zstr(node->value.item.tag)) {
stream->write_function(stream, "%s", "(?:"); stream->write_function(stream, "%s", "(?:");
} else {
stream->write_function(stream, "(?P<%s>", node->value.item.tag);
}
} }
for(; item; item = item->next) { for(; item; item = item->next) {
if (!create_regexes(grammar, item, stream)) { if (!create_regexes(grammar, item, stream)) {
...@@ -980,6 +1014,8 @@ static int create_regexes(struct srgs_grammar *grammar, struct srgs_node *node, ...@@ -980,6 +1014,8 @@ static int create_regexes(struct srgs_grammar *grammar, struct srgs_node *node,
} else { } else {
stream->write_function(stream, "){%i}", node->value.item.repeat_min); stream->write_function(stream, "){%i}", node->value.item.repeat_min);
} }
} else if (!zstr(node->value.item.tag)) {
stream->write_function(stream, "%s", ")");
} }
} }
break; break;
...@@ -1171,7 +1207,7 @@ struct srgs_grammar *srgs_parse(struct srgs_parser *parser, const char *document ...@@ -1171,7 +1207,7 @@ struct srgs_grammar *srgs_parse(struct srgs_parser *parser, const char *document
} }
#define MAX_INPUT_SIZE 128 #define MAX_INPUT_SIZE 128
#define OVECTOR_SIZE 30 #define OVECTOR_SIZE MAX_TAGS
#define WORKSPACE_SIZE 1024 #define WORKSPACE_SIZE 1024
/** /**
...@@ -1214,15 +1250,17 @@ static int is_match_end(pcre *compiled_regex, const char *input) ...@@ -1214,15 +1250,17 @@ static int is_match_end(pcre *compiled_regex, const char *input)
* Find a match * Find a match
* @param grammar the grammar to match * @param grammar the grammar to match
* @param input the input to compare * @param input the input to compare
* @param interpretation the (optional) interpretation of the input result
* @return the match result * @return the match result
*/ */
enum srgs_match_type srgs_grammar_match(struct srgs_grammar *grammar, const char *input) enum srgs_match_type srgs_grammar_match(struct srgs_grammar *grammar, const char *input, const char **interpretation)
{ {
int result = 0; int result = 0;
int ovector[OVECTOR_SIZE]; int ovector[OVECTOR_SIZE];
int workspace[WORKSPACE_SIZE];
pcre *compiled_regex; pcre *compiled_regex;
*interpretation = NULL;
if (zstr(input)) { if (zstr(input)) {
return SMT_NO_MATCH; return SMT_NO_MATCH;
} }
...@@ -1234,12 +1272,24 @@ enum srgs_match_type srgs_grammar_match(struct srgs_grammar *grammar, const char ...@@ -1234,12 +1272,24 @@ enum srgs_match_type srgs_grammar_match(struct srgs_grammar *grammar, const char
if (!(compiled_regex = get_compiled_regex(grammar))) { if (!(compiled_regex = get_compiled_regex(grammar))) {
return SMT_NO_MATCH; return SMT_NO_MATCH;
} }
result = pcre_dfa_exec(compiled_regex, NULL, input, strlen(input), 0, PCRE_PARTIAL, result = pcre_exec(compiled_regex, NULL, input, strlen(input), 0, PCRE_PARTIAL,
ovector, sizeof(ovector) / sizeof(ovector[0]), ovector, OVECTOR_SIZE);
workspace, sizeof(workspace) / sizeof(workspace[0]));
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "match = %i\n", result); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "match = %i\n", result);
if (result > 0) { if (result > 0) {
int i;
char buffer[MAX_INPUT_SIZE + 1];
buffer[MAX_INPUT_SIZE] = '\0';
/* find matching instance... */
for (i = 0; i < grammar->tag_count; i++) {
buffer[0] = '\0';
if (pcre_copy_named_substring(compiled_regex, input, ovector, result, grammar->tags[i], buffer, MAX_INPUT_SIZE) != PCRE_ERROR_NOSUBSTRING && !zstr_buf(buffer)) {
*interpretation = grammar->tags[i];
break;
}
}
if (is_match_end(compiled_regex, input)) { if (is_match_end(compiled_regex, input)) {
return SMT_MATCH_END; return SMT_MATCH_END;
} }
...@@ -1562,7 +1612,7 @@ int srgs_init(void) ...@@ -1562,7 +1612,7 @@ int srgs_init(void)
add_root_tag_def("grammar", process_grammar, process_cdata_bad, "meta,metadata,lexicon,tag,rule"); add_root_tag_def("grammar", process_grammar, process_cdata_bad, "meta,metadata,lexicon,tag,rule");
add_tag_def("ruleref", process_ruleref, process_cdata_bad, ""); add_tag_def("ruleref", process_ruleref, process_cdata_bad, "");
add_tag_def("token", process_attribs_ignore, process_cdata_ignore, ""); add_tag_def("token", process_attribs_ignore, process_cdata_ignore, "");
add_tag_def("tag", process_attribs_ignore, process_cdata_ignore, ""); add_tag_def("tag", process_attribs_ignore, process_cdata_tag, "");
add_tag_def("one-of", process_attribs_ignore, process_cdata_tokens, "item"); add_tag_def("one-of", process_attribs_ignore, process_cdata_tokens, "item");
add_tag_def("item", process_item, process_cdata_tokens, "token,ruleref,item,one-of,tag"); add_tag_def("item", process_item, process_cdata_tokens, "token,ruleref,item,one-of,tag");
add_tag_def("rule", process_rule, process_cdata_tokens, "token,ruleref,item,one-of,tag,example"); add_tag_def("rule", process_rule, process_cdata_tokens, "token,ruleref,item,one-of,tag,example");
......
...@@ -51,7 +51,7 @@ extern struct srgs_grammar *srgs_parse(struct srgs_parser *parser, const char *d ...@@ -51,7 +51,7 @@ extern struct srgs_grammar *srgs_parse(struct srgs_parser *parser, const char *d
extern const char *srgs_grammar_to_regex(struct srgs_grammar *grammar); extern const char *srgs_grammar_to_regex(struct srgs_grammar *grammar);
extern const char *srgs_grammar_to_jsgf(struct srgs_grammar *grammar); extern const char *srgs_grammar_to_jsgf(struct srgs_grammar *grammar);
extern const char *srgs_grammar_to_jsgf_file(struct srgs_grammar *grammar, const char *basedir, const char *ext); extern const char *srgs_grammar_to_jsgf_file(struct srgs_grammar *grammar, const char *basedir, const char *ext);
extern enum srgs_match_type srgs_grammar_match(struct srgs_grammar *grammar, const char *input); extern enum srgs_match_type srgs_grammar_match(struct srgs_grammar *grammar, const char *input, const char **interpretation);
extern void srgs_parser_destroy(struct srgs_parser *parser); extern void srgs_parser_destroy(struct srgs_parser *parser);
#endif #endif
......
...@@ -245,14 +245,15 @@ static const char *nlsml_dtmf_result = ...@@ -245,14 +245,15 @@ static const char *nlsml_dtmf_result =
"<result xmlns='http://www.ietf.org/xml/ns/mrcpv2' " "<result xmlns='http://www.ietf.org/xml/ns/mrcpv2' "
"xmlns:xf='http://www.w3.org/2000/xforms'><interpretation>" "xmlns:xf='http://www.w3.org/2000/xforms'><interpretation>"
"<input mode='dtmf' confidence='100'>1 2 3 4</input>" "<input mode='dtmf' confidence='100'>1 2 3 4</input>"
"<instance>1 2 3 4</instance>"
"</interpretation></result>"; "</interpretation></result>";
/** /**
* Test parsing NLSML example results * Test creating DTMF match result
*/ */
static void test_create_dtmf_match(void) static void test_create_dtmf_match(void)
{ {
iks *result = nlsml_create_dtmf_match("1234"); iks *result = nlsml_create_dtmf_match("1234", NULL);
char *result_str; char *result_str;
ASSERT_NOT_NULL(result); ASSERT_NOT_NULL(result);
result_str = iks_string(NULL, result); result_str = iks_string(NULL, result);
...@@ -260,6 +261,26 @@ static void test_create_dtmf_match(void) ...@@ -260,6 +261,26 @@ static void test_create_dtmf_match(void)
iks_free(result_str); iks_free(result_str);
} }
static const char *nlsml_dtmf_instance_result =
"<result xmlns='http://www.ietf.org/xml/ns/mrcpv2' "
"xmlns:xf='http://www.w3.org/2000/xforms'><interpretation>"
"<input mode='dtmf' confidence='100'>1</input>"
"<instance>foo</instance>"
"</interpretation></result>";
/**
* Test creating DTMF match result with instance interpretation
*/
static void test_create_dtmf_instance(void)
{
iks *result = nlsml_create_dtmf_match("1", "foo");
char *result_str;
ASSERT_NOT_NULL(result);
result_str = iks_string(NULL, result);
ASSERT_STRING_EQUALS(nlsml_dtmf_instance_result, result_str);
iks_free(result_str);
}
static const char *nlsml_good_normalized = static const char *nlsml_good_normalized =
"<result x-model='http://theYesNoModel'" "<result x-model='http://theYesNoModel'"
" xmlns:xf='http://www.w3.org/2000/xforms'" " xmlns:xf='http://www.w3.org/2000/xforms'"
...@@ -295,6 +316,7 @@ int main(int argc, char **argv) ...@@ -295,6 +316,7 @@ int main(int argc, char **argv)
nlsml_init(); nlsml_init();
TEST(test_parse_nlsml_examples); TEST(test_parse_nlsml_examples);
TEST(test_create_dtmf_match); TEST(test_create_dtmf_match);
TEST(test_create_dtmf_instance);
TEST(test_normalize); TEST(test_normalize);
return 0; return 0;
} }
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论