func_talkdetect.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2014, Digium, Inc.
00005  *
00006  * Matt Jordan <mjordan@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Function that raises events when talking is detected on a channel
00022  *
00023  * \author Matt Jordan <mjordan@digium.com>
00024  *
00025  * \ingroup functions
00026  */
00027 
00028 /*** MODULEINFO
00029    <support_level>core</support_level>
00030  ***/
00031 
00032 #include "asterisk.h"
00033 
00034 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 427205 $")
00035 
00036 #include "asterisk/module.h"
00037 #include "asterisk/channel.h"
00038 #include "asterisk/pbx.h"
00039 #include "asterisk/app.h"
00040 #include "asterisk/dsp.h"
00041 #include "asterisk/audiohook.h"
00042 #include "asterisk/stasis.h"
00043 #include "asterisk/stasis_channels.h"
00044 
00045 /*** DOCUMENTATION
00046    <function name="TALK_DETECT" language="en_US">
00047       <synopsis>
00048          Raises notifications when Asterisk detects silence or talking on a channel.
00049       </synopsis>
00050       <syntax>
00051          <parameter name="action" required="true">
00052             <optionlist>
00053                <option name="remove">
00054                   <para>W/O. Remove talk detection from the channel.</para>
00055                </option>
00056                <option name="set">
00057                   <para>W/O. Enable TALK_DETECT and/or configure talk detection
00058                   parameters. Can be called multiple times to change parameters
00059                   on a channel with talk detection already enabled.</para>
00060                   <argument name="dsp_silence_threshold" required="false">
00061                      <para>The time in milliseconds before which a user is considered silent.</para>
00062                   </argument>
00063                   <argument name="dsp_talking_threshold" required="false">
00064                      <para>The time in milliseconds after which a user is considered talking.</para>
00065                   </argument>
00066                </option>
00067             </optionlist>
00068          </parameter>
00069       </syntax>
00070       <description>
00071          <para>The TALK_DETECT function enables events on the channel
00072          it is applied to. These events can be emited over AMI, ARI, and
00073          potentially other Asterisk modules that listen for the internal
00074          notification.</para>
00075          <para>The function has two parameters that can optionally be passed
00076          when <literal>set</literal> on a channel: <replaceable>dsp_talking_threshold</replaceable>
00077          and <replaceable>dsp_silence_threshold</replaceable>.</para>
00078          <para><replaceable>dsp_talking_threshold</replaceable> is the time in milliseconds of sound
00079          above what the dsp has established as base line silence for a user
00080          before a user is considered to be talking. By default, the value of
00081          <replaceable>silencethreshold</replaceable> from <filename>dsp.conf</filename>
00082          is used. If this value is set too tight events may be
00083          falsely triggered by variants in room noise.</para>
00084          <para>Valid values are 1 through 2^31.</para>
00085          <para><replaceable>dsp_silence_threshold</replaceable> is the time in milliseconds of sound
00086          falling within what the dsp has established as baseline silence before
00087          a user is considered be silent. If this value is set too low events
00088          indicating the user has stopped talking may get falsely sent out when
00089          the user briefly pauses during mid sentence.</para>
00090          <para>The best way to approach this option is to set it slightly above
00091          the maximum amount of ms of silence a user may generate during
00092          natural speech.</para>
00093          <para>By default this value is 2500ms. Valid values are 1
00094          through 2^31.</para>
00095          <para>Example:</para>
00096          <para>same => n,Set(TALK_DETECT(set)=)     ; Enable talk detection</para>
00097          <para>same => n,Set(TALK_DETECT(set)=1200) ; Update existing talk detection's silence threshold to 1200 ms</para>
00098          <para>same => n,Set(TALK_DETECT(remove)=)  ; Remove talk detection</para>
00099          <para>same => n,Set(TALK_DETECT(set)=,128) ; Enable and set talk threshold to 128</para>
00100          <para>This function will set the following variables:</para>
00101          <note>
00102             <para>The TALK_DETECT function uses an audiohook to inspect the
00103             voice media frames on a channel. Other functions, such as JITTERBUFFER,
00104             DENOISE, and AGC use a similar mechanism. Audiohooks are processed
00105             in the order in which they are placed on the channel. As such,
00106             it typically makes sense to place functions that modify the voice
00107             media data prior to placing the TALK_DETECT function, as this will
00108             yield better results.</para>
00109             <para>Example:</para>
00110             <para>same => n,Set(DENOISE(rx)=on)    ; Denoise received audio</para>
00111             <para>same => n,Set(TALK_DETECT(set)=) ; Perform talk detection on the denoised received audio</para>
00112          </note>
00113       </description>
00114    </function>
00115  ***/
00116 
00117 #define DEFAULT_SILENCE_THRESHOLD 2500
00118 
00119 /*! \brief Private data structure used with the function's datastore */
00120 struct talk_detect_params {
00121    /*! The audiohook for the function */
00122    struct ast_audiohook audiohook;
00123    /*! Our threshold above which we consider someone talking */
00124    int dsp_talking_threshold;
00125    /*! How long we'll wait before we decide someone is silent */
00126    int dsp_silence_threshold;
00127    /*! Whether or not the user is currently talking */
00128    int talking;
00129    /*! The time the current burst of talking started */
00130    struct timeval talking_start;
00131    /*! The DSP used to do the heavy lifting */
00132    struct ast_dsp *dsp;
00133 };
00134 
00135 /*! \internal \brief Destroy the datastore */
00136 static void datastore_destroy_cb(void *data) {
00137    struct talk_detect_params *td_params = data;
00138 
00139    ast_audiohook_destroy(&td_params->audiohook);
00140 
00141    if (td_params->dsp) {
00142       ast_dsp_free(td_params->dsp);
00143    }
00144    ast_free(data);
00145 }
00146 
00147 /*! \brief The channel datastore the function uses to store state */
00148 static const struct ast_datastore_info talk_detect_datastore = {
00149    .type = "talk_detect",
00150    .destroy = datastore_destroy_cb
00151 };
00152 
00153 /*! \internal \brief An audiohook modification callback
00154  *
00155  * This processes the read side of a channel's voice data to see if
00156  * they are talking
00157  *
00158  * \note We don't actually modify the audio, so this function always
00159  * returns a 'failure' indicating that it didn't modify the data
00160  */
00161 static int talk_detect_audiohook_cb(struct ast_audiohook *audiohook, struct ast_channel *chan, struct ast_frame *frame, enum ast_audiohook_direction direction)
00162 {
00163    int total_silence;
00164    int update_talking = 0;
00165    struct ast_datastore *datastore;
00166    struct talk_detect_params *td_params;
00167    struct stasis_message *message;
00168 
00169    if (audiohook->status == AST_AUDIOHOOK_STATUS_DONE) {
00170       return 1;
00171    }
00172 
00173    if (direction != AST_AUDIOHOOK_DIRECTION_READ) {
00174       return 1;
00175    }
00176 
00177    if (frame->frametype != AST_FRAME_VOICE) {
00178       return 1;
00179    }
00180 
00181    if (!(datastore = ast_channel_datastore_find(chan, &talk_detect_datastore, NULL))) {
00182       return 1;
00183    }
00184    td_params = datastore->data;
00185 
00186    ast_dsp_silence(td_params->dsp, frame, &total_silence);
00187 
00188    if (total_silence < td_params->dsp_silence_threshold) {
00189       if (!td_params->talking) {
00190          update_talking = 1;
00191          td_params->talking_start = ast_tvnow();
00192       }
00193       td_params->talking = 1;
00194    } else {
00195       if (td_params->talking) {
00196          update_talking = 1;
00197       }
00198       td_params->talking = 0;
00199    }
00200 
00201    if (update_talking) {
00202       struct ast_json *blob = NULL;
00203 
00204       if (!td_params->talking) {
00205          int64_t diff_ms = ast_tvdiff_ms(ast_tvnow(), td_params->talking_start);
00206          diff_ms -= td_params->dsp_silence_threshold;
00207 
00208          blob = ast_json_pack("{s: i}", "duration", diff_ms);
00209          if (!blob) {
00210             return 1;
00211          }
00212       }
00213 
00214       ast_verb(4, "%s is now %s\n", ast_channel_name(chan),
00215                   td_params->talking ? "talking" : "silent");
00216       message = ast_channel_blob_create_from_cache(ast_channel_uniqueid(chan),
00217                       td_params->talking ? ast_channel_talking_start() : ast_channel_talking_stop(),
00218                       blob);
00219       if (message) {
00220          stasis_publish(ast_channel_topic(chan), message);
00221          ao2_ref(message, -1);
00222       }
00223 
00224       ast_json_unref(blob);
00225    }
00226 
00227    return 1;
00228 }
00229 
00230 /*! \internal \brief Disable talk detection on the channel */
00231 static int remove_talk_detect(struct ast_channel *chan)
00232 {
00233    struct ast_datastore *datastore = NULL;
00234    struct talk_detect_params *td_params;
00235    SCOPED_CHANNELLOCK(chan_lock, chan);
00236 
00237    datastore = ast_channel_datastore_find(chan, &talk_detect_datastore, NULL);
00238    if (!datastore) {
00239       ast_log(AST_LOG_WARNING, "Cannot remove TALK_DETECT from %s: TALK_DETECT not currently enabled\n",
00240               ast_channel_name(chan));
00241       return -1;
00242    }
00243    td_params = datastore->data;
00244 
00245    if (ast_audiohook_remove(chan, &td_params->audiohook)) {
00246       ast_log(AST_LOG_WARNING, "Failed to remove TALK_DETECT audiohook from channel %s\n",
00247               ast_channel_name(chan));
00248       return -1;
00249    }
00250 
00251    if (ast_channel_datastore_remove(chan, datastore)) {
00252       ast_log(AST_LOG_WARNING, "Failed to remove TALK_DETECT datastore from channel %s\n",
00253               ast_channel_name(chan));
00254       return -1;
00255    }
00256    ast_datastore_free(datastore);
00257 
00258    return 0;
00259 }
00260 
00261 /*! \internal \brief Enable talk detection on the channel */
00262 static int set_talk_detect(struct ast_channel *chan, int dsp_silence_threshold, int dsp_talking_threshold)
00263 {
00264    struct ast_datastore *datastore = NULL;
00265    struct talk_detect_params *td_params;
00266    SCOPED_CHANNELLOCK(chan_lock, chan);
00267 
00268    datastore = ast_channel_datastore_find(chan, &talk_detect_datastore, NULL);
00269    if (!datastore) {
00270       datastore = ast_datastore_alloc(&talk_detect_datastore, NULL);
00271       if (!datastore) {
00272          return -1;
00273       }
00274 
00275       td_params = ast_calloc(1, sizeof(*td_params));
00276       if (!td_params) {
00277          ast_datastore_free(datastore);
00278          return -1;
00279       }
00280 
00281       ast_audiohook_init(&td_params->audiohook,
00282                          AST_AUDIOHOOK_TYPE_MANIPULATE,
00283                          "TALK_DETECT",
00284                          AST_AUDIOHOOK_MANIPULATE_ALL_RATES);
00285       td_params->audiohook.manipulate_callback = talk_detect_audiohook_cb;
00286       ast_set_flag(&td_params->audiohook, AST_AUDIOHOOK_TRIGGER_READ);
00287 
00288       td_params->dsp = ast_dsp_new_with_rate(ast_format_get_sample_rate(ast_channel_rawreadformat(chan)));
00289       if (!td_params->dsp) {
00290          ast_datastore_free(datastore);
00291          ast_free(td_params);
00292          return -1;
00293       }
00294       datastore->data = td_params;
00295 
00296       ast_channel_datastore_add(chan, datastore);
00297       ast_audiohook_attach(chan, &td_params->audiohook);
00298    } else {
00299       /* Talk detection already enabled; update existing settings */
00300       td_params = datastore->data;
00301    }
00302 
00303    td_params->dsp_talking_threshold = dsp_talking_threshold;
00304    td_params->dsp_silence_threshold = dsp_silence_threshold;
00305 
00306    ast_dsp_set_threshold(td_params->dsp, td_params->dsp_talking_threshold);
00307 
00308    return 0;
00309 }
00310 
00311 /*! \internal \brief TALK_DETECT write function callback */
00312 static int talk_detect_fn_write(struct ast_channel *chan, const char *function, char *data, const char *value)
00313 {
00314    int res;
00315 
00316    if (!chan) {
00317       return -1;
00318    }
00319 
00320    if (ast_strlen_zero(data)) {
00321       ast_log(AST_LOG_WARNING, "TALK_DETECT requires an argument\n");
00322       return -1;
00323    }
00324 
00325    if (!strcasecmp(data, "set")) {
00326       int dsp_silence_threshold = DEFAULT_SILENCE_THRESHOLD;
00327       int dsp_talking_threshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
00328 
00329       if (!ast_strlen_zero(value)) {
00330          char *parse = ast_strdupa(value);
00331 
00332          AST_DECLARE_APP_ARGS(args,
00333             AST_APP_ARG(silence_threshold);
00334             AST_APP_ARG(talking_threshold);
00335          );
00336 
00337          AST_STANDARD_APP_ARGS(args, parse);
00338 
00339          if (!ast_strlen_zero(args.silence_threshold)) {
00340             if (sscanf(args.silence_threshold, "%30d", &dsp_silence_threshold) != 1) {
00341                ast_log(AST_LOG_WARNING, "Failed to parse %s for dsp_silence_threshold\n",
00342                        args.silence_threshold);
00343                return -1;
00344             }
00345 
00346             if (dsp_silence_threshold < 1) {
00347                ast_log(AST_LOG_WARNING, "Invalid value %d for dsp_silence_threshold\n",
00348                        dsp_silence_threshold);
00349                return -1;
00350             }
00351          }
00352 
00353          if (!ast_strlen_zero(args.talking_threshold)) {
00354             if (sscanf(args.talking_threshold, "%30d", &dsp_talking_threshold) != 1) {
00355                ast_log(AST_LOG_WARNING, "Failed to parse %s for dsp_talking_threshold\n",
00356                        args.talking_threshold);
00357                return -1;
00358             }
00359 
00360             if (dsp_talking_threshold < 1) {
00361                ast_log(AST_LOG_WARNING, "Invalid value %d for dsp_talking_threshold\n",
00362                        dsp_silence_threshold);
00363                return -1;
00364             }
00365          }
00366       }
00367 
00368       res = set_talk_detect(chan, dsp_silence_threshold, dsp_talking_threshold);
00369    } else if (!strcasecmp(data, "remove")) {
00370       res = remove_talk_detect(chan);
00371    } else {
00372       ast_log(AST_LOG_WARNING, "TALK_DETECT: unknown option %s\n", data);
00373       res = -1;
00374    }
00375 
00376    return res;
00377 }
00378 
00379 /*! \brief Definition of the TALK_DETECT function */
00380 static struct ast_custom_function talk_detect_function = {
00381    .name = "TALK_DETECT",
00382    .write = talk_detect_fn_write,
00383 };
00384 
00385 /*! \internal \brief Unload the module */
00386 static int unload_module(void)
00387 {
00388    int res = 0;
00389 
00390    res |= ast_custom_function_unregister(&talk_detect_function);
00391 
00392    return res;
00393 }
00394 
00395 /*! \internal \brief Load the module */
00396 static int load_module(void)
00397 {
00398    int res = 0;
00399 
00400    res |= ast_custom_function_register(&talk_detect_function);
00401 
00402    return res ? AST_MODULE_LOAD_FAILURE : AST_MODULE_LOAD_SUCCESS;
00403 }
00404 
00405 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Talk detection dialplan function");

Generated on Thu Apr 16 06:27:36 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6