Wed Aug 18 22:33:52 2010

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

Generated on Wed Aug 18 22:33:52 2010 for Asterisk - the Open Source PBX by  doxygen 1.4.7