diff --git a/conf/freeswitch.xml b/conf/freeswitch.xml index 693486959f..828a94e79e 100644 --- a/conf/freeswitch.xml +++ b/conf/freeswitch.xml @@ -632,6 +632,43 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/src/include/switch_ivr.h b/src/include/switch_ivr.h index 198c19be34..fa74b85227 100644 --- a/src/include/switch_ivr.h +++ b/src/include/switch_ivr.h @@ -626,6 +626,13 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_menu_stack_xml_add_custom(switch_ivr_ */ SWITCH_DECLARE(switch_status_t) switch_ivr_menu_stack_xml_init(switch_ivr_menu_xml_ctx_t **xml_menu_ctx, switch_memory_pool_t *pool); +SWITCH_DECLARE(switch_status_t) switch_ivr_phrase_macro(switch_core_session_t *session, + char *macro_name, + char *data, + char *lang, + switch_input_callback_function_t input_callback, + void *buf, + uint32_t buflen); /** @} */ SWITCH_END_EXTERN_C diff --git a/src/include/switch_loadable_module.h b/src/include/switch_loadable_module.h index 82689c836e..436fab4e2f 100644 --- a/src/include/switch_loadable_module.h +++ b/src/include/switch_loadable_module.h @@ -75,6 +75,8 @@ struct switch_loadable_module_interface { const switch_directory_interface_t *directory_interface; /*! the table of chat interfaces the module has implmented */ const switch_chat_interface_t *chat_interface; + /*! the table of say interfaces the module has implmented */ + const switch_say_interface_t *say_interface; /*! the table of asr interfaces the module has implmented */ const switch_asr_interface_t *asr_interface; }; @@ -181,6 +183,13 @@ SWITCH_DECLARE(switch_directory_interface_t *) switch_loadable_module_get_direct */ SWITCH_DECLARE(switch_chat_interface_t *) switch_loadable_module_get_chat_interface(char *name); +/*! + \brief Retrieve the say interface by it's registered name + \param name the name of the say interface + \return the desired say interface + */ +SWITCH_DECLARE(switch_say_interface_t *) switch_loadable_module_get_say_interface(char *name); + /*! \brief Retrieve the list of loaded codecs into an array diff --git a/src/include/switch_module_interfaces.h b/src/include/switch_module_interfaces.h index 4e9d2902ea..b48bcff800 100644 --- a/src/include/switch_module_interfaces.h +++ b/src/include/switch_module_interfaces.h @@ -412,6 +412,20 @@ struct switch_speech_handle { void *private_info; }; +/*! \brief Abstract interface to a say module */ +struct switch_say_interface { + /*! the name of the interface */ + const char *interface_name; + /*! function to pass down to the module */ + switch_status_t (*say_function)(switch_core_session_t *session, + char *tosay, + switch_say_type_t type, + switch_say_method_t method, + switch_input_callback_function_t dtmf_callback, + void *buf, + uint32_t buflen); + const struct switch_say_interface *next; +}; /*! \brief Abstract interface to a chat module */ struct switch_chat_interface { diff --git a/src/include/switch_types.h b/src/include/switch_types.h index f04026f832..ed09ebd731 100644 --- a/src/include/switch_types.h +++ b/src/include/switch_types.h @@ -93,6 +93,33 @@ SWITCH_BEGIN_EXTERN_C #define SWITCH_BITS_PER_BYTE 8 typedef uint8_t switch_byte_t; +typedef enum { + SSM_NA, + SSM_PRONOUNCED, + SSM_ITERATED +} switch_say_method_t; + +typedef enum { + SST_NUMBER, + SST_ITEMS, + SST_PERSONS, + SST_MESSAGES, + SST_CURRENCY, + SST_TIME_MEASUREMENT, + SST_CURRENT_DATE, + SST_CURRENT_TIME, + SST_CURRENT_DATE_TIME, + SST_TELEPHONE_NUMBER, + SST_TELEPHONE_EXTENSION, + SST_URL, + SST_EMAIL_ADDRESS, + SST_POSTAL_ADDRESS, + SST_ACCOUNT_NUMBER, + SST_NAME_SPELLED, + SST_NAME_PHONETIC, +} switch_say_type_t; + + typedef enum { SMF_NONE = 0, SMF_REBRIDGE = (1 << 0), @@ -172,7 +199,8 @@ typedef enum { SWITCH_XML_SECTION_RESULT = 0, SWITCH_XML_SECTION_CONFIG = (1 << 0), SWITCH_XML_SECTION_DIRECTORY = (1 << 1), - SWITCH_XML_SECTION_DIALPLAN = (1 << 2) + SWITCH_XML_SECTION_DIALPLAN = (1 << 2), + SWITCH_XML_SECTION_PHRASES = (1 << 3) } switch_xml_section_t; /*! @@ -863,6 +891,7 @@ typedef struct switch_speech_interface switch_speech_interface_t; typedef struct switch_asr_interface switch_asr_interface_t; typedef struct switch_directory_interface switch_directory_interface_t; typedef struct switch_chat_interface switch_chat_interface_t; +typedef struct switch_say_interface switch_say_interface_t; typedef struct switch_core_port_allocator switch_core_port_allocator_t; typedef struct switch_media_bug switch_media_bug_t; typedef void (*switch_media_bug_callback_t)(switch_media_bug_t *, void *, switch_abc_type_t); diff --git a/src/mod/applications/mod_dptools/mod_dptools.c b/src/mod/applications/mod_dptools/mod_dptools.c index c6607cbf76..acd7e6e202 100644 --- a/src/mod/applications/mod_dptools/mod_dptools.c +++ b/src/mod/applications/mod_dptools/mod_dptools.c @@ -102,6 +102,32 @@ static void eval_function(switch_core_session_t *session, char *data) return; } +static void phrase_function(switch_core_session_t *session, char *data) +{ + switch_channel_t *channel; + char *mydata = NULL; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + if ((mydata = switch_core_session_strdup(session, data))) { + char *lang; + char *macro = mydata; + char *mdata = NULL; + + if ((mdata = strchr(macro, ','))) { + *mdata++ = '\0'; + } + if (!(lang = switch_channel_get_variable(channel, "language"))) { + lang = "en"; + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Execute %s(%s) lang %s\n", macro, mdata, lang); + switch_ivr_phrase_macro(session, macro, mdata, lang, NULL, NULL, 0); + } + +} + static void answer_function(switch_core_session_t *session, char *data) { switch_channel_t *channel; @@ -462,13 +488,23 @@ static const switch_application_interface_t eval_application_interface = { }; +static const switch_application_interface_t phrase_application_interface = { + /*.interface_name */ "phrase", + /*.application_function */ phrase_function, + /* long_desc */ "Say a Phrase", + /* short_desc */ "Say a Phrase", + /* syntax */ ",", + /*.next */ &eval_application_interface + +}; + static const switch_application_interface_t strftime_application_interface = { /*.interface_name */ "strftime", /*.application_function */ strftime_function, /* long_desc */ NULL, /* short_desc */ NULL, /* syntax */ NULL, - /*.next */ &eval_application_interface + /*.next */ &phrase_application_interface }; diff --git a/src/mod/say/mod_say_en/mod_say_en.c b/src/mod/say/mod_say_en/mod_say_en.c new file mode 100644 index 0000000000..27d2f3a906 --- /dev/null +++ b/src/mod/say/mod_say_en/mod_say_en.c @@ -0,0 +1,178 @@ +/* + * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * Copyright (C) 2005/2006, Anthony Minessale II + * + * Version: MPL 1.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application + * + * The Initial Developer of the Original Code is + * Anthony Minessale II + * Portions created by the Initial Developer are Copyright (C) + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Anthony Minessale II + * + * mod_say_en.c -- Say for English + * + */ +#include +#include + +static const char modname[] = "mod_say_en"; + +static switch_status_t en_say(switch_core_session_t *session, + char *tosay, + switch_say_type_t type, + switch_say_method_t method, + switch_input_callback_function_t input_callback, + void *buf, + uint32_t buflen) +{ + switch_channel_t *channel; + + assert(session != NULL); + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + switch(type) { + case SST_NUMBER: + case SST_ITEMS: + case SST_PERSONS: + case SST_MESSAGES: + { + int in; + int x, places[7] = {0}; + char tmp[25]; + + in = atoi(tosay); + + for(x = 6; x >= 0; x--) { + int num = pow(10, x); + if ((places[x] = in / num)) { + in -= places[x] * num; + } + } + + switch (method) { + case SSM_PRONOUNCED: + if (places[6]) { + snprintf(tmp, sizeof(tmp), "digits/%d.wav", places[6]); + switch_ivr_play_file(session, NULL, tmp, NULL, input_callback, buf, buflen); + switch_ivr_play_file(session, NULL, "digits/million.wav", NULL, input_callback, buf, buflen); + } + + if (places[5]) { + snprintf(tmp, sizeof(tmp), "digits/%d.wav", places[5]); + switch_ivr_play_file(session, NULL, tmp, NULL, input_callback, buf, buflen); + switch_ivr_play_file(session, NULL, "digits/hundred.wav", NULL, input_callback, buf, buflen); + } + + if (places[4]) { + if (places[4] > 1) { + snprintf(tmp, sizeof(tmp), "digits/%d0.wav", places[4]); + switch_ivr_play_file(session, NULL, tmp, NULL, input_callback, buf, buflen); + } else { + snprintf(tmp, sizeof(tmp), "digits/%d%d.wav", places[4], places[3]); + switch_ivr_play_file(session, NULL, tmp, NULL, input_callback, buf, buflen); + places[3] = 0; + } + } + + if (places[3]) { + snprintf(tmp, sizeof(tmp), "digits/%d.wav", places[3]); + switch_ivr_play_file(session, NULL, tmp, NULL, input_callback, buf, buflen); + } + + if (places[4] || places[3]) { + switch_ivr_play_file(session, NULL, "digits/thousand.wav", NULL, input_callback, buf, buflen); + } + + if (places[2]) { + snprintf(tmp, sizeof(tmp), "digits/%d.wav", places[2]); + switch_ivr_play_file(session, NULL, tmp, NULL, input_callback, buf, buflen); + switch_ivr_play_file(session, NULL, "digits/hundred.wav", NULL, input_callback, buf, buflen); + } + + if (places[1]) { + if (places[1] > 1) { + snprintf(tmp, sizeof(tmp), "digits/%d0.wav", places[1]); + switch_ivr_play_file(session, NULL, tmp, NULL, input_callback, buf, buflen); + } else { + snprintf(tmp, sizeof(tmp), "digits/%d%d.wav", places[1], places[0]); + switch_ivr_play_file(session, NULL, tmp, NULL, input_callback, buf, buflen); + places[0] = 0; + } + } + + if (places[0]) { + snprintf(tmp, sizeof(tmp), "digits/%d.wav", places[0]); + switch_ivr_play_file(session, NULL, tmp, NULL, input_callback, buf, buflen); + } + + break; + case SSM_ITERATED: + for(x = 7; x >= 0; x--) { + if (places[x]) { + snprintf(tmp, sizeof(tmp), "digits/%d.wav", places[x]); + switch_ivr_play_file(session, NULL, tmp, NULL, input_callback, buf, buflen); + } + } + break; + default: + break; + } + } + break; + default: + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Finish ME!\n"); + break; + } + + + + return SWITCH_STATUS_SUCCESS; +} + +static const switch_chat_interface_t en_say_interface= { + /*.name */ "en", + /*.say_function */ en_say, +}; + +static switch_loadable_module_interface_t say_en_module_interface = { + /*.module_name */ modname, + /*.endpoint_interface */ NULL, + /*.timer_interface */ NULL, + /*.dialplan_interface */ NULL, + /*.codec_interface */ NULL, + /*.application_interface */ NULL, + /*.api_interface */ NULL, + /*.file_interface */ NULL, + /*.speech_interface */ NULL, + /*.directory_interface */ NULL, + /*.chat_interface */ NULL, + /*.say_inteface*/ &en_say_interface, + /*.asr_interface*/ NULL +}; + +SWITCH_MOD_DECLARE(switch_status_t) switch_module_load(const switch_loadable_module_interface_t **module_interface, char *filename) +{ + /* connect my internal structure to the blank pointer passed to me */ + *module_interface = &say_en_module_interface; + + /* indicate that the module should continue to be loaded */ + return SWITCH_STATUS_SUCCESS; +} + diff --git a/src/switch_ivr.c b/src/switch_ivr.c index a0ed7741e7..879f31a096 100644 --- a/src/switch_ivr.c +++ b/src/switch_ivr.c @@ -977,8 +977,23 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_play_file(switch_core_session_t *sess char *title = "", *copyright = "", *software = "", *artist = "", *comment = "", *date = ""; uint8_t asis = 0; char *ext; + char *prefix; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + prefix = switch_channel_get_variable(channel, "sound_prefix"); if (file) { + if (prefix && *file != '/' && *file != '\\' && *(file+1) != ':') { + char *new_file; + uint32_t len; + len = (uint32_t)strlen(file) + (uint32_t)strlen(prefix) + 10; + new_file = switch_core_session_alloc(session, len); + snprintf(new_file, len, "%s/%s", prefix, file); + file = new_file; + } + if ((ext = strrchr(file, '.'))) { ext++; } else { @@ -1004,10 +1019,6 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_play_file(switch_core_session_t *sess fh->samples = 0; } - - channel = switch_core_session_get_channel(session); - assert(channel != NULL); - if (switch_core_file_open(fh, file, SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, @@ -1054,7 +1065,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_play_file(switch_core_session_t *sess date = (char *) switch_core_session_strdup(session, (char *)p); switch_channel_set_variable(channel, "RECORD_DATE", (char *)p); } - +#if 0 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "OPEN FILE %s %uhz %u channels\n" "TITLE=%s\n" @@ -1069,6 +1080,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_play_file(switch_core_session_t *sess artist, comment, date); +#endif assert(read_codec != NULL); interval = read_codec->implementation->microseconds_per_frame / 1000; @@ -4336,6 +4348,230 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_menu_stack_xml_build(switch_ivr_menu_ return status; } + +static char *SAY_METHOD_NAMES[] = { + "N/A", + "PRONOUNCED", + "ITERATED", + NULL +}; + +static char *SAY_TYPE_NAMES[] = { + "NUMBER", + "ITEMS", + "PERSONS", + "MESSAGES", + "CURRENCY", + "TIME_MEASUREMENT", + "CURRENT_DATE", + "CURRENT_TIME", + "CURRENT_DATE_TIME", + "TELEPHONE_NUMBER", + "TELEPHONE_EXTENSION", + "URL", + "EMAIL_ADDRESS", + "POSTAL_ADDRESS", + "ACCOUNT_NUMBER", + "NAME_SPELLED", + "NAME_PHONETIC", + NULL +}; + + +static switch_say_method_t get_say_method_by_name(char *name) +{ + int x = 0; + for (x = 0; SAY_METHOD_NAMES[x]; x++) { + if (!strcasecmp(SAY_METHOD_NAMES[x], name)) { + break; + } + } + + return (switch_say_method_t) x; +} + +static switch_say_method_t get_say_type_by_name(char *name) +{ + int x = 0; + for (x = 0; SAY_TYPE_NAMES[x]; x++) { + if (!strcasecmp(SAY_TYPE_NAMES[x], name)) { + break; + } + } + + return (switch_say_method_t) x; +} + + +SWITCH_DECLARE(switch_status_t) switch_ivr_phrase_macro(switch_core_session_t *session, + char *macro_name, + char *data, + char *lang, + switch_input_callback_function_t input_callback, + void *buf, + uint32_t buflen) +{ + switch_xml_t cfg, xml = NULL, language, macros, macro, input, action; + char *lname = NULL, *mname = NULL, hint_data[1024] = "", enc_hint[1024] = ""; + switch_status_t status = SWITCH_STATUS_GENERR; + char *old_sound_prefix, *sound_path = NULL, *tts_engine = NULL, *tts_voice = NULL; + switch_channel_t *channel; + + channel = switch_core_session_get_channel(session); + assert(channel != NULL); + + switch_url_encode(data, enc_hint, sizeof(enc_hint)); + snprintf(hint_data, sizeof(hint_data), "macro_name=%s&lang=%s&data=%s", macro_name, lang, enc_hint); + + if (switch_xml_locate("phrases", NULL, NULL, NULL, &xml, &cfg, hint_data) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "open of phrases failed.\n"); + goto done; + } + + if (!(macros = switch_xml_child(cfg, "macros"))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "can't find macros tag.\n"); + goto done; + } + + if (!(language = switch_xml_child(macros, "language"))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "can't find language tag.\n"); + goto done; + } + + while(language) { + if ((lname = switch_xml_attr(language, "name")) && !strcasecmp(lname, lang)) { + break; + } + language = language->next; + } + + if (!language) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "can't find language %s.\n", lang); + goto done; + } + + sound_path = switch_xml_attr_soft(language, "sound_path"); + tts_engine = switch_xml_attr_soft(language, "tts_engine"); + tts_voice = switch_xml_attr_soft(language, "tts_voice"); + + old_sound_prefix = switch_channel_get_variable(channel, "sound_prefix"); + switch_channel_set_variable(channel, "sound_prefix", sound_path); + + if (!(macro = switch_xml_child(language, "macro"))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "can't find any macro tags.\n"); + goto done; + } + + while(macro) { + if ((mname = switch_xml_attr(macro, "name")) && !strcasecmp(mname, macro_name)) { + break; + } + macro = macro->next; + } + + if (!macro) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "can't find macro %s.\n", macro_name); + goto done; + } + + if (!(input = switch_xml_child(macro, "input"))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "can't find any input tags.\n"); + goto done; + } + + switch_channel_pre_answer(channel); + + while(input) { + char *pattern = switch_xml_attr(input, "pattern"); + + if (pattern) { + pcre *re = NULL; + int proceed = 0, ovector[30]; + char substituted[1024] = ""; + char *odata = NULL; + + if ((proceed = switch_perform_regex(data, pattern, &re, ovector, sizeof(ovector) / sizeof(ovector[0])))) { + for (action = switch_xml_child(input, "action"); action; action = action->next) { + char *adata = switch_xml_attr_soft(action, "data"); + char *func = switch_xml_attr_soft(action, "function"); + + if (strchr(pattern, '(') && strchr(adata, '$')) { + switch_perform_substitution(re, proceed, adata, data, substituted, sizeof(substituted), ovector); + odata = substituted; + } else { + odata = adata; + } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Handle %s:[%s] (%s)\n", func, odata, lang); + + if (!strcasecmp(func, "play-file")) { + switch_ivr_play_file(session, NULL, odata, NULL, input_callback, buf, buflen); + } else if (!strcasecmp(func, "execute")) { + const switch_application_interface_t *application_interface; + char *app_name = NULL; + char *app_arg = NULL; + + if ((app_name = strdup(odata))) { + char *e = NULL; + if ((app_arg = strchr(app_name, '('))) { + *app_arg++ = '\0'; + if ((e = strchr(app_arg, ')'))) { + *e = '\0'; + } + if (app_name && app_arg && e && (application_interface = switch_loadable_module_get_application_interface(app_name))) { + if (application_interface->application_function) { + application_interface->application_function(session, app_arg); + } + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Application!\n"); + } + } + switch_safe_free(app_name); + } + } else if (!strcasecmp(func, "say")) { + switch_say_interface_t *si; + if ((si = switch_loadable_module_get_say_interface(lang))) { + char *say_type = switch_xml_attr_soft(action, "type"); + char *say_method = switch_xml_attr_soft(action, "method"); + + si->say_function(session, odata, get_say_type_by_name(say_type), get_say_method_by_name(say_method), input_callback, buf, buflen); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invaid SAY Interface [%s]!\n", lang); + } + } else if (!strcasecmp(func, "speak-text")) { + switch_codec_t *read_codec; + if ((read_codec = switch_core_session_get_read_codec(session))) { + switch_ivr_speak_text(session, + tts_engine, + tts_voice, + NULL, + read_codec->implementation->samples_per_second, + input_callback, + odata, + buf, + buflen); + } + } + } + } + + switch_clean_re(re); + } + + input = input->next; + } + + done: + + switch_channel_set_variable(channel, "sound_prefix", old_sound_prefix); + + if (xml) { + switch_xml_free(xml); + } + return status; +} + + /* For Emacs: * Local Variables: * mode:c diff --git a/src/switch_loadable_module.c b/src/switch_loadable_module.c index a84d706d4a..368b918e29 100644 --- a/src/switch_loadable_module.c +++ b/src/switch_loadable_module.c @@ -54,6 +54,7 @@ struct switch_loadable_module_container { switch_hash_t *asr_hash; switch_hash_t *directory_hash; switch_hash_t *chat_hash; + switch_hash_t *say_hash; switch_memory_pool_t *pool; }; @@ -260,6 +261,20 @@ static switch_status_t switch_loadable_module_process(char *key, switch_loadable switch_core_hash_insert(loadable_modules.chat_hash, (char *) ptr->interface_name, (void *) ptr); } } + + if (new_module->module_interface->say_interface) { + const switch_say_interface_t *ptr; + + for (ptr = new_module->module_interface->say_interface; ptr; ptr = ptr->next) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Adding Say interface '%s'\n", ptr->interface_name); + if (switch_event_create(&event, SWITCH_EVENT_MODULE_LOAD) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "type", "say"); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "name", "%s", ptr->interface_name); + switch_event_fire(&event); + } + switch_core_hash_insert(loadable_modules.say_hash, (char *) ptr->interface_name, (void *) ptr); + } + } return SWITCH_STATUS_SUCCESS; @@ -506,6 +521,7 @@ SWITCH_DECLARE(switch_status_t) switch_loadable_module_init() switch_core_hash_init(&loadable_modules.asr_hash, loadable_modules.pool); switch_core_hash_init(&loadable_modules.directory_hash, loadable_modules.pool); switch_core_hash_init(&loadable_modules.chat_hash, loadable_modules.pool); + switch_core_hash_init(&loadable_modules.say_hash, loadable_modules.pool); switch_core_hash_init(&loadable_modules.dialplan_hash, loadable_modules.pool); if ((xml = switch_xml_open_cfg(cf, &cfg, NULL))) { @@ -683,6 +699,11 @@ SWITCH_DECLARE(switch_chat_interface_t *) switch_loadable_module_get_chat_interf return switch_core_hash_find(loadable_modules.chat_hash, name); } +SWITCH_DECLARE(switch_say_interface_t *) switch_loadable_module_get_say_interface(char *name) +{ + return switch_core_hash_find(loadable_modules.say_hash, name); +} + SWITCH_DECLARE(int) switch_loadable_module_get_codecs(switch_memory_pool_t *pool, const switch_codec_implementation_t **array, int arraylen) { diff --git a/src/switch_xml.c b/src/switch_xml.c index abcca867ff..f2da227986 100644 --- a/src/switch_xml.c +++ b/src/switch_xml.c @@ -107,6 +107,7 @@ static struct xml_section_t SECTIONS[] = { { "config", SWITCH_XML_SECTION_CONFIG}, { "directory", SWITCH_XML_SECTION_DIRECTORY}, { "dialplan", SWITCH_XML_SECTION_DIALPLAN}, + { "phrases", SWITCH_XML_SECTION_PHRASES}, { NULL, 0} };