]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
Ringback (sponsored by Front Logic)
authorAnthony Minessale <anthony.minessale@gmail.com>
Sun, 19 Nov 2006 01:05:06 +0000 (01:05 +0000)
committerAnthony Minessale <anthony.minessale@gmail.com>
Sun, 19 Nov 2006 01:05:06 +0000 (01:05 +0000)
This addition lets you set artifical ringback on a channel
that is waiting for an originated call to be answered.

the syntax is

<action application="set" data="ringback=[data]"/>

where data is either the full path to an audio file
or a teletone generation script..

syntax of teletone scripts

LEGEND:

0-9,a-d,*,# (standard dtmf tones)

variables: c,r,d,v,>,<,+,w,l,L,%

c (channels)        - Sets the number of channels.
r (rate)            - Sets the sample rate.
d (duration)        - Sets the default tone duration.
v (volume)          - Sets the default volume.
> (decrease vol)    - factor to decrease volume by per frame (0 for even decrease across duration).
< (increase vol)    - factor to increase volume by per frame (0 for even increase across duration).
+ (step)            - factor to step by used by < and >.
w (wait)            - default silence after each tone.
l (loops)           - number of times to repeat each tone in the script.
L (LOOPS)           - number of times to repeat the the whole script.
% (manual tone)     - a generic tone specified by a duration, a wait and a list of frequencies.

standard tones can have custom duration per use with the () modifier
7(1000, 500) to generate DTMF 7 for 1 second then pause .5 seconds

EXAMPLES

UK Ring Tone [400+450 hz on for 400ms off for 200ms then 400+450 hz on for 400ms off for 2200ms]
%(400,200,400,450);%(400,2200,400,450)

US Ring Tone [440+480 hz on for 2000ms off for 4000ms]
%(2000,4000,440,480)

