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