Mon Aug 31 12:30:08 2015

Asterisk developer's documentation


func_speex.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2008, Digium, Inc.
00005  *
00006  * Brian Degenhardt <bmd@digium.com>
00007  * Brett Bryant <bbryant@digium.com> 
00008  *
00009  * See http://www.asterisk.org for more information about
00010  * the Asterisk project. Please do not directly contact
00011  * any of the maintainers of this project for assistance;
00012  * the project provides a web site, mailing lists and IRC
00013  * channels for your use.
00014  *
00015  * This program is free software, distributed under the terms of
00016  * the GNU General Public License Version 2. See the LICENSE file
00017  * at the top of the source tree.
00018  */
00019 
00020 /*! \file
00021  *
00022  * \brief Noise reduction and automatic gain control (AGC)
00023  *
00024  * \author Brian Degenhardt <bmd@digium.com> 
00025  * \author Brett Bryant <bbryant@digium.com> 
00026  *
00027  * \ingroup functions
00028  *
00029  * \extref The Speex 1.2 library - http://www.speex.org
00030  * \note Requires the 1.2 version of the Speex library (which might not be what you find in Linux packages)
00031  */
00032 
00033 /*** MODULEINFO
00034    <depend>speex</depend>
00035    <depend>speex_preprocess</depend>
00036    <use>speexdsp</use>
00037    <support_level>core</support_level>
00038  ***/
00039 
00040 #include "asterisk.h"
00041 
00042 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 411313 $")
00043 
00044 #include <speex/speex_preprocess.h>
00045 #include "asterisk/module.h"
00046 #include "asterisk/channel.h"
00047 #include "asterisk/pbx.h"
00048 #include "asterisk/utils.h"
00049 #include "asterisk/audiohook.h"
00050 
00051 #define DEFAULT_AGC_LEVEL 8000.0
00052 
00053 /*** DOCUMENTATION
00054    <function name="AGC" language="en_US">
00055       <synopsis>
00056          Apply automatic gain control to audio on a channel.
00057       </synopsis>
00058       <syntax>
00059          <parameter name="channeldirection" required="true">
00060             <para>This can be either <literal>rx</literal> or <literal>tx</literal></para>
00061          </parameter>
00062       </syntax>
00063       <description>
00064          <para>The AGC function will apply automatic gain control to the audio on the
00065          channel that it is executed on. Using <literal>rx</literal> for audio received
00066          and <literal>tx</literal> for audio transmitted to the channel. When using this
00067          function you set a target audio level. It is primarily intended for use with
00068          analog lines, but could be useful for other channels as well. The target volume 
00069          is set with a number between <literal>1-32768</literal>. The larger the number
00070          the louder (more gain) the channel will receive.</para>
00071          <para>Examples:</para>
00072          <para>exten => 1,1,Set(AGC(rx)=8000)</para>
00073          <para>exten => 1,2,Set(AGC(tx)=off)</para>
00074       </description>
00075    </function>
00076    <function name="DENOISE" language="en_US">
00077       <synopsis>
00078          Apply noise reduction to audio on a channel.
00079       </synopsis>
00080       <syntax>
00081          <parameter name="channeldirection" required="true">
00082             <para>This can be either <literal>rx</literal> or <literal>tx</literal> 
00083             the values that can be set to this are either <literal>on</literal> and
00084             <literal>off</literal></para>
00085          </parameter>
00086       </syntax>
00087       <description>
00088          <para>The DENOISE function will apply noise reduction to audio on the channel
00089          that it is executed on. It is very useful for noisy analog lines, especially
00090          when adjusting gains or using AGC. Use <literal>rx</literal> for audio received from the channel
00091          and <literal>tx</literal> to apply the filter to the audio being sent to the channel.</para>
00092          <para>Examples:</para>
00093          <para>exten => 1,1,Set(DENOISE(rx)=on)</para>
00094          <para>exten => 1,2,Set(DENOISE(tx)=off)</para>
00095       </description>
00096    </function>
00097  ***/
00098 
00099 struct speex_direction_info {
00100    SpeexPreprocessState *state;  /*!< speex preprocess state object */
00101    int agc;                /*!< audio gain control is enabled or not */
00102    int denoise;               /*!< denoise is enabled or not */
00103    int samples;               /*!< n of 8Khz samples in last frame */
00104    float agclevel;               /*!< audio gain control level [1.0 - 32768.0] */
00105 };
00106 
00107 struct speex_info {
00108    struct ast_audiohook audiohook;
00109    struct speex_direction_info *tx, *rx;
00110 };
00111 
00112 static void destroy_callback(void *data) 
00113 {
00114    struct speex_info *si = data;
00115 
00116    ast_audiohook_destroy(&si->audiohook);
00117 
00118    if (si->rx && si->rx->state) {
00119       speex_preprocess_state_destroy(si->rx->state);
00120    }
00121 
00122    if (si->tx && si->tx->state) {
00123       speex_preprocess_state_destroy(si->tx->state);
00124    }
00125 
00126    if (si->rx) {
00127       ast_free(si->rx);
00128    }
00129 
00130    if (si->tx) {
00131       ast_free(si->tx);
00132    }
00133 
00134    ast_free(data);
00135 };
00136 
00137 static const struct ast_datastore_info speex_datastore = {
00138    .type = "speex",
00139    .destroy = destroy_callback
00140 };
00141 
00142 static int speex_callback(struct ast_audiohook *audiohook, struct ast_channel *chan, struct ast_frame *frame, enum ast_audiohook_direction direction)
00143 {
00144    struct ast_datastore *datastore = NULL;
00145    struct speex_direction_info *sdi = NULL;
00146    struct speex_info *si = NULL;
00147    char source[80];
00148 
00149    /* If the audiohook is stopping it means the channel is shutting down.... but we let the datastore destroy take care of it */
00150    if (audiohook->status == AST_AUDIOHOOK_STATUS_DONE || frame->frametype != AST_FRAME_VOICE) {
00151       return -1;
00152    }
00153 
00154    /* We are called with chan already locked */
00155    if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) {
00156       return -1;
00157    }
00158 
00159    si = datastore->data;
00160 
00161    sdi = (direction == AST_AUDIOHOOK_DIRECTION_READ) ? si->rx : si->tx;
00162 
00163    if (!sdi) {
00164       return -1;
00165    }
00166 
00167    if (sdi->samples != frame->samples) {
00168       if (sdi->state) {
00169          speex_preprocess_state_destroy(sdi->state);
00170       }
00171 
00172       if (!(sdi->state = speex_preprocess_state_init((sdi->samples = frame->samples), 8000))) {
00173          return -1;
00174       }
00175 
00176       speex_preprocess_ctl(sdi->state, SPEEX_PREPROCESS_SET_AGC, &sdi->agc);
00177 
00178       if (sdi->agc) {
00179          speex_preprocess_ctl(sdi->state, SPEEX_PREPROCESS_SET_AGC_LEVEL, &sdi->agclevel);
00180       }
00181 
00182       speex_preprocess_ctl(sdi->state, SPEEX_PREPROCESS_SET_DENOISE, &sdi->denoise);
00183    }
00184 
00185    speex_preprocess(sdi->state, frame->data.ptr, NULL);
00186    snprintf(source, sizeof(source), "%s/speex", frame->src);
00187    if (frame->mallocd & AST_MALLOCD_SRC) {
00188       ast_free((char *) frame->src);
00189    }
00190    frame->src = ast_strdup(source);
00191    frame->mallocd |= AST_MALLOCD_SRC;
00192 
00193    return 0;
00194 }
00195 
00196 static int speex_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
00197 {
00198    struct ast_datastore *datastore = NULL;
00199    struct speex_info *si = NULL;
00200    struct speex_direction_info **sdi = NULL;
00201    int is_new = 0;
00202 
00203    if (!chan) {
00204       ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
00205       return -1;
00206    }
00207 
00208    if (strcasecmp(data, "rx") && strcasecmp(data, "tx")) {
00209       ast_log(LOG_ERROR, "Invalid argument provided to the %s function\n", cmd);
00210       return -1;
00211    }
00212 
00213    ast_channel_lock(chan);
00214    if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) {
00215       ast_channel_unlock(chan);
00216 
00217       if (!(datastore = ast_datastore_alloc(&speex_datastore, NULL))) {
00218          return 0;
00219       }
00220 
00221       if (!(si = ast_calloc(1, sizeof(*si)))) {
00222          ast_datastore_free(datastore);
00223          return 0;
00224       }
00225 
00226       ast_audiohook_init(&si->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "speex");
00227       si->audiohook.manipulate_callback = speex_callback;
00228 
00229       is_new = 1;
00230    } else {
00231       ast_channel_unlock(chan);
00232       si = datastore->data;
00233    }
00234 
00235    if (!strcasecmp(data, "rx")) {
00236       sdi = &si->rx;
00237    } else {
00238       sdi = &si->tx;
00239    }
00240 
00241    if (!*sdi) {
00242       if (!(*sdi = ast_calloc(1, sizeof(**sdi)))) {
00243          return 0;
00244       }
00245       /* Right now, the audiohooks API will _only_ provide us 8 kHz slinear
00246        * audio.  When it supports 16 kHz (or any other sample rates, we will
00247        * have to take that into account here. */
00248       (*sdi)->samples = -1;
00249    }
00250 
00251    if (!strcasecmp(cmd, "agc")) {
00252       if (!sscanf(value, "%30f", &(*sdi)->agclevel))
00253          (*sdi)->agclevel = ast_true(value) ? DEFAULT_AGC_LEVEL : 0.0;
00254    
00255       if ((*sdi)->agclevel > 32768.0) {
00256          ast_log(LOG_WARNING, "AGC(%s)=%.01f is greater than 32768... setting to 32768 instead\n", 
00257                ((*sdi == si->rx) ? "rx" : "tx"), (*sdi)->agclevel);
00258          (*sdi)->agclevel = 32768.0;
00259       }
00260    
00261       (*sdi)->agc = !!((*sdi)->agclevel);
00262 
00263       if ((*sdi)->state) {
00264          speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_AGC, &(*sdi)->agc);
00265          if ((*sdi)->agc) {
00266             speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_AGC_LEVEL, &(*sdi)->agclevel);
00267          }
00268       }
00269    } else if (!strcasecmp(cmd, "denoise")) {
00270       (*sdi)->denoise = (ast_true(value) != 0);
00271 
00272       if ((*sdi)->state) {
00273          speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_DENOISE, &(*sdi)->denoise);
00274       }
00275    }
00276 
00277    if (!(*sdi)->agc && !(*sdi)->denoise) {
00278       if ((*sdi)->state)
00279          speex_preprocess_state_destroy((*sdi)->state);
00280 
00281       ast_free(*sdi);
00282       *sdi = NULL;
00283    }
00284 
00285    if (!si->rx && !si->tx) {
00286       if (is_new) {
00287          is_new = 0;
00288       } else {
00289          ast_channel_lock(chan);
00290          ast_channel_datastore_remove(chan, datastore);
00291          ast_channel_unlock(chan);
00292          ast_audiohook_remove(chan, &si->audiohook);
00293          ast_audiohook_detach(&si->audiohook);
00294       }
00295       
00296       ast_datastore_free(datastore);
00297    }
00298 
00299    if (is_new) { 
00300       datastore->data = si;
00301       ast_channel_lock(chan);
00302       ast_channel_datastore_add(chan, datastore);
00303       ast_channel_unlock(chan);
00304       ast_audiohook_attach(chan, &si->audiohook);
00305    }
00306 
00307    return 0;
00308 }
00309 
00310 static int speex_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00311 {
00312    struct ast_datastore *datastore = NULL;
00313    struct speex_info *si = NULL;
00314    struct speex_direction_info *sdi = NULL;
00315 
00316    if (!chan) {
00317       ast_log(LOG_ERROR, "%s cannot be used without a channel!\n", cmd);
00318       return -1;
00319    }
00320 
00321    ast_channel_lock(chan);
00322    if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) {
00323       ast_channel_unlock(chan);
00324       return -1;
00325    }
00326    ast_channel_unlock(chan);
00327 
00328    si = datastore->data;
00329 
00330    if (!strcasecmp(data, "tx"))
00331       sdi = si->tx;
00332    else if (!strcasecmp(data, "rx"))
00333       sdi = si->rx;
00334    else {
00335       ast_log(LOG_ERROR, "%s(%s) must either \"tx\" or \"rx\"\n", cmd, data);
00336       return -1;
00337    }
00338 
00339    if (!strcasecmp(cmd, "agc"))
00340       snprintf(buf, len, "%.01f", sdi ? sdi->agclevel : 0.0);
00341    else
00342       snprintf(buf, len, "%d", sdi ? sdi->denoise : 0);
00343 
00344    return 0;
00345 }
00346 
00347 static struct ast_custom_function agc_function = {
00348    .name = "AGC",
00349    .write = speex_write,
00350    .read = speex_read,
00351    .read_max = 22,
00352 };
00353 
00354 static struct ast_custom_function denoise_function = {
00355    .name = "DENOISE",
00356    .write = speex_write,
00357    .read = speex_read,
00358    .read_max = 22,
00359 };
00360 
00361 static int unload_module(void)
00362 {
00363    ast_custom_function_unregister(&agc_function);
00364    ast_custom_function_unregister(&denoise_function);
00365    return 0;
00366 }
00367 
00368 static int load_module(void)
00369 {
00370    if (ast_custom_function_register(&agc_function)) {
00371       return AST_MODULE_LOAD_DECLINE;
00372    }
00373 
00374    if (ast_custom_function_register(&denoise_function)) {
00375       ast_custom_function_unregister(&agc_function);
00376       return AST_MODULE_LOAD_DECLINE;
00377    }
00378 
00379    return AST_MODULE_LOAD_SUCCESS;
00380 }
00381 
00382 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Noise reduction and Automatic Gain Control (AGC)");

Generated on 31 Aug 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1