ATT BONG [volume level 4000, even decay, step by 2, # key for 60ms with no wait, volume level 2000, 350+440hz {us dialtone} for 940ms
v=4000;>=0;+=2;#(60,0);v=2000;%(940,0,350,440)

SIT Tone 913.8 hz for 274 ms with no wait, 1370.6 hz for 274 ms with no wait, 1776.7 hz for 380ms with no wait
%(274,0,913.8);%(274,0,1370.6);%(380,0,1776.7)

ATTN TONE (phone's off the hook!) 1400+2060+2450+2600 hz for 100ms with 100ms wait
%(100,100,1400,2060,2450,2600)

git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@3408 d0543943-73ff-0310-b7d9-9358b9ac24b2

12 files changed:
conf/freeswitch.xml
libs/libteletone/src/libteletone_generate.c
libs/libteletone/src/libteletone_generate.h
src/include/switch_apr.h
src/include/switch_core.h
src/mod/dialplans/mod_dialplan_directory/mod_dialplan_directory.c
src/mod/dialplans/mod_dialplan_xml/mod_dialplan_xml.c
src/mod/formats/mod_native_file/mod_native_file.c
src/mod/languages/mod_spidermonkey_teletone/mod_spidermonkey_teletone.c
src/switch_channel.c
src/switch_core.c
src/switch_ivr.c

index 2e13d1e3e2705aa9bd68ccb62e48e9b418aac592..32b474cb6417f288ef9340a0c088cb21448fd79a 100644 (file)
@@ -8,6 +8,12 @@
         <!--Most channels to allow at once -->
         <param name="max-sessions" value="1000"/>
       </settings>
+      <!--Any variables defined here will be available in every channel, in the dialplan etc -->
+      <variables>
+       <variable name="uk-ring" value="%(400,200,400,450);%(400,2200,400,450)"/>
+       <variable name="us-ring" value="%(2000, 4000, 440.0, 480.0)"/>
+       <variable name="bong-ring" value="v=4000;>=0;+=2;#(60,0);v=2000;%(940,0,350,440)"/>
+      </variables>
     </configuration>
 
     <configuration name="modules.conf" description="Modules">
 
       <extension name="testmusic">
         <condition field="destination_number" expression="^1234$">
+         <!-- Request a certain tone/file to be played while you wait for the call to be answered-->
+         <action application="set" data="ringback=${us-ring}"/>
+         <!--<action application="set" data="ringback=/home/ring.wav"/>-->
           <action application="bridge" data="sofia/test/1234@66.250.68.194"/>
         </condition>
       </extension>
index 6c1237a0698f771457442c967a47e1a0a21f1a5c..b3421da35870937a1d06f6e5962a3541e0dac8d2 100644 (file)
@@ -84,11 +84,14 @@ int teletone_init_session(teletone_generation_session_t *ts, int buflen, tone_ha
        ts->user_data = user_data;
        ts->volume = 1500;
        ts->decay_step = 0;
-       if ((ts->buffer = calloc(buflen, sizeof(teletone_audio_t))) == 0) {
-               return -1;
+       if (buflen) {
+               if ((ts->buffer = calloc(buflen, sizeof(teletone_audio_t))) == 0) {
+                       return -1;
+               }
+               ts->datalen = buflen;
+       } else {
+               ts->dynamic = 1024;
        }
-       ts->datalen = buflen;
-
        /* Add Standard DTMF Tones */
        teletone_set_tone(ts, '1', 697.0, 1209.0, 0.0);
        teletone_set_tone(ts, '2', 697.0, 1336.0, 0.0);
@@ -120,8 +123,21 @@ int teletone_destroy_session(teletone_generation_session_t *ts)
        return 0;
 }
 
-/** Generate a specified number of samples containing the three specified
- *  frequencies (in hertz) and dump to the file descriptor audio_fd. */
+static int ensure_buffer(teletone_generation_session_t *ts, int need)
+{
+       need += ts->samples;
+       need *= sizeof(teletone_audio_t);
+       need *= ts->channels;
+
+       if (need > ts->datalen) {
+               ts->datalen = need + ts->dynamic;
+               if (!(ts->buffer = realloc(ts->buffer, ts->datalen))) {
+                       return -1;
+               }
+       }
+
+       return 0;
+}
 
 int teletone_mux_tones(teletone_generation_session_t *ts, teletone_tone_map_t *map)
 {
@@ -159,6 +175,11 @@ int teletone_mux_tones(teletone_generation_session_t *ts, teletone_tone_map_t *m
                        duration *= ts->channels;
                }
 
+               if (ts->dynamic) {
+                       if (ensure_buffer(ts, duration)) {
+                               return -1;
+                       }
+               }
                for (ts->samples = 0; ts->samples < ts->datalen && ts->samples < duration; ts->samples++) {
                        if (ts->decay_step && !(ts->samples % ts->decay_step) && ts->volume > 0 && ts->samples > decay) {
                                ts->volume += ts->decay_direction;
@@ -179,6 +200,11 @@ int teletone_mux_tones(teletone_generation_session_t *ts, teletone_tone_map_t *m
                        
                }
        }
+       if (ts->dynamic) {
+               if (ensure_buffer(ts, wait)) {
+                       return -1;
+               }
+       }
        for (c = 0; c < ts->channels; c++) {
                for (i = 0; i < wait && ts->samples < ts->datalen; i++) {
                        ts->buffer[ts->samples++] = 0;
@@ -211,19 +237,37 @@ int teletone_mux_tones(teletone_generation_session_t *ts, teletone_tone_map_t *m
        return ts->samples;
 }
 
+/* don't ask */
+static char *my_strdup (const char *s)
+{
+       size_t len = strlen (s) + 1;
+       void *new = malloc (len);
+       
+       if (new == NULL) {
+               return NULL;
+       }
+
+       return (char *) memcpy (new, s, len);
+}
 
 int teletone_run(teletone_generation_session_t *ts, char *cmd)
 {
-       char *data, *cur, *end;
+       char *data = NULL, *cur = NULL, *end = NULL;
        int var = 0, LOOPING = 0;
+       
+       if (!cmd) {
+               return -1;
+       }
 
        do {
-               data = strdup(cmd);
+               if (!(data = my_strdup(cmd))) {
+                       return -1;
+               }
+
                cur = data;
-               
+
                while (*cur) {
                        var = 0;
-
                        if (*cur == ' ' || *cur == '\r' || *cur == '\n') {
                                cur++;
                                continue;
index e47ca5387e31245c5d0bc723e1588fbd73f0c8ec..817cda0c63c24be6b97e70f4bcc21fa3d20bb681 100644 (file)
@@ -56,7 +56,7 @@ extern "C" {
        This module is responsible for tone generation specifics
 */
 
-typedef short teletone_audio_t;
+typedef int16_t teletone_audio_t;
 struct teletone_generation_session;
 typedef int (*tone_handler)(struct teletone_generation_session *ts, teletone_tone_map_t *map);
 
@@ -101,6 +101,7 @@ struct teletone_generation_session {
        /*! In-Use size of the buffer */
        int samples;
        /*! Callback function called during generation */
+       int dynamic;
        tone_handler handler;
 };
 
index 7dde426e385a69589da41fab54abf3cd16f58301..71b40476c0375a4628ef97e966c90319c1cf7d83 100644 (file)
@@ -183,6 +183,7 @@ typedef apr_file_t switch_file_t;
  */
 DoxyDefine(apr_status_t switch_file_open(switch_file_t **newf, const char *fname, apr_int32_t flag, switch_fileperms_t perm, switch_pool_t *pool);)
 #define switch_file_open apr_file_open
+#define switch_file_seek apr_file_seek
 
 /**
  * Close the specified file.
index 8e40776b5153dfdd96b3508632fdc50d17e624d1..56de1c27871e51049dd8705efd30d9c2c53a0ac2 100644 (file)
@@ -417,6 +417,13 @@ SWITCH_DECLARE(char *) switch_core_session_get_uuid(switch_core_session_t *sessi
 */
 SWITCH_DECLARE(switch_core_session_t *) switch_core_session_locate(char *uuid_str);
 
+/*! 
+  \brief Retrieve a global variable from the core
+  \param varname the name of the variable
+  \return the value of the desired variable
+*/
+SWITCH_DECLARE(char *) switch_core_get_variable(char *varname);
+
 /*! 
   \brief Hangup All Sessions
   \param cause the hangup cause to apply to the hungup channels
index 1a4d516e0227df397f350d15c9088b7668ffe75a..ba2cc98b5ef1b66f75e757c663355ba5773c9ae1 100644 (file)
@@ -151,7 +151,7 @@ static switch_caller_extension_t *directory_dialplan_hunt(switch_core_session_t
        if (extension) {
                switch_channel_set_state(channel, CS_EXECUTE);
        } else {
-               switch_channel_hangup(channel, SWITCH_CAUSE_MESSAGE_TYPE_NONEXIST);
+               switch_channel_hangup(channel, SWITCH_CAUSE_NO_ROUTE_DESTINATION);
        }
 
        return extension;
index 1a832598255c877b4f8fe617a3d831c5469f8c63..c247aa62f47a0513694e95d30ffc3e5db5ae1814 100644 (file)
@@ -296,7 +296,7 @@ static switch_caller_extension_t *dialplan_hunt(switch_core_session_t *session)
        if (!(xcontext = switch_xml_find_child(cfg, "context", "name", context))) {
                if (!(xcontext = switch_xml_find_child(cfg, "context", "name", "global"))) {
                        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "context %s not found\n", context);
-                       switch_channel_hangup(channel, SWITCH_CAUSE_MESSAGE_TYPE_NONEXIST);
+                       switch_channel_hangup(channel, SWITCH_CAUSE_NO_ROUTE_DESTINATION);
                        switch_xml_free(xml);
                        return NULL;
                }
@@ -325,7 +325,7 @@ static switch_caller_extension_t *dialplan_hunt(switch_core_session_t *session)
        if (extension) {
                switch_channel_set_state(channel, CS_EXECUTE);
        } else {
-               switch_channel_hangup(channel, SWITCH_CAUSE_MESSAGE_TYPE_NONEXIST);
+               switch_channel_hangup(channel, SWITCH_CAUSE_NO_ROUTE_DESTINATION);
        }
 
        return extension;
index 7a6a600e76464edfb31e3ce90b7e43e4cc9b9a3f..06b38b80f23047dbfc55ea80ffc159251a00b0a9 100644 (file)
@@ -75,7 +75,7 @@ static switch_status_t native_file_file_open(switch_file_handle_t *handle, char
        handle->channels = 1;
        handle->format = 0;
        handle->sections = 0;
-       handle->seekable = 0;
+       handle->seekable = 1;
        handle->speed = 0;
        handle->private_info = context;
        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Opening File [%s] %dhz\n", path, handle->samplerate);
@@ -98,8 +98,10 @@ static switch_status_t native_file_file_close(switch_file_handle_t *handle)
 
 static switch_status_t native_file_file_seek(switch_file_handle_t *handle, unsigned int *cur_sample, int64_t samples, int whence)
 {
-       //native_file_context *context = handle->private_info;
-
+       native_file_context *context = handle->private_info;
+       
+       switch_file_seek(context->fd, whence, &samples);
+       
        return SWITCH_STATUS_FALSE;
 
 }
index 19769cd9bf8ac0ac2ead7577669f8da52339315e..a14a3629ea77bf01eb2fa41eaf88091015f15458 100644 (file)
@@ -75,7 +75,6 @@ static int teletone_handler(teletone_generation_session_t *ts, teletone_tone_map
 /*********************************************************************************/
 static JSBool teletone_construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 {
-       int32 memory = 65535;
        JSObject *session_obj;
        struct teletone_obj *tto = NULL;
        struct js_session *jss = NULL;
@@ -101,12 +100,6 @@ static JSBool teletone_construct(JSContext *cx, JSObject *obj, uintN argc, jsval
                timer_name = JS_GetStringBytes(JS_ValueToString(cx, argv[1]));
        }
 
-       if (argc > 2) {
-               if (!JS_ValueToInt32(cx, argv[2], &memory)) {
-                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Cannot Convert to INT\n");
-                       return JS_FALSE;
-               }
-       } 
        switch_core_new_memory_pool(&pool);
 
        if (!(tto = switch_core_alloc(pool, sizeof(*tto)))) {
@@ -149,7 +142,7 @@ static JSBool teletone_construct(JSContext *cx, JSObject *obj, uintN argc, jsval
        tto->obj = obj;
        tto->cx = cx;
        tto->session = jss->session;
-       teletone_init_session(&tto->ts, memory, teletone_handler, tto);
+       teletone_init_session(&tto->ts, 0, teletone_handler, tto);
        JS_SetPrivate(cx, obj, tto);
 
        return JS_TRUE;
index 06c60d9533b9b6a620b1ea7745785ff531bea80f..821b9b3473b82cefd3a9829dd7364aa707f6a31c 100644 (file)
@@ -344,7 +344,7 @@ SWITCH_DECLARE(void) switch_channel_presence(switch_channel_t *channel, char *rp
 
 SWITCH_DECLARE(char *) switch_channel_get_variable(switch_channel_t *channel, char *varname)
 {
-       char *v;
+       char *v = NULL;
        assert(channel != NULL);
 
        if (!(v=switch_core_hash_find(channel->variables, varname))) {
@@ -352,6 +352,7 @@ SWITCH_DECLARE(char *) switch_channel_get_variable(switch_channel_t *channel, ch
                        if (!strcmp(varname, "base_dir")) {
                                return SWITCH_GLOBAL_dirs.base_dir;
                        }
+                       v = switch_core_get_variable(varname);
                }
        }
        
@@ -1113,7 +1114,7 @@ SWITCH_DECLARE(char *) switch_channel_expand_variables(switch_channel_t *channel
 {
        char *p, *c;
        char *data, *indup;
-       size_t sp = 0, len = 0, olen = 0, vtype = 0, br = 0, vnamepos, vvalpos, cpos, ppos, block = 128;
+       size_t sp = 0, len = 0, olen = 0, vtype = 0, br = 0, cpos, block = 128;
        char *sub_val = NULL, *func_val = NULL;
 
        if (!strchr(in, '$') && !strchr(in, '&')) {
@@ -1195,36 +1196,35 @@ SWITCH_DECLARE(char *) switch_channel_expand_variables(switch_channel_t *channel
                                                return in;
                                        }
                                }
-                               nlen = strlen(sub_val);
+                               nlen = sub_val ? strlen(sub_val) : 0;
+
                                if (len + nlen >= olen) {
-                                       olen += block;
+                                       olen = (olen + len + nlen + block);
                                        cpos = c - data;
-                                       ppos = p - data;
-                                       vnamepos = vname - data;
-                                       vvalpos = vval - data;
                                        data = realloc(data, olen);
-
                                        c = data + cpos;
-                                       p = data + ppos;
-                                       vname = data + vnamepos;
-                                       vname = data + vvalpos;
+                                       memset(c, 0, olen - cpos);
                                }
-
-                               len += nlen;
-                               strcat(c, sub_val);
-                               c += nlen;
-
-                               if (func_val) {
-                                       free(func_val);
-                                       func_val = NULL;
+                               if (nlen) {
+                                       len += nlen;
+                                       strcat(c, sub_val);
+                                       c += nlen;
                                }
+                               
+                               switch_safe_free(func_val);
                        }
                        if (sp) {
                                *c++ = ' ';
                                sp = 0;
+                               len++;
                        }
-                       *c++ = *p;
-                       len++;
+
+                       if (*p == '$' || *p == '&') {
+                               p--;
+                       } else {
+                               *c++ = *p;
+                               len++;
+                       } 
                }
        }
        free(indup);
index a06293ce041e38a062610327a424327683fbef5c..926d936988718949d0f7e61e2d57351a8d02f96b 100644 (file)
@@ -120,6 +120,7 @@ struct switch_core_runtime {
        uint32_t session_id;
        apr_pool_t *memory_pool;
        switch_hash_t *session_table;
+       switch_hash_t *global_vars;
        switch_mutex_t *session_table_mutex;
 #ifdef CRASH_PROT
        switch_hash_t *stack_table;
@@ -558,6 +559,11 @@ SWITCH_DECLARE(void) switch_core_session_rwunlock(switch_core_session_t *session
 
 }
 
+SWITCH_DECLARE(char *) switch_core_get_variable(char *varname)
+{
+       return (char *) switch_core_hash_find(runtime.global_vars, varname);
+}
+
 SWITCH_DECLARE(switch_core_session_t *) switch_core_session_locate(char *uuid_str)
 {
        switch_core_session_t *session;
@@ -3849,6 +3855,8 @@ SWITCH_DECLARE(switch_status_t) switch_core_init(char *console, const char **err
                return SWITCH_STATUS_MEMERR;
        }
 
+       switch_core_hash_init(&runtime.global_vars, runtime.memory_pool);
+
        if (switch_xml_init(runtime.memory_pool, err) != SWITCH_STATUS_SUCCESS) {
                apr_terminate();
                return SWITCH_STATUS_MEMERR;
@@ -3868,6 +3876,18 @@ SWITCH_DECLARE(switch_status_t) switch_core_init(char *console, const char **err
                                }
                        }
                }
+
+               if ((settings = switch_xml_child(cfg, "variables"))) {
+                       for (param = switch_xml_child(settings, "variable"); param; param = param->next) {
+                               char *var = (char *) switch_xml_attr_soft(param, "name");
+                               char *val = (char *) switch_xml_attr_soft(param, "value");
+                               char *varr = NULL, *vall = NULL;
+
+                               varr = switch_core_strdup(runtime.memory_pool, var);
+                               vall = switch_core_strdup(runtime.memory_pool, val);
+                               switch_core_hash_insert(runtime.global_vars, varr, vall);
+                       }
+               }
                switch_xml_free(xml);
        }
 
index 28e5183a23f482fd808e4c580988edaa90048416..45d7e617558d0fe72e89d9f48ccbd4fcc2c7d345 100644 (file)
@@ -32,6 +32,7 @@
  */
 #include <switch.h>
 #include <switch_ivr.h>
+#include <libteletone.h>
 
 static const switch_state_handler_table_t audio_bridge_peer_state_handlers;
 
@@ -2198,6 +2199,31 @@ static uint8_t check_channel_status(switch_channel_t **peer_channels,
        
 }
 
+struct ringback {
+       switch_buffer_t *audio_buffer;
+    switch_buffer_t *loop_buffer;
+       teletone_generation_session_t ts;       
+       switch_file_handle_t fhb;
+       switch_file_handle_t *fh;
+       uint8_t asis;
+};
+
+typedef struct ringback ringback_t;
+
+static int teletone_handler(teletone_generation_session_t *ts, teletone_tone_map_t *map)
+{
+       ringback_t *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;
+}
+
 #define MAX_PEERS 256
 SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *session,
                                                                                                         switch_core_session_t **bleg,
@@ -2220,6 +2246,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess
        switch_caller_profile_t *caller_profiles[MAX_PEERS] = {0}, *caller_caller_profile;
        char *chan_type = NULL, *chan_data;
        switch_channel_t *peer_channel = NULL, *peer_channels[MAX_PEERS] = {0};
+       ringback_t ringback = {0};
        time_t start;
        switch_frame_t *read_frame = NULL;
        switch_memory_pool_t *pool = NULL;
@@ -2231,6 +2258,9 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess
        char *file = NULL, *key = NULL, *odata, *var;
        switch_call_cause_t reason = SWITCH_CAUSE_UNALLOCATED;
        uint8_t to = 0;
+       char *ringback_data = NULL;
+       switch_codec_t *read_codec = NULL;
+
        write_frame.data = fdata;
        
        *bleg = NULL;
@@ -2261,6 +2291,7 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess
                caller_channel = switch_core_session_get_channel(session);
                assert(caller_channel != NULL);
 
+               ringback_data = switch_channel_get_variable(caller_channel, "ringback");
                switch_channel_set_variable(caller_channel, "originate_disposition", "failure");
 
                if ((var = switch_channel_get_variable(caller_channel, "group_confirm_key"))) {
@@ -2449,12 +2480,15 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess
 
                }
        endfor1:
-               
-               if (session && !switch_channel_test_flag(caller_channel, CF_NOMEDIA)) {
-                       switch_codec_t *read_codec = NULL;
 
+               if (ringback_data && !switch_channel_test_flag(caller_channel, CF_ANSWERED) && !switch_channel_test_flag(caller_channel, CF_EARLY_MEDIA)) {
+                       switch_channel_pre_answer(caller_channel);
+               }
+
+               if (session && !switch_channel_test_flag(caller_channel, CF_NOMEDIA)) {
                        read_codec = switch_core_session_get_read_codec(session);
                        assert(read_codec != NULL);
+
                        if (!(pass = (uint8_t)switch_test_flag(read_codec, SWITCH_CODEC_FLAG_PASSTHROUGH))) {
                                if (switch_core_codec_init(&write_codec,
                                                                                   "L16",
@@ -2465,6 +2499,8 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess
                                                                                   SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE,
                                                                                   NULL,
                                                                                   pool) == SWITCH_STATUS_SUCCESS) {
+                                       
+                                       
                                        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Raw Codec Activation Success L16@%uhz 1 channel %dms\n",
                                                                          read_codec->implementation->samples_per_second,
                                                                          read_codec->implementation->microseconds_per_frame / 1000);
@@ -2472,9 +2508,60 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess
                                        write_frame.datalen = read_codec->implementation->bytes_per_frame;
                                        write_frame.samples = write_frame.datalen / 2;
                                        memset(write_frame.data, 255, write_frame.datalen);
+
+                                       if (ringback_data) {
+                                               switch_buffer_create_dynamic(&ringback.audio_buffer, 512, 1024, 0);
+                                               switch_buffer_create_dynamic(&ringback.loop_buffer, 512, 1024, 0);
+                                               char *tmp_data = NULL;
+
+                                               if (*ringback_data == '/') {
+                                                       char *ext;
+                                                       
+                                                       if ((ext = strrchr(ringback_data, '.'))) {
+                                                               switch_core_session_set_read_codec(session, &write_codec);
+                                                               ext++;
+                                                       } else {
+                                                               ringback.asis++;
+                                                               write_frame.codec = read_codec;
+                                                               ext = read_codec->implementation->iananame;
+                                                               tmp_data = switch_mprintf("%s.%s", ringback_data, ext);
+                                                               ringback_data = tmp_data;
+                                                       }
+
+                                                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Play Ringback File [%s]\n", ringback_data);
+
+                                                       ringback.fhb.channels = read_codec->implementation->number_of_channels;
+                                                       ringback.fhb.samplerate = read_codec->implementation->samples_per_second;
+                                                       if (switch_core_file_open(&ringback.fhb,
+                                                                                                         ringback_data,
+                                                                                                         SWITCH_FILE_FLAG_READ | SWITCH_FILE_DATA_SHORT,
+                                                                                                         switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) {
+                                                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Playing File\n");
+                                                               switch_safe_free(tmp_data);
+                                                               goto notready;
+                                                       }
+                                                       ringback.fh = &ringback.fhb;
+
+                                                       
+                                               } else {
+                                                       teletone_init_session(&ringback.ts, 0, teletone_handler, &ringback);
+                                                       switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Play Ringback Tone [%s]\n", ringback_data);
+                                                       //ringback.ts.debug = 1;
+                                                       //ringback.ts.debug_stream = switch_core_get_console();
+                                                       if (teletone_run(&ringback.ts, ringback_data)) {
+                                                               switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error Playing Tone\n");
+                                                               teletone_destroy_session(&ringback.ts);
+                                                               switch_buffer_destroy(&ringback.audio_buffer);
+                                                               switch_buffer_destroy(&ringback.loop_buffer);
+                                                               ringback_data = NULL;
+                                                       }
+                                               }
+                                               switch_safe_free(tmp_data);
+                                       }
                                } else {
                                        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Codec Error!");
                                        switch_channel_hangup(caller_channel, SWITCH_CAUSE_NORMAL_TEMPORARY_FAILURE);
+                                       read_codec = NULL;
                                }
                        }
                }
@@ -2505,10 +2592,56 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess
                                if (!SWITCH_READ_ACCEPTABLE(status)) {
                                        break;
                                }
-                               if (read_frame && !pass) {
+                               if (read_frame && !pass && !switch_test_flag(read_frame, SFF_CNG) && read_frame->datalen > 1) {
+                                       if (ringback.fh) {
+                                               uint8_t abuf[1024];
+                                               switch_size_t mlen, olen;
+                                               unsigned int pos = 0;
+
+                                               if (ringback.asis) {
+                                                       mlen = read_frame->datalen;
+                                               } else {
+                                                       mlen = read_frame->datalen  / 2;
+                                               }
+
+                                               olen = mlen;
+                                               switch_core_file_read(ringback.fh, abuf, &olen);
+                                               
+                                               if (olen == 0) {
+                                                       olen = mlen;
+                                                       ringback.fh->speed = 0;
+                                                       switch_core_file_seek(ringback.fh, &pos, 0, SEEK_SET);
+                                                       switch_core_file_read(ringback.fh, abuf, &olen);
+                                                       if (olen == 0) {
+                                                               break;
+                                                       }
+                                               }
+                                               write_frame.data = abuf;
+                                               write_frame.datalen = (uint32_t) ringback.asis ? olen : olen * 2;
+                                               if (switch_core_session_write_frame(session, &write_frame, 1000, 0) != SWITCH_STATUS_SUCCESS) {
+                                                       break;
+                                               }
+                                       } else if (ringback.audio_buffer) {
+                                               if ((write_frame.datalen = (uint32_t)switch_buffer_read(ringback.audio_buffer,
+                                                                                                                                                               write_frame.data,
+                                                                                                                                                               write_frame.codec->implementation->bytes_per_frame)) <= 0) {
+                                                       switch_buffer_t *tmp;
+                                                       tmp = ringback.audio_buffer;
+                                                       ringback.audio_buffer = ringback.loop_buffer;
+                                                       ringback.loop_buffer = tmp;
+                                                       if ((write_frame.datalen = (uint32_t)switch_buffer_read(ringback.audio_buffer,
+                                                                                                                                                                       write_frame.data,
+                                                                                                                                                                       write_frame.codec->implementation->bytes_per_frame)) <= 0) {
+                                                               break;
+                                                       }
+                                               }
+                                       }       
                                        if (switch_core_session_write_frame(session, &write_frame, 1000, 0) != SWITCH_STATUS_SUCCESS) {
                                                break;
                                        }
+                                       if (ringback.loop_buffer) {
+                                               switch_buffer_write(ringback.loop_buffer, write_frame.data, write_frame.datalen);
+                                       }
                                }
 
                        } else {
@@ -2617,6 +2750,19 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess
                        switch_core_codec_destroy(&write_codec);
                }
 
+               if (ringback.fh) {
+                       switch_core_file_close(ringback.fh);
+                       ringback.fh = NULL;
+                       if (read_codec && !ringback.asis) {
+                               switch_core_session_set_read_codec(session, read_codec);
+                               switch_core_session_reset(session);
+                       }
+               } else if (ringback.audio_buffer) {
+                       teletone_destroy_session(&ringback.ts);
+                       switch_buffer_destroy(&ringback.audio_buffer);
+                       switch_buffer_destroy(&ringback.loop_buffer);
+               }
+
                for (i = 0; i < and_argc; i++) {
                        if (!peer_channels[i]) {
                                continue;