/* * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application * Copyright (C) 2005/2006, Anthony Minessale II * * Version: MPL 1.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application * * The Initial Developer of the Original Code is * Anthony Minessale II * Portions created by the Initial Developer are Copyright (C) * the Initial Developer. All Rights Reserved. * * Contributor(s): * * Anthony Minessale II * * * mod_spidermonkey_teletone.c -- TeleTone Javascript Module * */ #include "mod_spidermonkey.h" static const char modname[] = "TeleTone"; typedef enum { TTF_DTMF = (1 << 0) } teletone_flag_t; struct teletone_obj { teletone_generation_session_t ts; JSContext *cx; JSObject *obj; switch_core_session_t *session; switch_codec_t codec; switch_buffer_t *audio_buffer; switch_buffer_t *loop_buffer; switch_memory_pool_t *pool; switch_timer_t *timer; switch_timer_t timer_base; JSFunction *function; jsval arg; jsval ret; unsigned int flags; }; static int teletone_handler(teletone_generation_session_t *ts, teletone_tone_map_t *map) { struct teletone_obj *tto = ts->user_data; int wrote; if (!tto) { return -1; } wrote = teletone_mux_tones(ts, map); switch_buffer_write(tto->audio_buffer, ts->buffer, wrote * 2); return 0; } /* TeleTone Object */ /*********************************************************************************/ static JSBool teletone_construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSObject *session_obj; struct teletone_obj *tto = NULL; struct js_session *jss = NULL; switch_codec_t *read_codec; switch_memory_pool_t *pool; char *timer_name = NULL; if (argc > 0) { if (JS_ValueToObject(cx, argv[0], &session_obj)) { if (!(jss = JS_GetPrivate(cx, session_obj))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot Find Session [1]\n"); return JS_FALSE; } } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot Find Session [2]\n"); return JS_FALSE; } } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing Session Arg\n"); return JS_FALSE; } if (argc > 1) { timer_name = JS_GetStringBytes(JS_ValueToString(cx, argv[1])); } switch_core_new_memory_pool(&pool); if (!(tto = switch_core_alloc(pool, sizeof(*tto)))) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memory Error\n"); return JS_FALSE; } read_codec = switch_core_session_get_read_codec(jss->session); if (switch_core_codec_init(&tto->codec, "L16", NULL, read_codec->implementation->samples_per_second, read_codec->implementation->microseconds_per_frame / 1000, read_codec->implementation->number_of_channels, SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, pool) == SWITCH_STATUS_SUCCESS) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Raw Codec Activated\n"); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Raw Codec Activation Failed\n"); return JS_FALSE; } if (timer_name) { unsigned int ms = read_codec->implementation->microseconds_per_frame / 1000; if (switch_core_timer_init(&tto->timer_base, timer_name, ms, (read_codec->implementation->samples_per_second / 50) * read_codec->implementation->number_of_channels, pool) == SWITCH_STATUS_SUCCESS) { tto->timer = &tto->timer_base; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Timer INIT Success %u\n", ms); } else { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Timer INIT Failed\n"); } } switch_buffer_create_dynamic(&tto->audio_buffer, JS_BLOCK_SIZE, JS_BUFFER_SIZE, 0); tto->pool = pool; tto->obj = obj; tto->cx = cx; tto->session = jss->session; teletone_init_session(&tto->ts, 0, teletone_handler, tto); JS_SetPrivate(cx, obj, tto); return JS_TRUE; } static void teletone_destroy(JSContext *cx, JSObject *obj) { struct teletone_obj *tto = JS_GetPrivate(cx, obj); switch_memory_pool_t *pool; if (tto) { if (tto->timer) { switch_core_timer_destroy(tto->timer); } teletone_destroy_session(&tto->ts); switch_buffer_destroy(&tto->audio_buffer); switch_buffer_destroy(&tto->loop_buffer); switch_core_codec_destroy(&tto->codec); pool = tto->pool; tto->pool = NULL; if (pool) { switch_core_destroy_memory_pool(&pool); } } } static JSBool teletone_add_tone(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { struct teletone_obj *tto = JS_GetPrivate(cx, obj); if (argc > 2) { int x; char *fval; char *map_str; map_str = JS_GetStringBytes(JS_ValueToString(cx, argv[0])); for(x = 1; x < TELETONE_MAX_TONES; x++) { fval = JS_GetStringBytes(JS_ValueToString(cx, argv[x])); tto->ts.TONES[(int)*map_str].freqs[x-1] = strtod(fval, NULL); } return JS_TRUE; } return JS_FALSE; } static JSBool teletone_on_dtmf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { struct teletone_obj *tto = JS_GetPrivate(cx, obj); if (argc > 0) { tto->function = JS_ValueToFunction(cx, argv[0]); if (argc > 1) { tto->arg = argv[1]; } switch_set_flag(tto, TTF_DTMF); } return JS_TRUE; } static JSBool teletone_generate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { struct teletone_obj *tto = JS_GetPrivate(cx, obj); int32 loops = 0; if (argc > 0) { char *script; switch_core_session_t *session; switch_frame_t write_frame = {0}; unsigned char *fdata[1024]; switch_frame_t *read_frame; int stream_id; switch_core_thread_session_t thread_session; switch_channel_t *channel; if (argc > 1) { if (!JS_ValueToInt32(cx, argv[1], &loops)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot Convert to INT\n"); return JS_FALSE; } loops--; if (!tto->loop_buffer) { switch_buffer_create_dynamic(&tto->loop_buffer, JS_BLOCK_SIZE, JS_BUFFER_SIZE, 0); } } if (tto->audio_buffer) { switch_buffer_zero(tto->audio_buffer); } if (tto->loop_buffer) { switch_buffer_zero(tto->loop_buffer); } tto->ts.debug = 1; tto->ts.debug_stream = switch_core_get_console(); script = JS_GetStringBytes(JS_ValueToString(cx, argv[0])); teletone_run(&tto->ts, script); session = tto->session; write_frame.codec = &tto->codec; write_frame.data = fdata; channel = switch_core_session_get_channel(session); if (tto->timer) { for (stream_id = 0; stream_id < switch_core_session_get_stream_count(session); stream_id++) { switch_core_service_session(session, &thread_session, stream_id); } } for(;;) { if (switch_test_flag(tto, TTF_DTMF)) { char dtmf[128]; char *ret; if (switch_channel_has_dtmf(channel)) { uintN aargc = 0; jsval aargv[4]; switch_channel_dequeue_dtmf(channel, dtmf, sizeof(dtmf)); aargv[aargc++] = STRING_TO_JSVAL (JS_NewStringCopyZ(cx, dtmf)); JS_CallFunction(cx, obj, tto->function, aargc, aargv, &tto->ret); ret = JS_GetStringBytes(JS_ValueToString(cx, tto->ret)); if (strcmp(ret, "true") && strcmp(ret, "undefined")) { *rval = tto->ret; return JS_TRUE; } } } if (tto->timer) { if (switch_core_timer_next(tto->timer)< 0) { break; } } else { switch_status_t status; status = switch_core_session_read_frame(session, &read_frame, -1, 0); if (!SWITCH_READ_ACCEPTABLE(status)) { break; } } if ((write_frame.datalen = (uint32_t)switch_buffer_read(tto->audio_buffer, fdata, write_frame.codec->implementation->bytes_per_frame)) <= 0) { if (loops) { switch_buffer_t *tmp; /* Switcharoo*/ tmp = tto->audio_buffer; tto->audio_buffer = tto->loop_buffer; tto->loop_buffer = tmp; loops--; /* try again */ if ((write_frame.datalen = (uint32_t)switch_buffer_read(tto->audio_buffer, fdata, write_frame.codec->implementation->bytes_per_frame)) <= 0) { break; } } else { break; } } write_frame.samples = write_frame.datalen / 2; 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_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Bad Write\n"); break; } } if (tto->loop_buffer && loops) { switch_buffer_write(tto->loop_buffer, write_frame.data, write_frame.datalen); } } if (tto->timer) { switch_core_thread_session_end(&thread_session); } return JS_TRUE; } return JS_FALSE; } enum teletone_tinyid { TELETONE_NAME }; static JSFunctionSpec teletone_methods[] = { {"generate", teletone_generate, 1}, {"onDTMF", teletone_on_dtmf, 1}, {"addTone", teletone_add_tone, 10}, {0} }; static JSPropertySpec teletone_props[] = { {"name", TELETONE_NAME, JSPROP_READONLY|JSPROP_PERMANENT}, {0} }; static JSBool teletone_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSBool res = JS_TRUE; return res; } JSClass teletone_class = { modname, JSCLASS_HAS_PRIVATE, JS_PropertyStub, JS_PropertyStub, teletone_getProperty, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, teletone_destroy, NULL, NULL, NULL, teletone_construct }; switch_status_t teletone_load(JSContext *cx, JSObject *obj) { JS_InitClass(cx, obj, NULL, &teletone_class, teletone_construct, 3, teletone_props, teletone_methods, teletone_props, teletone_methods ); return SWITCH_STATUS_SUCCESS; } const sm_module_interface_t teletone_module_interface = { /*.name = */ modname, /*.spidermonkey_load*/ teletone_load, /*.next*/ NULL }; SWITCH_MOD_DECLARE(switch_status_t) spidermonkey_init(const sm_module_interface_t **module_interface) { *module_interface = &teletone_module_interface; return SWITCH_STATUS_SUCCESS; } /* For Emacs: * Local Variables: * mode:c * indent-tabs-mode:nil * tab-width:4 * c-basic-offset:4 * End: * For VIM: * vim:set softtabstop=4 shiftwidth=4 tabstop=4 expandtab: */