static const char modname[] = "mod_spidermonkey";
+static int eval_some_js(char *code, JSContext *cx, JSObject *obj, jsval *rval);
static struct {
size_t gStackChunkSize;
};
-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;
};
}
-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);
+ }
+
+ 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 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) {
- 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) );
+ file_name = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));
+ if (switch_strlen_zero(file_name)) {
+ return JS_FALSE;
}
}
- 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) {
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;
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;
}
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);
};
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}
};
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));
}
-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
};
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];
}
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);
}
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;
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 {
*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);
}
}
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,
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) {
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;
}
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 */
ilen = samples;
while (switch_channel_get_state(channel) == CS_EXECUTE) {
int done = 0;
-
- if (dtmf_callback || buf) {
+ 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.
}
}
- 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");
}
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);