diff --git a/src/include/switch_buffer.h b/src/include/switch_buffer.h index 6461e1bc1d..fd0366ef16 100644 --- a/src/include/switch_buffer.h +++ b/src/include/switch_buffer.h @@ -104,6 +104,11 @@ SWITCH_DECLARE(int) switch_buffer_write(switch_buffer *buffer, void *data, size_ * \return int size of buffer, or 0 if unable to toss that much data */ SWITCH_DECLARE(int) switch_buffer_toss(switch_buffer *buffer, size_t datalen); + +/*! \brief Remove all data from the buffer + * \param buffer any buffer of type switch_buffer + */ +SWITCH_DECLARE(void) switch_buffer_zero(switch_buffer *buffer); /** @} */ diff --git a/src/include/switch_core.h b/src/include/switch_core.h index fe54a0c527..f3c6f4cdf5 100644 --- a/src/include/switch_core.h +++ b/src/include/switch_core.h @@ -743,7 +743,7 @@ SWITCH_DECLARE(switch_status) switch_core_file_write(switch_file_handle *fh, voi \param whence the indicator (see traditional seek) \return SWITCH_STATUS_SUCCESS with cur_pos adjusted to new position */ -SWITCH_DECLARE(switch_status) switch_core_file_seek(switch_file_handle *fh, unsigned int *cur_pos, unsigned int samples, int whence); +SWITCH_DECLARE(switch_status) switch_core_file_seek(switch_file_handle *fh, unsigned int *cur_pos, int64_t samples, int whence); /*! \brief Close an open file handle diff --git a/src/include/switch_ivr.h b/src/include/switch_ivr.h index 8a48ae1c31..c8409dd836 100644 --- a/src/include/switch_ivr.h +++ b/src/include/switch_ivr.h @@ -85,6 +85,7 @@ SWITCH_DECLARE(switch_status) switch_ivr_collect_digits_count(switch_core_sessio /*! \brief play a file from the disk to the session \param session the session to play the file too + \param pointer to file handle to use (NULL for builtin one) \param file the path to the file \param timer_name the name of a timer to use input will be absorbed (NULL to time off the session input). \param dtmf_callback code to execute if any dtmf is dialed during the playback @@ -94,6 +95,7 @@ SWITCH_DECLARE(switch_status) switch_ivr_collect_digits_count(switch_core_sessio \note passing a NULL dtmf_callback nad a not NULL buf indicates to copy any dtmf to buf and stop playback. */ SWITCH_DECLARE(switch_status) switch_ivr_play_file(switch_core_session *session, + switch_file_handle *fh, char *file, char *timer_name, switch_dtmf_callback_function dtmf_callback, diff --git a/src/include/switch_module_interfaces.h b/src/include/switch_module_interfaces.h index 3f42b4d3eb..39712cd0bd 100644 --- a/src/include/switch_module_interfaces.h +++ b/src/include/switch_module_interfaces.h @@ -245,7 +245,7 @@ struct switch_file_interface { /*! function to write from the file */ switch_status (*file_write)(switch_file_handle *, void *data, size_t *len); /*! function to seek to a certian position in the file */ - switch_status (*file_seek)(switch_file_handle *, unsigned int *cur_pos, unsigned int samples, int whence); + switch_status (*file_seek)(switch_file_handle *, unsigned int *cur_pos, int64_t samples, int whence); /*! list of supported file extensions */ char **extens; const struct switch_file_interface *next; @@ -273,10 +273,14 @@ struct switch_file_handle { int seekable; /*! the sample count of the file */ unsigned int sample_count; + /*! the speed of the file playback*/ + int speed; /*! the handle's memory pool */ switch_memory_pool *memory_pool; /*! private data for the format module to store handle specific info */ void *private_info; + int64_t pos; + switch_buffer *audio_buffer; }; diff --git a/src/include/switch_types.h b/src/include/switch_types.h index 524bb0baf6..be1a4eb4be 100644 --- a/src/include/switch_types.h +++ b/src/include/switch_types.h @@ -319,6 +319,7 @@ SWITCH_FILE_DATA_INT = (1 << 4) - Read data in ints SWITCH_FILE_DATA_FLOAT = (1 << 5) - Read data in floats SWITCH_FILE_DATA_DOUBLE = (1 << 6) - Read data in doubles SWITCH_FILE_DATA_RAW = (1 << 7) - Read data as is +SWITCH_FILE_PAUSE = (1 << 8) - Pause */ typedef enum { @@ -330,6 +331,7 @@ typedef enum { SWITCH_FILE_DATA_FLOAT = (1 << 5), SWITCH_FILE_DATA_DOUBLE = (1 << 6), SWITCH_FILE_DATA_RAW = (1 << 7), + SWITCH_FILE_PAUSE = (1 << 8) } switch_file_flag; typedef enum { diff --git a/src/mod/applications/mod_ivrtest/mod_ivrtest.c b/src/mod/applications/mod_ivrtest/mod_ivrtest.c index e44cc167d0..f617b1d9b0 100644 --- a/src/mod/applications/mod_ivrtest/mod_ivrtest.c +++ b/src/mod/applications/mod_ivrtest/mod_ivrtest.c @@ -149,7 +149,7 @@ static void ivrtest_function(switch_core_session *session, char *data) /* you could have passed NULL instead of on_dtmf to get this exact behaviour (copy the digits to buf and stop playing) but you may want to pass the function if you have something cooler to do... */ - status = switch_ivr_play_file(session, data, NULL, on_dtmf, buf, sizeof(buf)); + status = switch_ivr_play_file(session, NULL, data, NULL, on_dtmf, buf, sizeof(buf)); if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_BREAK) { switch_channel_hangup(channel); diff --git a/src/mod/applications/mod_playback/mod_playback.c b/src/mod/applications/mod_playback/mod_playback.c index d56da492be..e7c35de991 100644 --- a/src/mod/applications/mod_playback/mod_playback.c +++ b/src/mod/applications/mod_playback/mod_playback.c @@ -64,7 +64,7 @@ static void playback_function(switch_core_session *session, char *data) channel = switch_core_session_get_channel(session); assert(channel != NULL); - if (switch_ivr_play_file(session, file_name, timer_name, on_dtmf, NULL, 0) != SWITCH_STATUS_SUCCESS) { + if (switch_ivr_play_file(session, NULL, file_name, timer_name, on_dtmf, NULL, 0) != SWITCH_STATUS_SUCCESS) { switch_channel_hangup(channel); } diff --git a/src/mod/endpoints/mod_exosip/mod_exosip.c b/src/mod/endpoints/mod_exosip/mod_exosip.c index 6dbe77b9dc..43b0955830 100644 --- a/src/mod/endpoints/mod_exosip/mod_exosip.c +++ b/src/mod/endpoints/mod_exosip/mod_exosip.c @@ -117,6 +117,8 @@ struct private_object { char call_id[50]; int ssrc; char last_digit; + unsigned int dc; + time_t last_digit_time; switch_mutex_t *rtp_lock; switch_queue_t *dtmf_queue; char out_digit; @@ -480,6 +482,8 @@ static switch_status exosip_answer_channel(switch_core_session *session) struct private_object *tech_pvt; switch_channel *channel = NULL; + assert(session != NULL); + channel = switch_core_session_get_channel(session); assert(channel != NULL); @@ -554,11 +558,24 @@ static switch_status exosip_read_frame(switch_core_session *session, switch_fram int duration = (packet[2]<<8) + packet[3]; char key = switch_rfc2833_to_char(packet[0]); - if (duration && end && key != tech_pvt->last_digit) { - char digit_str[] = {key, 0}; - switch_channel_queue_dtmf(channel, digit_str); - tech_pvt->last_digit = key; + /* SHEESH.... Curse you RFC2833 inventors!!!!*/ + if ((time(NULL) - tech_pvt->last_digit_time) > 2) { + tech_pvt->last_digit = 0; + tech_pvt->dc = 0; } + if (duration && end) { + if (key != tech_pvt->last_digit) { + char digit_str[] = {key, 0}; + time(&tech_pvt->last_digit_time); + switch_channel_queue_dtmf(channel, digit_str); + } + if (++tech_pvt->dc >= 3) { + tech_pvt->last_digit = 0; + tech_pvt->dc = 0; + } else { + tech_pvt->last_digit = key; + } + } } if (globals.supress_telephony_events && payload != tech_pvt->payload_num) { diff --git a/src/mod/formats/mod_sndfile/mod_sndfile.c b/src/mod/formats/mod_sndfile/mod_sndfile.c index 27d8255957..0d1b1dc7de 100644 --- a/src/mod/formats/mod_sndfile/mod_sndfile.c +++ b/src/mod/formats/mod_sndfile/mod_sndfile.c @@ -138,7 +138,7 @@ switch_status sndfile_file_open(switch_file_handle *handle, char *path) handle->format = context->sfinfo.format; handle->sections = context->sfinfo.sections; handle->seekable = context->sfinfo.seekable; - + handle->speed = 0; handle->private_info = context; return SWITCH_STATUS_SUCCESS; @@ -153,15 +153,17 @@ switch_status sndfile_file_close(switch_file_handle *handle) return SWITCH_STATUS_SUCCESS; } -switch_status sndfile_file_seek(switch_file_handle *handle, unsigned int *cur_sample, unsigned int samples, int whence) +switch_status sndfile_file_seek(switch_file_handle *handle, unsigned int *cur_sample, int64_t samples, int whence) { sndfile_context *context = handle->private_info; if (!handle->seekable) { + switch_console_printf(SWITCH_CHANNEL_CONSOLE, "File is not seekable\n"); return SWITCH_STATUS_NOTIMPL; } *cur_sample = (unsigned int) sf_seek(context->handle, samples, whence); + handle->pos = *cur_sample; return SWITCH_STATUS_SUCCESS; diff --git a/src/mod/languages/mod_spidermonkey/mod_spidermonkey.c b/src/mod/languages/mod_spidermonkey/mod_spidermonkey.c index a21867c9b5..7cd4534f95 100644 --- a/src/mod/languages/mod_spidermonkey/mod_spidermonkey.c +++ b/src/mod/languages/mod_spidermonkey/mod_spidermonkey.c @@ -53,6 +53,7 @@ static const char modname[] = "mod_spidermonkey"; +static int eval_some_js(char *code, JSContext *cx, JSObject *obj, jsval *rval); static struct { size_t gStackChunkSize; @@ -74,8 +75,20 @@ static JSClass global_class = { }; -struct jchan { +struct dtmf_callback_state { + struct js_session *session_state; + char code_buffer[1024]; + int code_buffer_len; + char ret_buffer[1024]; + int ret_buffer_len; + int digit_count; + void *extra; +}; + +struct js_session { switch_core_session *session; + JSContext *cx; + JSObject *obj; unsigned int flags; }; @@ -110,33 +123,177 @@ static switch_status init_js(void) } -static JSBool chan_streamfile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +static switch_status js_dtmf_callback(switch_core_session *session, char *dtmf, void *buf, unsigned int buflen) { - struct jchan *jc = JS_GetPrivate(cx, obj); - JSString *str = NULL; - char *filename = NULL; - char buf[25] = ""; + char code[2048]; + struct dtmf_callback_state *cb_state = buf; + struct js_session *jss = cb_state->session_state; + switch_file_handle *fh = cb_state->extra; + jsval rval; + char *ret; + + if(!jss) { + return SWITCH_STATUS_FALSE; + } + + if (cb_state->digit_count || (cb_state->code_buffer[0] > 47 && cb_state->code_buffer[0] < 58)) { + char *d; + if (!cb_state->digit_count) { + cb_state->digit_count = atoi(cb_state->code_buffer); + } - if (argc > 0) { - if ((str = JS_ValueToString(cx, argv[0])) && (filename = JS_GetStringBytes(str))) { - switch_ivr_play_file(jc->session, filename, NULL, NULL, buf, sizeof(buf)); - *rval = STRING_TO_JSVAL ( JS_NewStringCopyZ(cx, buf) ); + for(d = dtmf; *d; d++) { + cb_state->ret_buffer[cb_state->ret_buffer_len++] = *d; + if ((cb_state->ret_buffer_len > cb_state->digit_count)|| + (cb_state->ret_buffer_len > sizeof(cb_state->ret_buffer))|| + (cb_state->ret_buffer_len >= cb_state->digit_count) + ) { + return SWITCH_STATUS_FALSE; + } + } + return SWITCH_STATUS_SUCCESS; + } else { + snprintf(code, sizeof(code), "~%s(\"%s\")", cb_state->code_buffer, dtmf); + eval_some_js(code, jss->cx, jss->obj, &rval); + ret = JS_GetStringBytes(JS_ValueToString(jss->cx, rval)); + + if (*ret == 'F') { + int step; + ret++; + + step = *ret ? atoi(ret) : 1; + fh->speed += step ? step : 1; + return SWITCH_STATUS_SUCCESS; + } + + if (*ret == 'S') { + int step; + ret++; + + step = *ret ? atoi(ret) : 1; + fh->speed -= step ? step : 1; + return SWITCH_STATUS_SUCCESS; + } + + if (*ret == 'N') { + fh->speed = 0; + return SWITCH_STATUS_SUCCESS; + } + + if (*ret == 'P') { + if (switch_test_flag(fh, SWITCH_FILE_PAUSE)) { + printf("unpause\n"); + switch_clear_flag(fh, SWITCH_FILE_PAUSE); + } else { + printf("pause\n"); + switch_set_flag(fh, SWITCH_FILE_PAUSE); + fh->speed = 0; + } + return SWITCH_STATUS_SUCCESS; + } + + if (*ret == 'R') { + unsigned int pos = 0; + fh->speed = 0; + switch_core_file_seek(fh, &pos, 0, SEEK_SET); + return SWITCH_STATUS_SUCCESS; + } + + if (*ret == '+' || *ret == '-') { + switch_codec *codec; + codec = switch_core_session_get_read_codec(jss->session); + unsigned int samps = 0; + unsigned int pos = 0; + if (*ret == '+') { + ret++; + samps = atoi(ret) * (codec->implementation->samples_per_second / 1000); + switch_core_file_seek(fh, &pos, samps, SEEK_CUR); + } else { + samps = atoi(ret) * (codec->implementation->samples_per_second / 1000); + switch_core_file_seek(fh, &pos, fh->pos - samps, SEEK_SET); + } + + return SWITCH_STATUS_SUCCESS; + } + + if (!strcmp(ret, "true") || !strcmp(ret, "undefined")) { + return SWITCH_STATUS_SUCCESS; + } + + if (ret) { + switch_copy_string(cb_state->ret_buffer, ret, sizeof(cb_state->ret_buffer)); } } - return JS_FALSE; + + return SWITCH_STATUS_FALSE; +} + +static JSBool chan_streamfile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) +{ + struct js_session *jss = JS_GetPrivate(cx, obj); + switch_channel *channel; + char *file_name = NULL; + char *timer_name = NULL; + char *dtmf_callback = NULL; + void *bp = NULL; + int len = 0; + switch_dtmf_callback_function dtmf_func = NULL; + struct dtmf_callback_state cb_state = {0}; + switch_file_handle fh; + + channel = switch_core_session_get_channel(jss->session); + assert(channel != NULL); + + if (argc > 0) { + file_name = JS_GetStringBytes(JS_ValueToString(cx, argv[0])); + if (switch_strlen_zero(file_name)) { + return JS_FALSE; + } + } + if (argc > 1) { + timer_name = JS_GetStringBytes(JS_ValueToString(cx, argv[1])); + if (switch_strlen_zero(timer_name)) { + timer_name = NULL; + } + } + if (argc > 2) { + dtmf_callback = JS_GetStringBytes(JS_ValueToString(cx, argv[2])); + if (switch_strlen_zero(dtmf_callback)) { + dtmf_callback = NULL; + } else { + memset(&cb_state, 0, sizeof(cb_state)); + switch_copy_string(cb_state.code_buffer, dtmf_callback, sizeof(cb_state.code_buffer)); + cb_state.code_buffer_len = strlen(cb_state.code_buffer); + cb_state.session_state = jss; + dtmf_func = js_dtmf_callback; + bp = &cb_state; + len = sizeof(cb_state); + } + } + + memset(&fh, 0, sizeof(fh)); + cb_state.extra = &fh; + + switch_ivr_play_file(jss->session, &fh, file_name, timer_name, dtmf_func, bp, len); + *rval = STRING_TO_JSVAL (JS_NewStringCopyZ(cx, cb_state.ret_buffer)); + + return (switch_channel_get_state(channel) == CS_EXECUTE) ? JS_TRUE : JS_FALSE; } static JSBool chan_speak(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - struct jchan *jc = JS_GetPrivate(cx, obj); + struct js_session *jss = JS_GetPrivate(cx, obj); switch_channel *channel; char *tts_name = NULL; char *voice_name = NULL; char *text = NULL; switch_codec *codec; char buf[10] = ""; + void *bp = NULL; + int len = 0; + //switch_dtmf_callback_function dtmf_func = NULL; - channel = switch_core_session_get_channel(jc->session); + channel = switch_core_session_get_channel(jss->session); assert(channel != NULL); if (argc > 0) { @@ -148,30 +305,35 @@ static JSBool chan_speak(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, if (argc > 2) { text = JS_GetStringBytes(JS_ValueToString(cx, argv[2])); } + if (argc > 3) { + bp = buf; + len = sizeof(buf); + } if (!tts_name && text) { return JS_FALSE; } - codec = switch_core_session_get_read_codec(jc->session); - switch_ivr_speak_text(jc->session, + codec = switch_core_session_get_read_codec(jss->session); + switch_ivr_speak_text(jss->session, tts_name, voice_name && strlen(voice_name) ? voice_name : NULL, NULL, codec->implementation->samples_per_second, NULL, text, - buf, - sizeof(buf)); - *rval = STRING_TO_JSVAL ( JS_NewStringCopyZ(cx, buf) ); - + bp, + len); + if(len) { + *rval = STRING_TO_JSVAL ( JS_NewStringCopyZ(cx, buf) ); + } return JS_TRUE; } static JSBool chan_get_digits(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - struct jchan *jc = JS_GetPrivate(cx, obj); + struct js_session *jss = JS_GetPrivate(cx, obj); char *terminators = NULL; char *buf; int digits; @@ -182,8 +344,8 @@ static JSBool chan_get_digits(JSContext *cx, JSObject *obj, uintN argc, jsval *a if (argc > 1) { terminators = JS_GetStringBytes(JS_ValueToString(cx, argv[1])); } - buf = switch_core_session_alloc(jc->session, digits); - switch_ivr_collect_digits_count(jc->session, buf, digits, digits, terminators, &term); + buf = switch_core_session_alloc(jss->session, digits); + switch_ivr_collect_digits_count(jss->session, buf, digits, digits, terminators, &term); *rval = STRING_TO_JSVAL ( JS_NewStringCopyZ(cx, buf) ); return JS_TRUE; } @@ -193,10 +355,10 @@ static JSBool chan_get_digits(JSContext *cx, JSObject *obj, uintN argc, jsval *a static JSBool chan_answer(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { - struct jchan *jc = JS_GetPrivate(cx, obj); + struct js_session *jss = JS_GetPrivate(cx, obj); switch_channel *channel; - channel = switch_core_session_get_channel(jc->session); + channel = switch_core_session_get_channel(jss->session); assert(channel != NULL); switch_channel_answer(channel); @@ -208,10 +370,10 @@ enum its_tinyid { }; static JSFunctionSpec chan_methods[] = { - {"StreamFile", chan_streamfile, 1}, - {"Speak", chan_speak, 1}, - {"GetDigits", chan_get_digits, 1}, - {"Answer", chan_answer, 0}, + {"streamFile", chan_streamfile, 1}, + {"speak", chan_speak, 1}, + {"getDigits", chan_get_digits, 1}, + {"answer", chan_answer, 0}, {0} }; @@ -224,13 +386,13 @@ static JSPropertySpec chan_props[] = { static JSBool chan_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { - struct jchan *jc = JS_GetPrivate(cx, obj); + struct js_session *jss = JS_GetPrivate(cx, obj); int param = 0; JSBool res = JS_TRUE; switch_channel *channel; char *name; - channel = switch_core_session_get_channel(jc->session); + channel = switch_core_session_get_channel(jss->session); assert(channel != NULL); name = JS_GetStringBytes(JS_ValueToString(cx, id)); @@ -258,8 +420,8 @@ static JSBool chan_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp } -JSClass chan_class = { - "Chan", JSCLASS_HAS_PRIVATE, +JSClass session_class = { + "Session", JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, chan_getProperty, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub }; @@ -279,28 +441,32 @@ static JSBool js_log(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsva static JSFunctionSpec fs_functions[] = { - {"Log", js_log, 2}, + {"console_log", js_log, 2}, {0} }; -static JSObject *new_jchan(JSContext *cx, JSObject *obj, switch_core_session *session, struct jchan *jc, int flags) { - JSObject *Chan; - if ((Chan = JS_DefineObject(cx, obj, "Chan", &chan_class, NULL, 0))) { - memset(jc, 0, sizeof(struct jchan)); - jc->session = session; - jc->flags = flags; - if ((JS_SetPrivate(cx, Chan, jc) && - JS_DefineProperties(cx, Chan, chan_props) && - JS_DefineFunctions(cx, Chan, chan_methods))) { - return Chan; +static JSObject *new_js_session(JSContext *cx, JSObject *obj, switch_core_session *session, struct js_session *jss, char *name, int flags) +{ + JSObject *session_obj; + if ((session_obj = JS_DefineObject(cx, obj, name, &session_class, NULL, 0))) { + memset(jss, 0, sizeof(struct js_session)); + jss->session = session; + jss->flags = flags; + jss->cx = cx; + jss->obj = obj; + if ((JS_SetPrivate(cx, session_obj, jss) && + JS_DefineProperties(cx, session_obj, chan_props) && + JS_DefineFunctions(cx, session_obj, chan_methods))) { + return session_obj; } } return NULL; } -static int eval_some_js(char *code, JSContext *cx, JSObject *obj, jsval *rval) { +static int eval_some_js(char *code, JSContext *cx, JSObject *obj, jsval *rval) +{ JSScript *script; char *cptr; char path[512]; @@ -321,7 +487,7 @@ static int eval_some_js(char *code, JSContext *cx, JSObject *obj, jsval *rval) { } if (script) { - res = JS_ExecuteScript(cx, obj, script, rval) == JS_TRUE ? 0 : -1; + res = JS_ExecuteScript(cx, obj, script, rval) == JS_TRUE ? 1 : 0; JS_DestroyScript(cx, script); } @@ -334,8 +500,8 @@ static void js_exec(switch_core_session *session, char *data) int res=-1; jsval rval; JSContext *cx; - JSObject *glob, *Chan; - struct jchan jc; + JSObject *javascript_global_object, *session_obj; + struct js_session jss; int x = 0, y = 0; char buf[512]; int flags = 0; @@ -366,12 +532,12 @@ static void js_exec(switch_core_session *session, char *data) if ((cx = JS_NewContext(globals.rt, globals.gStackChunkSize))) { JS_SetErrorReporter(cx, js_error); - if ((glob = JS_NewObject(cx, &global_class, NULL, NULL)) && - JS_DefineFunctions(cx, glob, fs_functions) && - JS_InitStandardClasses(cx, glob) && - (Chan = new_jchan(cx, glob, session, &jc, flags))) { - JS_SetGlobalObject(cx, glob); - JS_SetPrivate(cx, glob, session); + if ((javascript_global_object = JS_NewObject(cx, &global_class, NULL, NULL)) && + JS_DefineFunctions(cx, javascript_global_object, fs_functions) && + JS_InitStandardClasses(cx, javascript_global_object) && + (session_obj = new_js_session(cx, javascript_global_object, session, &jss, "session", flags))) { + JS_SetGlobalObject(cx, javascript_global_object); + JS_SetPrivate(cx, javascript_global_object, session); res = 0; do { @@ -386,22 +552,22 @@ static void js_exec(switch_core_session *session, char *data) *arg = '\0'; arg++; snprintf(buf, sizeof(buf), "~var Argv = new Array(%d);", y); - eval_some_js(buf, cx, glob, &rval); + eval_some_js(buf, cx, javascript_global_object, &rval); snprintf(buf, sizeof(buf), "~var argc = %d", y); - eval_some_js(buf, cx, glob, &rval); + eval_some_js(buf, cx, javascript_global_object, &rval); do { if ((nextarg = strchr(arg, ':'))) { *nextarg = '\0'; nextarg++; } snprintf(buf, sizeof(buf), "~Argv[%d] = \"%s\";", x++, arg); - eval_some_js(buf, cx, glob, &rval); + eval_some_js(buf, cx, javascript_global_object, &rval); arg = nextarg; } while (arg); } - if ((res=eval_some_js(code, cx, glob, &rval)) < 0) { + if (!(res=eval_some_js(code, cx, javascript_global_object, &rval))) { break; - } + } code = next; } while (code); } diff --git a/src/switch_buffer.c b/src/switch_buffer.c index 27272661cb..b63c7e7f0c 100644 --- a/src/switch_buffer.c +++ b/src/switch_buffer.c @@ -138,3 +138,12 @@ SWITCH_DECLARE(int) switch_buffer_write(switch_buffer *buffer, void *data, size_ //printf("i %d = %d\n", datalen, buffer->used); return (int) buffer->used; } + +SWITCH_DECLARE(void) switch_buffer_zero(switch_buffer *buffer) +{ + assert(buffer != NULL); + assert(buffer->data != NULL); + + buffer->used = 0; + +} diff --git a/src/switch_core.c b/src/switch_core.c index c049509772..72e50e68ce 100644 --- a/src/switch_core.c +++ b/src/switch_core.c @@ -416,7 +416,7 @@ SWITCH_DECLARE(switch_status) switch_core_file_write(switch_file_handle *fh, voi return fh->file_interface->file_write(fh, data, len); } -SWITCH_DECLARE(switch_status) switch_core_file_seek(switch_file_handle *fh, unsigned int *cur_pos, unsigned int samples, +SWITCH_DECLARE(switch_status) switch_core_file_seek(switch_file_handle *fh, unsigned int *cur_pos, int64_t samples, int whence) { return fh->file_interface->file_seek(fh, cur_pos, samples, whence); diff --git a/src/switch_ivr.c b/src/switch_ivr.c index 52e753d273..6758716cd7 100644 --- a/src/switch_ivr.c +++ b/src/switch_ivr.c @@ -220,6 +220,7 @@ SWITCH_DECLARE(switch_status) switch_ivr_record_file(switch_core_session *sessio } SWITCH_DECLARE(switch_status) switch_ivr_play_file(switch_core_session *session, + switch_file_handle *fh, char *file, char *timer_name, switch_dtmf_callback_function dtmf_callback, @@ -230,24 +231,27 @@ SWITCH_DECLARE(switch_status) switch_ivr_play_file(switch_core_session *session, short abuf[960]; char dtmf[128]; int interval = 0, samples = 0; - size_t len = 0, ilen = 0; + size_t len = 0, ilen = 0, olen = 0; switch_frame write_frame; switch_timer timer; switch_core_thread_session thread_session; switch_codec codec; switch_memory_pool *pool = switch_core_session_get_pool(session); - switch_file_handle fh; char *codec_name; int x; int stream_id; switch_status status = SWITCH_STATUS_SUCCESS; - - memset(&fh, 0, sizeof(fh)); + switch_file_handle lfh; + + if (!fh) { + fh = &lfh; + memset(fh, 0, sizeof(lfh)); + } channel = switch_core_session_get_channel(session); assert(channel != NULL); - if (switch_core_file_open(&fh, + if (switch_core_file_open(fh, file, SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT, switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) { @@ -261,27 +265,27 @@ SWITCH_DECLARE(switch_status) switch_ivr_play_file(switch_core_session *session, write_frame.buflen = sizeof(abuf); - switch_console_printf(SWITCH_CHANNEL_CONSOLE, "OPEN FILE %s %dhz %d channels\n", file, fh.samplerate, fh.channels); + switch_console_printf(SWITCH_CHANNEL_CONSOLE, "OPEN FILE %s %dhz %d channels\n", file, fh->samplerate, fh->channels); interval = 20; - samples = (fh.samplerate / 50) * fh.channels; + samples = (fh->samplerate / 50) * fh->channels; len = samples * 2; codec_name = "L16"; if (switch_core_codec_init(&codec, codec_name, - fh.samplerate, + fh->samplerate, interval, - fh.channels, + fh->channels, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, pool) == SWITCH_STATUS_SUCCESS) { switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Raw Codec Activated\n"); write_frame.codec = &codec; } else { switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Raw Codec Activation Failed %s@%dhz %d channels %dms\n", - codec_name, fh.samplerate, fh.channels, interval); - switch_core_file_close(&fh); + codec_name, fh->samplerate, fh->channels, interval); + switch_core_file_close(fh); return SWITCH_STATUS_GENERR; } @@ -289,12 +293,12 @@ SWITCH_DECLARE(switch_status) switch_ivr_play_file(switch_core_session *session, if (switch_core_timer_init(&timer, timer_name, interval, samples, pool) != SWITCH_STATUS_SUCCESS) { switch_console_printf(SWITCH_CHANNEL_CONSOLE, "setup timer failed!\n"); switch_core_codec_destroy(&codec); - switch_core_file_close(&fh); + switch_core_file_close(fh); return SWITCH_STATUS_GENERR; } switch_console_printf(SWITCH_CHANNEL_CONSOLE, "setup timer success %d bytes per %d ms!\n", len, interval); } - write_frame.rate = fh.samplerate; + write_frame.rate = fh->samplerate; if (timer_name) { /* start a thread to absorb incoming audio */ @@ -306,10 +310,11 @@ SWITCH_DECLARE(switch_status) switch_ivr_play_file(switch_core_session *session, ilen = samples; while (switch_channel_get_state(channel) == CS_EXECUTE) { int done = 0; + int do_speed = 1; + int last_speed = -1; + if (dtmf_callback || buf) { - - /* dtmf handler function you can hook up to be executed when a digit is dialed during playback if you return anything but SWITCH_STATUS_SUCCESS the playback will stop. @@ -330,18 +335,77 @@ SWITCH_DECLARE(switch_status) switch_ivr_play_file(switch_core_session *session, } } - switch_core_file_read(&fh, abuf, &ilen); + if (switch_test_flag(fh, SWITCH_FILE_PAUSE)) { + memset(abuf, 0, ilen * 2); + } else if (fh->audio_buffer && (switch_buffer_inuse(fh->audio_buffer) > (ilen * 2))) { + switch_buffer_read(fh->audio_buffer, abuf, ilen * 2); + olen = ilen; + do_speed = 0; + } else { + olen = ilen; + switch_core_file_read(fh, abuf, &olen); + } - if (done || ilen <= 0) { + if (done || olen <= 0) { break; } - write_frame.datalen = ilen * 2; - write_frame.samples = (int) ilen; -#ifdef SWAP_LINEAR + if (fh->speed > 2) { + fh->speed = 2; + } else if(fh->speed < -2) { + fh->speed = -2; + } + + if (fh->audio_buffer && last_speed > -1 && last_speed != fh->speed) { + switch_buffer_zero(fh->audio_buffer); + } + + + if (fh->speed && do_speed) { + float factor = 0.25 * abs(fh->speed); + unsigned int newlen, supplement, step; + short *bp = write_frame.data; + int wrote = 0; + + if (!fh->audio_buffer) { + switch_buffer_create(fh->memory_pool, &fh->audio_buffer, SWITCH_RECCOMMENDED_BUFFER_SIZE); + } + + supplement = (int) (factor * olen); + newlen = (fh->speed > 0) ? olen - supplement : olen + supplement; + step = (fh->speed > 0) ? (newlen / supplement) : (olen / supplement); + + while ((wrote + step) < newlen) { + switch_buffer_write(fh->audio_buffer, bp, step * 2); + wrote += step; + bp += step; + if (fh->speed > 0) { + bp++; + } else { + float f; + short s; + f = *bp + *(bp+1) + *(bp-1); + f /= 3; + s = (short) f; + switch_buffer_write(fh->audio_buffer, &s, 2); + wrote++; + } + } + if (wrote < newlen) { + unsigned int r = newlen - wrote; + switch_buffer_write(fh->audio_buffer, bp, r*2); + wrote += r; + } + last_speed = fh->speed; + continue; + } + + write_frame.datalen = olen * 2; + write_frame.samples = (int) olen; +#if __BYTE_ORDER == __BIG_ENDIAN switch_swap_linear(write_frame.data, (int) write_frame.datalen / 2); #endif - + for (stream_id = 0; stream_id < switch_core_session_get_stream_count(session); stream_id++) { if (switch_core_session_write_frame(session, &write_frame, -1, stream_id) != SWITCH_STATUS_SUCCESS) { switch_console_printf(SWITCH_CHANNEL_CONSOLE, "Bad Write\n"); @@ -366,7 +430,7 @@ SWITCH_DECLARE(switch_status) switch_ivr_play_file(switch_core_session *session, } switch_console_printf(SWITCH_CHANNEL_CONSOLE, "done playing file\n"); - switch_core_file_close(&fh); + switch_core_file_close(fh); switch_core_codec_destroy(&codec);