Thu Apr 28 2011 16:56:47

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