From d5ef86d7788ef0080ca3be7e2ff39bda989d4b4d Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Tue, 29 Mar 2011 19:55:28 -0500 Subject: [PATCH] introduce new say_string method of doing say and use it in mod_say_en as an example. try: eval ${say_string en.gsm en current_date_time pronounced ${strepoch()}} from the cli with this patch. We can do more to centralize the say things and go back and apply it to other langs, using this method you can set the desired file ext as well which I think is a bounty.... --- src/include/switch_apr.h | 2 + src/include/switch_core.h | 9 + src/include/switch_ivr.h | 10 + src/include/switch_module_interfaces.h | 25 +- src/include/switch_types.h | 17 ++ .../applications/mod_commands/mod_commands.c | 54 ++++ src/mod/say/mod_say_en/mod_say_en.c | 288 +++++++++++++----- src/switch_channel.c | 17 +- src/switch_event.c | 18 +- src/switch_ivr.c | 128 ++++++++ src/switch_loadable_module.c | 91 ++++++ 11 files changed, 548 insertions(+), 111 deletions(-) diff --git a/src/include/switch_apr.h b/src/include/switch_apr.h index 62559faf63..2306075e1f 100644 --- a/src/include/switch_apr.h +++ b/src/include/switch_apr.h @@ -145,6 +145,8 @@ SWITCH_DECLARE(int) switch_snprintf(_Out_z_cap_(len) SWITCH_DECLARE(int) switch_vasprintf(_Out_opt_ char **buf, _In_z_ _Printf_format_string_ const char *format, _In_ va_list ap); +SWITCH_DECLARE(int) switch_vsnprintf(char *buf, switch_size_t len, const char *format, va_list ap); + SWITCH_DECLARE(char *) switch_copy_string(_Out_z_cap_(dst_size) char *dst, _In_z_ const char *src, _In_ switch_size_t dst_size); diff --git a/src/include/switch_core.h b/src/include/switch_core.h index e54de72027..2946478c89 100644 --- a/src/include/switch_core.h +++ b/src/include/switch_core.h @@ -2240,6 +2240,15 @@ SWITCH_DECLARE(switch_status_t) switch_core_del_registration(const char *user, c */ SWITCH_DECLARE(switch_status_t) switch_core_expire_registration(int force); + +SWITCH_DECLARE(char *) switch_say_file_handle_get_variable(switch_say_file_handle_t *sh, const char *var); +SWITCH_DECLARE(char *) switch_say_file_handle_get_path(switch_say_file_handle_t *sh); +SWITCH_DECLARE(char *) switch_say_file_handle_detach_path(switch_say_file_handle_t *sh); +SWITCH_DECLARE(void) switch_say_file_handle_destroy(switch_say_file_handle_t **sh); +SWITCH_DECLARE(switch_status_t) switch_say_file_handle_create(switch_say_file_handle_t **sh, const char *ext, switch_event_t **var_event); +SWITCH_DECLARE(void) switch_say_file(switch_say_file_handle_t *sh, const char *fmt, ...); + + SWITCH_END_EXTERN_C #endif /* For Emacs: diff --git a/src/include/switch_ivr.h b/src/include/switch_ivr.h index 9b353f3ded..36eaaec9ca 100644 --- a/src/include/switch_ivr.h +++ b/src/include/switch_ivr.h @@ -859,6 +859,16 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_say(switch_core_session_t *session, const char *say_gender, switch_input_args_t *args); +SWITCH_DECLARE(switch_status_t) switch_ivr_say_string(switch_core_session_t *session, + const char *lang, + const char *ext, + const char *tosay, + const char *module_name, + const char *say_type, + const char *say_method, + const char *say_gender, + char **rstr); + SWITCH_DECLARE(switch_say_method_t) switch_ivr_get_say_method_by_name(const char *name); SWITCH_DECLARE(switch_say_gender_t) switch_ivr_get_say_gender_by_name(const char *name); SWITCH_DECLARE(switch_say_type_t) switch_ivr_get_say_type_by_name(const char *name); diff --git a/src/include/switch_module_interfaces.h b/src/include/switch_module_interfaces.h index 90d7342ef4..33c4c22bac 100644 --- a/src/include/switch_module_interfaces.h +++ b/src/include/switch_module_interfaces.h @@ -103,29 +103,9 @@ struct switch_stream_handle { }; struct switch_io_event_hooks; +struct switch_say_file_handle; - - -typedef switch_call_cause_t (*switch_io_outgoing_channel_t) - - - - - - - - - - - - - - - - - - - +typedef switch_call_cause_t (*switch_io_outgoing_channel_t) (switch_core_session_t *, switch_event_t *, switch_caller_profile_t *, switch_core_session_t **, switch_memory_pool_t **, switch_originate_flag_t, switch_call_cause_t *); typedef switch_status_t (*switch_io_read_frame_t) (switch_core_session_t *, switch_frame_t **, switch_io_flag_t, int); @@ -490,6 +470,7 @@ struct switch_say_interface { const char *interface_name; /*! function to pass down to the module */ switch_say_callback_t say_function; + switch_say_string_callback_t say_string_function; switch_thread_rwlock_t *rwlock; int refs; switch_mutex_t *reflock; diff --git a/src/include/switch_types.h b/src/include/switch_types.h index e732bca597..81cc3fb719 100644 --- a/src/include/switch_types.h +++ b/src/include/switch_types.h @@ -36,6 +36,7 @@ #define SWITCH_TYPES_H #include + SWITCH_BEGIN_EXTERN_C #define SWITCH_ENT_ORIGINATE_DELIM ":_:" #define SWITCH_BLANK_STRING "" @@ -1735,6 +1736,7 @@ typedef switch_status_t (*switch_stream_handle_raw_write_function_t) (switch_str typedef switch_status_t (*switch_api_function_t) (_In_opt_z_ const char *cmd, _In_opt_ switch_core_session_t *session, _In_ switch_stream_handle_t *stream); + #define SWITCH_STANDARD_API(name) static switch_status_t name (_In_opt_z_ const char *cmd, _In_opt_ switch_core_session_t *session, _In_ switch_stream_handle_t *stream) typedef switch_status_t (*switch_input_callback_function_t) (switch_core_session_t *session, void *input, @@ -1772,17 +1774,32 @@ typedef struct { switch_ivr_dmachine_t *dmachine; } switch_input_args_t; + typedef struct { switch_say_type_t type; switch_say_method_t method; switch_say_gender_t gender; + const char *ext; } switch_say_args_t; + typedef switch_status_t (*switch_say_callback_t) (switch_core_session_t *session, char *tosay, switch_say_args_t *say_args, switch_input_args_t *args); +typedef switch_status_t (*switch_say_string_callback_t) (switch_core_session_t *session, + char *tosay, + switch_say_args_t *say_args, char **rstr); + +struct switch_say_file_handle; +typedef struct switch_say_file_handle switch_say_file_handle_t; + +typedef switch_status_t (*switch_new_say_callback_t) (switch_say_file_handle_t *sh, + char *tosay, + switch_say_args_t *say_args); + + typedef struct switch_xml *switch_xml_t; typedef struct switch_core_time_duration switch_core_time_duration_t; typedef switch_xml_t(*switch_xml_search_function_t) (const char *section, diff --git a/src/mod/applications/mod_commands/mod_commands.c b/src/mod/applications/mod_commands/mod_commands.c index fd2081e31d..6fb35598c2 100644 --- a/src/mod/applications/mod_commands/mod_commands.c +++ b/src/mod/applications/mod_commands/mod_commands.c @@ -116,6 +116,59 @@ static switch_status_t select_url(const char *user, return SWITCH_STATUS_SUCCESS; } + +#define SAY_STRING_SYNTAX "[.] [.] [] " +SWITCH_STANDARD_API(say_string_function) +{ + char *argv[6] = { 0 }; + int argc; + char *lbuf = NULL, *string = NULL; + int err = 1, par = 0; + char *p, *ext = "wav"; + + if (cmd) { + lbuf = strdup(cmd); + } + + if (lbuf && (argc = switch_separate_string(lbuf, ' ', argv, (sizeof(argv) / sizeof(argv[0])))) && (argc == 5 || argc == 6)) { + + if ((p = strchr(argv[0], '.'))) { + *p++ = '\0'; + ext = p; + par++; + } + + if (!par && (p = strchr(argv[1], '.'))) { + *p++ = '\0'; + ext = p; + } + switch_ivr_say_string(session, + argv[1], + ext, + (argc == 5) ? argv[4] : argv[5], + argv[0], + argv[2], + argv[3], + (argc == 6) ? argv[4] : NULL , + &string); + if (string) { + stream->write_function(stream, "%s", string); + free(string); + err = 0; + } + } + + if (err) { + stream->write_function(stream, "-ERR Usage: %s\n", SAY_STRING_SYNTAX); + } + + free(lbuf); + + return SWITCH_STATUS_SUCCESS; + +} + + SWITCH_STANDARD_API(reg_url_function) { char *data; @@ -5105,6 +5158,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load) SWITCH_ADD_API(commands_api_interface, "reload", "Reload Module", reload_function, UNLOAD_SYNTAX); SWITCH_ADD_API(commands_api_interface, "reloadxml", "Reload XML", reload_xml_function, ""); SWITCH_ADD_API(commands_api_interface, "replace", "replace a string", replace_function, "||"); + SWITCH_ADD_API(commands_api_interface, "say_string", "", say_string_function, SAY_STRING_SYNTAX); SWITCH_ADD_API(commands_api_interface, "sched_api", "Schedule an api command", sched_api_function, SCHED_SYNTAX); SWITCH_ADD_API(commands_api_interface, "sched_broadcast", "Schedule a broadcast event to a running call", sched_broadcast_function, SCHED_BROADCAST_SYNTAX); diff --git a/src/mod/say/mod_say_en/mod_say_en.c b/src/mod/say/mod_say_en/mod_say_en.c index 6c604f208e..ec577374dc 100644 --- a/src/mod/say/mod_say_en/mod_say_en.c +++ b/src/mod/say/mod_say_en/mod_say_en.c @@ -51,7 +51,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_say_en_load); SWITCH_MODULE_DEFINITION(mod_say_en, mod_say_en_load, NULL, NULL); -#define say_num(num, meth) { \ + +#define say_num(_sh, num, meth) { \ char tmp[80]; \ switch_status_t tstatus; \ switch_say_method_t smeth = say_args->method; \ @@ -59,7 +60,7 @@ SWITCH_MODULE_DEFINITION(mod_say_en, mod_say_en_load, NULL, NULL); say_args->type = SST_ITEMS; say_args->method = meth; \ switch_snprintf(tmp, sizeof(tmp), "%u", (unsigned)num); \ if ((tstatus = \ - en_say_general_count(session, tmp, say_args, args)) \ + en_say_general_count(_sh, tmp, say_args)) \ != SWITCH_STATUS_SUCCESS) { \ return tstatus; \ } \ @@ -67,57 +68,44 @@ SWITCH_MODULE_DEFINITION(mod_say_en, mod_say_en_load, NULL, NULL); } \ -#define say_file(...) { \ - char tmp[80]; \ - switch_status_t tstatus; \ - switch_snprintf(tmp, sizeof(tmp), __VA_ARGS__); \ - if ((tstatus = \ - switch_ivr_play_file(session, NULL, tmp, args)) \ - != SWITCH_STATUS_SUCCESS){ \ - return tstatus; \ - } \ - if (!switch_channel_ready(switch_core_session_get_channel(session))) { \ - return SWITCH_STATUS_FALSE; \ - }} \ - -static switch_status_t play_group(switch_say_method_t method, int a, int b, int c, char *what, switch_core_session_t *session, switch_input_args_t *args) +static switch_status_t play_group(switch_say_method_t method, int a, int b, int c, char *what, switch_say_file_handle_t *sh) { if (a) { - say_file("digits/%d.wav", a); - say_file("digits/hundred.wav"); + switch_say_file(sh, "digits/%d", a); + switch_say_file(sh, "digits/hundred"); } if (b) { if (b > 1) { if ((c == 0) && (method == SSM_COUNTED)) { - say_file("digits/h-%d0.wav", b); + switch_say_file(sh, "digits/h-%d0", b); } else { - say_file("digits/%d0.wav", b); + switch_say_file(sh, "digits/%d0", b); } } else { - say_file("digits/%d%d.wav", b, c); + switch_say_file(sh, "digits/%d%d", b, c); c = 0; } } if (c) { if (method == SSM_COUNTED) { - say_file("digits/h-%d.wav", c); + switch_say_file(sh, "digits/h-%d", c); } else { - say_file("digits/%d.wav", c); + switch_say_file(sh, "digits/%d", c); } } if (what && (a || b || c)) { - say_file(what); + switch_say_file(sh, what); } return SWITCH_STATUS_SUCCESS; } -static switch_status_t en_say_general_count(switch_core_session_t *session, char *tosay, switch_say_args_t *say_args, switch_input_args_t *args) +static switch_status_t en_say_general_count(switch_say_file_handle_t *sh, char *tosay, switch_say_args_t *say_args) { int in; int x = 0; @@ -129,7 +117,7 @@ static switch_status_t en_say_general_count(switch_core_session_t *session, char if ((tosay = switch_strip_commas(tosay, sbuf, sizeof(sbuf)-1))) { char *p; for (p = tosay; p && *p; p++) { - say_file("digits/%c.wav", *p); + switch_say_file(sh, "digits/%c", *p); } } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Parse Error!\n"); @@ -156,13 +144,13 @@ static switch_status_t en_say_general_count(switch_core_session_t *session, char switch (say_args->method) { case SSM_COUNTED: case SSM_PRONOUNCED: - if ((status = play_group(SSM_PRONOUNCED, places[8], places[7], places[6], "digits/million.wav", session, args)) != SWITCH_STATUS_SUCCESS) { + if ((status = play_group(SSM_PRONOUNCED, places[8], places[7], places[6], "digits/million", sh)) != SWITCH_STATUS_SUCCESS) { return status; } - if ((status = play_group(SSM_PRONOUNCED, places[5], places[4], places[3], "digits/thousand.wav", session, args)) != SWITCH_STATUS_SUCCESS) { + if ((status = play_group(SSM_PRONOUNCED, places[5], places[4], places[3], "digits/thousand", sh)) != SWITCH_STATUS_SUCCESS) { return status; } - if ((status = play_group(say_args->method, places[2], places[1], places[0], NULL, session, args)) != SWITCH_STATUS_SUCCESS) { + if ((status = play_group(say_args->method, places[2], places[1], places[0], NULL, sh)) != SWITCH_STATUS_SUCCESS) { return status; } break; @@ -170,20 +158,21 @@ static switch_status_t en_say_general_count(switch_core_session_t *session, char break; } } else { - say_file("digits/0.wav"); + switch_say_file(sh, "digits/0"); } return SWITCH_STATUS_SUCCESS; } -static switch_status_t en_say_time(switch_core_session_t *session, char *tosay, switch_say_args_t *say_args, switch_input_args_t *args) +static switch_status_t en_say_time(switch_say_file_handle_t *sh, char *tosay, switch_say_args_t *say_args) { int32_t t; switch_time_t target = 0, target_now = 0; switch_time_exp_t tm, tm_now; uint8_t say_date = 0, say_time = 0, say_year = 0, say_month = 0, say_dow = 0, say_day = 0, say_yesterday = 0, say_today = 0; - switch_channel_t *channel = switch_core_session_get_channel(session); - const char *tz = switch_channel_get_variable(channel, "timezone"); + const char *tz = NULL; + + tz = switch_say_file_handle_get_variable(sh, "timezone"); if (say_args->type == SST_TIME_MEASUREMENT) { int64_t hours = 0; @@ -192,7 +181,7 @@ static switch_status_t en_say_time(switch_core_session_t *session, char *tosay, int64_t r = 0; if (strchr(tosay, ':')) { - char *tme = switch_core_session_strdup(session, tosay); + char *tme = strdup(tosay); char *p; if ((p = strrchr(tme, ':'))) { @@ -208,6 +197,7 @@ static switch_status_t en_say_time(switch_core_session_t *session, char *tosay, minutes = atoi(tme); } } + free(tme); } else { if ((seconds = atol(tosay)) <= 0) { seconds = (int64_t) switch_epoch_time_now(NULL); @@ -227,39 +217,39 @@ static switch_status_t en_say_time(switch_core_session_t *session, char *tosay, } if (hours) { - say_num(hours, SSM_PRONOUNCED); + say_num(sh, hours, SSM_PRONOUNCED); if (hours == 1) { - say_file("time/hour.wav"); + switch_say_file(sh, "time/hour"); } else { - say_file("time/hours.wav"); + switch_say_file(sh, "time/hours"); } } else { - say_file("digits/0.wav"); - say_file("time/hours.wav"); + switch_say_file(sh, "digits/0"); + switch_say_file(sh, "time/hours"); } if (minutes) { - say_num(minutes, SSM_PRONOUNCED); + say_num(sh, minutes, SSM_PRONOUNCED); if (minutes == 1) { - say_file("time/minute.wav"); + switch_say_file(sh, "time/minute"); } else { - say_file("time/minutes.wav"); + switch_say_file(sh, "time/minutes"); } } else { - say_file("digits/0.wav"); - say_file("time/minutes.wav"); + switch_say_file(sh, "digits/0"); + switch_say_file(sh, "time/minutes"); } if (seconds) { - say_num(seconds, SSM_PRONOUNCED); + say_num(sh, seconds, SSM_PRONOUNCED); if (seconds == 1) { - say_file("time/second.wav"); + switch_say_file(sh, "time/second"); } else { - say_file("time/seconds.wav"); + switch_say_file(sh, "time/seconds"); } } else { - say_file("digits/0.wav"); - say_file("time/seconds.wav"); + switch_say_file(sh, "digits/0"); + switch_say_file(sh, "time/seconds"); } return SWITCH_STATUS_SUCCESS; @@ -275,7 +265,7 @@ static switch_status_t en_say_time(switch_core_session_t *session, char *tosay, if (tz) { int check = atoi(tz); - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Timezone is [%s]\n", tz); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Timezone is [%s]\n", tz); if (check) { switch_time_exp_tz(&tm, target, check); switch_time_exp_tz(&tm_now, target_now, check); @@ -329,13 +319,13 @@ static switch_status_t en_say_time(switch_core_session_t *session, char *tosay, } if (say_today) { - say_file("time/today.wav"); + switch_say_file(sh, "time/today"); } if (say_yesterday) { - say_file("time/yesterday.wav"); + switch_say_file(sh, "time/yesterday"); } if (say_dow) { - say_file("time/day-%d.wav", tm.tm_wday); + switch_say_file(sh, "time/day-%d", tm.tm_wday); } if (say_date) { @@ -344,20 +334,20 @@ static switch_status_t en_say_time(switch_core_session_t *session, char *tosay, } if (say_month) { - say_file("time/mon-%d.wav", tm.tm_mon); + switch_say_file(sh, "time/mon-%d", tm.tm_mon); } if (say_day) { - say_num(tm.tm_mday, SSM_COUNTED); + say_num(sh, tm.tm_mday, SSM_COUNTED); } if (say_year) { - say_num(tm.tm_year + 1900, SSM_PRONOUNCED); + say_num(sh, tm.tm_year + 1900, SSM_PRONOUNCED); } if (say_time) { int32_t hour = tm.tm_hour, pm = 0; if (say_date || say_today || say_yesterday || say_dow) { - say_file("time/at.wav"); + switch_say_file(sh, "time/at"); } if (hour > 12) { @@ -370,25 +360,25 @@ static switch_status_t en_say_time(switch_core_session_t *session, char *tosay, pm = 0; } - say_num(hour, SSM_PRONOUNCED); + say_num(sh, hour, SSM_PRONOUNCED); if (tm.tm_min > 9) { - say_num(tm.tm_min, SSM_PRONOUNCED); + say_num(sh, tm.tm_min, SSM_PRONOUNCED); } else if (tm.tm_min) { - say_file("time/oh.wav"); - say_num(tm.tm_min, SSM_PRONOUNCED); + switch_say_file(sh, "time/oh"); + say_num(sh, tm.tm_min, SSM_PRONOUNCED); } else { - say_file("time/oclock.wav"); + switch_say_file(sh, "time/oclock"); } - say_file("time/%s.wav", pm ? "p-m" : "a-m"); + switch_say_file(sh, "time/%s", pm ? "p-m" : "a-m"); } return SWITCH_STATUS_SUCCESS; } -static switch_status_t en_say_money(switch_core_session_t *session, char *tosay, switch_say_args_t *say_args, switch_input_args_t *args) +static switch_status_t en_say_money(switch_say_file_handle_t *sh, char *tosay, switch_say_args_t *say_args) { char sbuf[16] = ""; /* enough for 999,999,999,999.99 (w/o the commas or leading $) */ char *dollars = NULL; @@ -415,43 +405,108 @@ static switch_status_t en_say_money(switch_core_session_t *session, char *tosay, /* If negative say "negative" */ if (sbuf[0] == '-') { - say_file("currency/negative.wav"); + switch_say_file(sh, "currency/negative"); dollars++; } /* Say dollar amount */ - en_say_general_count(session, dollars, say_args, args); + en_say_general_count(sh, dollars, say_args); if (atoi(dollars) == 1) { - say_file("currency/dollar.wav"); + switch_say_file(sh, "currency/dollar"); } else { - say_file("currency/dollars.wav"); + switch_say_file(sh, "currency/dollars"); } /* Say "and" */ - say_file("currency/and.wav"); + switch_say_file(sh, "currency/and"); /* Say cents */ if (cents) { - en_say_general_count(session, cents, say_args, args); + en_say_general_count(sh, cents, say_args); if (atoi(cents) == 1) { - say_file("currency/cent.wav"); + switch_say_file(sh, "currency/cent"); } else { - say_file("currency/cents.wav"); + switch_say_file(sh, "currency/cents"); } } else { - say_file("digits/0.wav"); - say_file("currency/cents.wav"); + switch_say_file(sh, "digits/0"); + switch_say_file(sh, "currency/cents"); } return SWITCH_STATUS_SUCCESS; } - -static switch_status_t en_say(switch_core_session_t *session, char *tosay, switch_say_args_t *say_args, switch_input_args_t *args) +static switch_status_t say_ip(switch_say_file_handle_t *sh, + char *tosay, + switch_say_args_t *say_args) + { + char *a, *b, *c, *d; + switch_status_t status = SWITCH_STATUS_FALSE; + + if (!(a = strdup(tosay))) { + abort(); + } - switch_say_callback_t say_cb = NULL; + if (!(b = strchr(a, '.'))) { + goto end; + } + + *b++ = '\0'; + + if (!(c = strchr(b, '.'))) { + goto end; + } + + *c++ = '\0'; + + if (!(d = strchr(c, '.'))) { + goto end; + } + + *d++ = '\0'; + + say_num(sh, atoi(a), say_args->method); + switch_say_file(sh, "digits/dot"); + say_num(sh, atoi(b), say_args->method); + switch_say_file(sh, "digits/dot"); + say_num(sh, atoi(c), say_args->method); + switch_say_file(sh, "digits/dot"); + say_num(sh, atoi(d), say_args->method); + + end: + + free(a); + + return status; +} + + +static switch_status_t say_spell(switch_say_file_handle_t *sh, char *tosay, switch_say_args_t *say_args) +{ + char *p; + + for (p = tosay; p && *p; p++) { + int a = tolower((int) *p); + if (a >= '0' && a <= '9') { + switch_say_file(sh, "digits/%d", a - '0'); + } else { + if (say_args->type == SST_NAME_SPELLED) { + switch_say_file(sh, "ascii/%d", a); + } else if (say_args->type == SST_NAME_PHONETIC) { + switch_say_file(sh, "phonetic-ascii/%d", a); + } + } + } + + return SWITCH_STATUS_SUCCESS; +} + + +static switch_new_say_callback_t choose_callback(switch_say_args_t *say_args) +{ + switch_new_say_callback_t say_cb = NULL; switch (say_args->type) { case SST_NUMBER: @@ -468,11 +523,11 @@ static switch_status_t en_say(switch_core_session_t *session, char *tosay, switc say_cb = en_say_time; break; case SST_IP_ADDRESS: - return switch_ivr_say_ip(session, tosay, en_say_general_count, say_args, args); + say_cb = say_ip; break; case SST_NAME_SPELLED: case SST_NAME_PHONETIC: - return switch_ivr_say_spell(session, tosay, say_args, args); + say_cb = say_spell; break; case SST_CURRENCY: say_cb = en_say_money; @@ -482,11 +537,77 @@ static switch_status_t en_say(switch_core_session_t *session, char *tosay, switc break; } - if (say_cb) { - return say_cb(session, tosay, say_args, args); + return say_cb; +} + + +static switch_status_t run_callback(switch_new_say_callback_t say_cb, char *tosay, switch_say_args_t *say_args, switch_core_session_t *session, char **rstr) +{ + switch_say_file_handle_t *sh; + switch_status_t status = SWITCH_STATUS_FALSE; + switch_event_t *var_event = NULL; + + if (session) { + switch_channel_t *channel = switch_core_session_get_channel(session); + switch_channel_get_variables(channel, &var_event); } - return SWITCH_STATUS_FALSE; + switch_say_file_handle_create(&sh, say_args->ext, &var_event); + + status = say_cb(sh, tosay, say_args); + + if ((*rstr = switch_say_file_handle_detach_path(sh))) { + status = SWITCH_STATUS_SUCCESS; + } + + switch_say_file_handle_destroy(&sh); + + return status; +} + + +static switch_status_t en_say(switch_core_session_t *session, char *tosay, switch_say_args_t *say_args, switch_input_args_t *args) +{ + + switch_new_say_callback_t say_cb = NULL; + char *string = NULL; + + switch_status_t status = SWITCH_STATUS_FALSE; + + say_cb = choose_callback(say_args); + + if (say_cb) { + status = run_callback(say_cb, tosay, say_args, session, &string); + if (session && string) { + status = switch_ivr_play_file(session, NULL, string, args); + } + + switch_safe_free(string); + } + + return status; +} + + +static switch_status_t en_say_string(switch_core_session_t *session, char *tosay, switch_say_args_t *say_args, char **rstr) +{ + + switch_new_say_callback_t say_cb = NULL; + char *string = NULL; + + switch_status_t status = SWITCH_STATUS_FALSE; + + say_cb = choose_callback(say_args); + + if (say_cb) { + status = run_callback(say_cb, tosay, say_args, session, &string); + if (string) { + status = SWITCH_STATUS_SUCCESS; + *rstr = string; + } + } + + return status; } SWITCH_MODULE_LOAD_FUNCTION(mod_say_en_load) @@ -497,7 +618,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_say_en_load) say_interface = switch_loadable_module_create_interface(*module_interface, SWITCH_SAY_INTERFACE); say_interface->interface_name = "en"; say_interface->say_function = en_say; - + say_interface->say_string_function = en_say_string; + /* indicate that the module should continue to be loaded */ return SWITCH_STATUS_SUCCESS; } diff --git a/src/switch_channel.c b/src/switch_channel.c index 7d246893f7..52542e12d6 100644 --- a/src/switch_channel.c +++ b/src/switch_channel.c @@ -2943,7 +2943,7 @@ SWITCH_DECLARE(char *) switch_channel_expand_variables(switch_channel_t *channel char *data, *indup, *endof_indup; size_t sp = 0, len = 0, olen = 0, vtype = 0, br = 0, cpos, block = 128; char *cloned_sub_val = NULL, *sub_val = NULL; - char *func_val = NULL; + char *func_val = NULL, *sb = NULL; int nv = 0; if (zstr(in)) { @@ -3033,8 +3033,19 @@ SWITCH_DECLARE(char *) switch_channel_expand_variables(switch_channel_t *channel } p = e > endof_indup ? endof_indup : e; - if ((vval = strchr(vname, '(')) || (vval = strchr(vname, ' '))) { - if (*vval == '(') br = 1; + vval = NULL; + for(sb = vname; sb && *sb; sb++) { + if (*sb == ' ') { + vval = sb; + break; + } else if (*sb == '(') { + vval = sb; + br = 1; + break; + } + } + + if (vval) { e = vval - 1; *vval++ = '\0'; while (*e == ' ') { diff --git a/src/switch_event.c b/src/switch_event.c index b7687c2041..e290987248 100644 --- a/src/switch_event.c +++ b/src/switch_event.c @@ -1563,7 +1563,7 @@ SWITCH_DECLARE(char *) switch_event_expand_headers(switch_event_t *event, const char *cloned_sub_val = NULL; char *func_val = NULL; int nv = 0; - char *gvar = NULL; + char *gvar = NULL, *sb = NULL; if (zstr(in)) { return (char *) in; @@ -1651,10 +1651,22 @@ SWITCH_DECLARE(char *) switch_event_expand_headers(switch_event_t *event, const } p = e > endof_indup ? endof_indup : e; - if ((vval = strchr(vname, '(')) || (vval = strchr(vname, ' '))) { - if (*vval == '(') br = 1; + vval = NULL; + for(sb = vname; sb && *sb; sb++) { + if (*sb == ' ') { + vval = sb; + break; + } else if (*sb == '(') { + vval = sb; + br = 1; + break; + } + } + + if (vval) { e = vval - 1; *vval++ = '\0'; + while (*e == ' ') { *e-- = '\0'; } diff --git a/src/switch_ivr.c b/src/switch_ivr.c index 5a082763b2..b6dac4d71d 100644 --- a/src/switch_ivr.c +++ b/src/switch_ivr.c @@ -2432,6 +2432,134 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_say(switch_core_session_t *session, return status; } +SWITCH_DECLARE(switch_status_t) switch_ivr_say_string(switch_core_session_t *session, + const char *lang, + const char *ext, + const char *tosay, + const char *module_name, + const char *say_type, + const char *say_method, + const char *say_gender, + char **rstr) +{ + switch_say_interface_t *si; + switch_channel_t *channel = NULL; + switch_status_t status = SWITCH_STATUS_FALSE; + const char *save_path = NULL, *chan_lang = NULL, *lname = NULL, *sound_path = NULL; + switch_event_t *hint_data; + switch_xml_t cfg, xml = NULL, language, macros; + + if (session) { + channel = switch_core_session_get_channel(session); + + if (!lang) { + lang = switch_channel_get_variable(channel, "language"); + + if (!lang) { + chan_lang = switch_channel_get_variable(channel, "default_language"); + if (!chan_lang) { + chan_lang = "en"; + } + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "No language specified - Using [%s]\n", chan_lang); + } else { + chan_lang = lang; + } + } + } + + if (!lang) lang = "en"; + if (!chan_lang) chan_lang = lang; + + switch_event_create(&hint_data, SWITCH_EVENT_REQUEST_PARAMS); + switch_assert(hint_data); + + switch_event_add_header_string(hint_data, SWITCH_STACK_BOTTOM, "macro_name", "say_app"); + switch_event_add_header_string(hint_data, SWITCH_STACK_BOTTOM, "lang", chan_lang); + + if (channel) { + switch_channel_event_set_data(channel, hint_data); + } + + if (switch_xml_locate("phrases", NULL, NULL, NULL, &xml, &cfg, hint_data, SWITCH_TRUE) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Open of phrases failed.\n"); + goto done; + } + + if (!(macros = switch_xml_child(cfg, "macros"))) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Can't find macros tag.\n"); + goto done; + } + + if (!(language = switch_xml_child(macros, "language"))) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Can't find language tag.\n"); + goto done; + } + + while (language) { + if ((lname = (char *) switch_xml_attr(language, "name")) && !strcasecmp(lname, chan_lang)) { + const char *tmp; + + if ((tmp = switch_xml_attr(language, "module"))) { + module_name = tmp; + } + break; + } + language = language->next; + } + + if (!language) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Can't find language %s.\n", chan_lang); + goto done; + } + + if (!module_name) { + module_name = chan_lang; + } + + if (!(sound_path = (char *) switch_xml_attr(language, "sound-path"))) { + sound_path = (char *) switch_xml_attr(language, "sound_path"); + } + + if (channel) { + save_path = switch_channel_get_variable(channel, "sound_prefix"); + } + + if (sound_path && channel) { + switch_channel_set_variable(channel, "sound_prefix", sound_path); + } + + if ((si = switch_loadable_module_get_say_interface(module_name))) { + /* should go back and proto all the say mods to const.... */ + switch_say_args_t say_args = {0}; + + say_args.type = switch_ivr_get_say_type_by_name(say_type); + say_args.method = switch_ivr_get_say_method_by_name(say_method); + say_args.gender = switch_ivr_get_say_gender_by_name(say_gender); + say_args.ext = ext; + status = si->say_string_function(session, (char *) tosay, &say_args, rstr); + } else { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Invalid SAY Interface [%s]!\n", module_name); + status = SWITCH_STATUS_FALSE; + } + + done: + + if (hint_data) { + switch_event_destroy(&hint_data); + } + + if (save_path && channel) { + switch_channel_set_variable(channel, "sound_prefix", save_path); + } + + if (xml) { + switch_xml_free(xml); + } + + return status; +} + + static const char *get_prefixed_str(char *buffer, size_t buffer_size, const char *prefix, size_t prefix_size, const char *str) { size_t str_len; diff --git a/src/switch_loadable_module.c b/src/switch_loadable_module.c index d6796c8119..ea7bc73684 100644 --- a/src/switch_loadable_module.c +++ b/src/switch_loadable_module.c @@ -1918,6 +1918,97 @@ SWITCH_DECLARE(void *) switch_loadable_module_create_interface(switch_loadable_m } } +struct switch_say_file_handle { + char *ext; + int cnt; + struct switch_stream_handle stream; + switch_event_t *param_event; +}; + +SWITCH_DECLARE(char *) switch_say_file_handle_get_variable(switch_say_file_handle_t *sh, const char *var) +{ + char *ret = NULL; + + if (sh->param_event) { + ret = switch_event_get_header(sh->param_event, var); + } + + return ret; + +} + +SWITCH_DECLARE(char *) switch_say_file_handle_get_path(switch_say_file_handle_t *sh) +{ + return (char *) sh->stream.data; +} + +SWITCH_DECLARE(char *) switch_say_file_handle_detach_path(switch_say_file_handle_t *sh) +{ + char *path; + + switch_assert(sh); + path = (char *) sh->stream.data; + sh->stream.data = NULL; + return path; +} + + +SWITCH_DECLARE(void) switch_say_file_handle_destroy(switch_say_file_handle_t **sh) +{ + switch_assert(sh); + + switch_safe_free((*sh)->stream.data); + switch_safe_free((*sh)->ext); + + if ((*sh)->param_event) { + switch_event_destroy(&(*sh)->param_event); + } + free(*sh); + *sh = NULL; +} + +SWITCH_DECLARE(switch_status_t) switch_say_file_handle_create(switch_say_file_handle_t **sh, const char *ext, switch_event_t **var_event) +{ + switch_assert(sh); + + *sh = malloc(sizeof(**sh)); + memset(*sh, 0, sizeof(**sh)); + + SWITCH_STANDARD_STREAM((*sh)->stream); + + if (var_event) { + (*sh)->param_event = *var_event; + *var_event = NULL; + } + + (*sh)->ext = strdup(ext); + + return SWITCH_STATUS_SUCCESS; +} + +SWITCH_DECLARE(void) switch_say_file(switch_say_file_handle_t *sh, const char *fmt, ...) +{ + char buf[256] = ""; + int ret; + va_list ap; + + va_start(ap, fmt); + + if ((ret = switch_vsnprintf(buf, sizeof(buf), fmt, ap)) > 0) { + if (!sh->cnt++) { + sh->stream.write_function(&sh->stream, "file_string://%s.%s", buf, sh->ext); + } else { + sh->stream.write_function(&sh->stream, "!%s.%s", buf, sh->ext); + } + + } + + va_end(ap); +} + + + + /* For Emacs: * Local Variables: * mode:c