2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2008, Digium, Inc.
6 * Brian Degenhardt <bmd@digium.com>
7 * Brett Bryant <bbryant@digium.com>
9 * See http://www.asterisk.org for more information about
10 * the Asterisk project. Please do not directly contact
11 * any of the maintainers of this project for assistance;
12 * the project provides a web site, mailing lists and IRC
13 * channels for your use.
15 * This program is free software, distributed under the terms of
16 * the GNU General Public License Version 2. See the LICENSE file
17 * at the top of the source tree.
22 * \brief Noise reduction and automatic gain control (AGC)
24 * \author Brian Degenhardt <bmd@digium.com>
25 * \author Brett Bryant <bbryant@digium.com>
29 * The Speex 1.2 library - http://www.speex.org
30 * \note Requires the 1.2 version of the Speex library (which might not be what you find in Linux packages)
34 <depend>speex</depend>
35 <depend>speex_preprocess</depend>
36 <use type="external">speexdsp</use>
37 <support_level>core</support_level>
42 #include <speex/speex_preprocess.h>
43 #include "asterisk/module.h"
44 #include "asterisk/channel.h"
45 #include "asterisk/pbx.h"
46 #include "asterisk/utils.h"
47 #include "asterisk/audiohook.h"
49 #define DEFAULT_AGC_LEVEL 8000.0
52 <function name="AGC" language="en_US">
54 Apply automatic gain control to audio on a channel.
57 <parameter name="channeldirection" required="true">
58 <para>This can be either <literal>rx</literal> or <literal>tx</literal></para>
62 <para>The AGC function will apply automatic gain control to the audio on the
63 channel that it is executed on. Using <literal>rx</literal> for audio received
64 and <literal>tx</literal> for audio transmitted to the channel. When using this
65 function you set a target audio level. It is primarily intended for use with
66 analog lines, but could be useful for other channels as well. The target volume
67 is set with a number between <literal>1-32768</literal>. The larger the number
68 the louder (more gain) the channel will receive.</para>
69 <para>Examples:</para>
70 <para>exten => 1,1,Set(AGC(rx)=8000)</para>
71 <para>exten => 1,2,Set(AGC(tx)=off)</para>
74 <function name="DENOISE" language="en_US">
76 Apply noise reduction to audio on a channel.
79 <parameter name="channeldirection" required="true">
80 <para>This can be either <literal>rx</literal> or <literal>tx</literal>
81 the values that can be set to this are either <literal>on</literal> and
82 <literal>off</literal></para>
86 <para>The DENOISE function will apply noise reduction to audio on the channel
87 that it is executed on. It is very useful for noisy analog lines, especially
88 when adjusting gains or using AGC. Use <literal>rx</literal> for audio received from the channel
89 and <literal>tx</literal> to apply the filter to the audio being sent to the channel.</para>
90 <para>Examples:</para>
91 <para>exten => 1,1,Set(DENOISE(rx)=on)</para>
92 <para>exten => 1,2,Set(DENOISE(tx)=off)</para>
97 struct speex_direction_info {
98 SpeexPreprocessState *state; /*!< speex preprocess state object */
99 int agc; /*!< audio gain control is enabled or not */
100 int denoise; /*!< denoise is enabled or not */
101 int samples; /*!< n of 8Khz samples in last frame */
102 float agclevel; /*!< audio gain control level [1.0 - 32768.0] */
106 struct ast_audiohook audiohook;
108 struct speex_direction_info *tx, *rx;
111 static void destroy_callback(void *data)
113 struct speex_info *si = data;
115 ast_audiohook_destroy(&si->audiohook);
117 if (si->rx && si->rx->state) {
118 speex_preprocess_state_destroy(si->rx->state);
121 if (si->tx && si->tx->state) {
122 speex_preprocess_state_destroy(si->tx->state);
136 static const struct ast_datastore_info speex_datastore = {
138 .destroy = destroy_callback
141 static int speex_callback(struct ast_audiohook *audiohook, struct ast_channel *chan, struct ast_frame *frame, enum ast_audiohook_direction direction)
143 struct ast_datastore *datastore = NULL;
144 struct speex_direction_info *sdi = NULL;
145 struct speex_info *si = NULL;
148 /* If the audiohook is stopping it means the channel is shutting down.... but we let the datastore destroy take care of it */
149 if (audiohook->status == AST_AUDIOHOOK_STATUS_DONE || frame->frametype != AST_FRAME_VOICE) {
153 /* We are called with chan already locked */
154 if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) {
158 si = datastore->data;
160 sdi = (direction == AST_AUDIOHOOK_DIRECTION_READ) ? si->rx : si->tx;
166 if ((sdi->samples != frame->samples) || (ast_format_get_sample_rate(frame->subclass.format) != si->lastrate)) {
167 si->lastrate = ast_format_get_sample_rate(frame->subclass.format);
169 speex_preprocess_state_destroy(sdi->state);
172 if (!(sdi->state = speex_preprocess_state_init((sdi->samples = frame->samples), si->lastrate))) {
176 speex_preprocess_ctl(sdi->state, SPEEX_PREPROCESS_SET_AGC, &sdi->agc);
179 speex_preprocess_ctl(sdi->state, SPEEX_PREPROCESS_SET_AGC_LEVEL, &sdi->agclevel);
182 speex_preprocess_ctl(sdi->state, SPEEX_PREPROCESS_SET_DENOISE, &sdi->denoise);
185 speex_preprocess(sdi->state, frame->data.ptr, NULL);
186 snprintf(source, sizeof(source), "%s/speex", frame->src);
187 if (frame->mallocd & AST_MALLOCD_SRC) {
188 ast_free((char *) frame->src);
190 frame->src = ast_strdup(source);
191 frame->mallocd |= AST_MALLOCD_SRC;
196 static int speex_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
198 struct ast_datastore *datastore = NULL;
199 struct speex_info *si = NULL;
200 struct speex_direction_info **sdi = NULL;
204 ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
208 if (strcasecmp(data, "rx") && strcasecmp(data, "tx")) {
209 ast_log(LOG_ERROR, "Invalid argument provided to the %s function\n", cmd);
213 ast_channel_lock(chan);
214 if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) {
215 ast_channel_unlock(chan);
217 if (!(datastore = ast_datastore_alloc(&speex_datastore, NULL))) {
221 if (!(si = ast_calloc(1, sizeof(*si)))) {
222 ast_datastore_free(datastore);
226 ast_audiohook_init(&si->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "speex", AST_AUDIOHOOK_MANIPULATE_ALL_RATES);
227 si->audiohook.manipulate_callback = speex_callback;
231 ast_channel_unlock(chan);
232 si = datastore->data;
235 if (!strcasecmp(data, "rx")) {
242 if (!(*sdi = ast_calloc(1, sizeof(**sdi)))) {
245 /* Right now, the audiohooks API will _only_ provide us 8 kHz slinear
246 * audio. When it supports 16 kHz (or any other sample rates, we will
247 * have to take that into account here. */
248 (*sdi)->samples = -1;
251 if (!strcasecmp(cmd, "agc")) {
252 if (!sscanf(value, "%30f", &(*sdi)->agclevel))
253 (*sdi)->agclevel = ast_true(value) ? DEFAULT_AGC_LEVEL : 0.0;
255 if ((*sdi)->agclevel > 32768.0) {
256 ast_log(LOG_WARNING, "AGC(%s)=%.01f is greater than 32768... setting to 32768 instead\n",
257 ((*sdi == si->rx) ? "rx" : "tx"), (*sdi)->agclevel);
258 (*sdi)->agclevel = 32768.0;
261 (*sdi)->agc = !!((*sdi)->agclevel);
264 speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_AGC, &(*sdi)->agc);
266 speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_AGC_LEVEL, &(*sdi)->agclevel);
269 } else if (!strcasecmp(cmd, "denoise")) {
270 (*sdi)->denoise = (ast_true(value) != 0);
273 speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_DENOISE, &(*sdi)->denoise);
277 if (!(*sdi)->agc && !(*sdi)->denoise) {
279 speex_preprocess_state_destroy((*sdi)->state);
285 if (!si->rx && !si->tx) {
289 ast_channel_lock(chan);
290 ast_channel_datastore_remove(chan, datastore);
291 ast_channel_unlock(chan);
292 ast_audiohook_remove(chan, &si->audiohook);
293 ast_audiohook_detach(&si->audiohook);
296 ast_datastore_free(datastore);
300 datastore->data = si;
301 ast_channel_lock(chan);
302 ast_channel_datastore_add(chan, datastore);
303 ast_channel_unlock(chan);
304 ast_audiohook_attach(chan, &si->audiohook);
310 static int speex_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
312 struct ast_datastore *datastore = NULL;
313 struct speex_info *si = NULL;
314 struct speex_direction_info *sdi = NULL;
317 ast_log(LOG_ERROR, "%s cannot be used without a channel!\n", cmd);
321 ast_channel_lock(chan);
322 if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) {
323 ast_channel_unlock(chan);
326 ast_channel_unlock(chan);
328 si = datastore->data;
330 if (!strcasecmp(data, "tx"))
332 else if (!strcasecmp(data, "rx"))
335 ast_log(LOG_ERROR, "%s(%s) must either \"tx\" or \"rx\"\n", cmd, data);
339 if (!strcasecmp(cmd, "agc"))
340 snprintf(buf, len, "%.01f", sdi ? sdi->agclevel : 0.0);
342 snprintf(buf, len, "%d", sdi ? sdi->denoise : 0);
347 static struct ast_custom_function agc_function = {
349 .write = speex_write,
354 static struct ast_custom_function denoise_function = {
356 .write = speex_write,
361 static int unload_module(void)
363 ast_custom_function_unregister(&agc_function);
364 ast_custom_function_unregister(&denoise_function);
368 static int load_module(void)
370 if (ast_custom_function_register(&agc_function)) {
371 return AST_MODULE_LOAD_DECLINE;
374 if (ast_custom_function_register(&denoise_function)) {
375 ast_custom_function_unregister(&agc_function);
376 return AST_MODULE_LOAD_DECLINE;
379 return AST_MODULE_LOAD_SUCCESS;
382 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Noise reduction and Automatic Gain Control (AGC)");