Asterisk - The Open Source Telephony Project GIT-master-8924258
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Modules Pages
func_speex.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2008, Digium, Inc.
5 *
6 * Brian Degenhardt <bmd@digium.com>
7 * Brett Bryant <bbryant@digium.com>
8 *
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.
14 *
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.
18 */
19
20/*! \file
21 *
22 * \brief Noise reduction and automatic gain control (AGC)
23 *
24 * \author Brian Degenhardt <bmd@digium.com>
25 * \author Brett Bryant <bbryant@digium.com>
26 *
27 * \ingroup functions
28 *
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)
31 */
32
33/*** MODULEINFO
34 <depend>speex</depend>
35 <depend>speex_preprocess</depend>
36 <use type="external">speexdsp</use>
37 <support_level>core</support_level>
38 ***/
39
40#include "asterisk.h"
41
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"
48
49#define DEFAULT_AGC_LEVEL 8000.0
50
51/*** DOCUMENTATION
52 <function name="AGC" language="en_US">
53 <since>
54 <version>1.6.1.0</version>
55 </since>
56 <synopsis>
57 Apply automatic gain control to audio on a channel.
58 </synopsis>
59 <syntax>
60 <parameter name="channeldirection" required="true">
61 <para>This can be either <literal>rx</literal> or <literal>tx</literal></para>
62 </parameter>
63 </syntax>
64 <description>
65 <para>The AGC function will apply automatic gain control to the audio on the
66 channel that it is executed on. Using <literal>rx</literal> for audio received
67 and <literal>tx</literal> for audio transmitted to the channel. When using this
68 function you set a target audio level. It is primarily intended for use with
69 analog lines, but could be useful for other channels as well. The target volume
70 is set with a number between <literal>1-32768</literal>. The larger the number
71 the louder (more gain) the channel will receive.</para>
72 <example title="Apply automatic gain control">
73 exten => 1,1,Set(AGC(rx)=8000)
74 exten => 1,2,Set(AGC(tx)=off)
75 </example>
76 </description>
77 </function>
78 <function name="DENOISE" language="en_US">
79 <since>
80 <version>1.6.1.0</version>
81 </since>
82 <synopsis>
83 Apply noise reduction to audio on a channel.
84 </synopsis>
85 <syntax>
86 <parameter name="channeldirection" required="true">
87 <para>This can be either <literal>rx</literal> or <literal>tx</literal>
88 the values that can be set to this are either <literal>on</literal> and
89 <literal>off</literal></para>
90 </parameter>
91 </syntax>
92 <description>
93 <para>The DENOISE function will apply noise reduction to audio on the channel
94 that it is executed on. It is very useful for noisy analog lines, especially
95 when adjusting gains or using AGC. Use <literal>rx</literal> for audio received from the channel
96 and <literal>tx</literal> to apply the filter to the audio being sent to the channel.</para>
97 <example title="Apply noise reduction">
98 exten => 1,1,Set(DENOISE(rx)=on)
99 exten => 1,2,Set(DENOISE(tx)=off)
100 </example>
101 </description>
102 </function>
103 ***/
104
106 SpeexPreprocessState *state; /*!< speex preprocess state object */
107 int agc; /*!< audio gain control is enabled or not */
108 int denoise; /*!< denoise is enabled or not */
109 int samples; /*!< n of 8Khz samples in last frame */
110 float agclevel; /*!< audio gain control level [1.0 - 32768.0] */
111};
112
117};
118
119static void destroy_callback(void *data)
120{
121 struct speex_info *si = data;
122
124
125 if (si->rx && si->rx->state) {
126 speex_preprocess_state_destroy(si->rx->state);
127 }
128
129 if (si->tx && si->tx->state) {
130 speex_preprocess_state_destroy(si->tx->state);
131 }
132
133 if (si->rx) {
134 ast_free(si->rx);
135 }
136
137 if (si->tx) {
138 ast_free(si->tx);
139 }
140
141 ast_free(data);
142};
143
144static const struct ast_datastore_info speex_datastore = {
145 .type = "speex",
146 .destroy = destroy_callback
147};
148
149static int speex_callback(struct ast_audiohook *audiohook, struct ast_channel *chan, struct ast_frame *frame, enum ast_audiohook_direction direction)
150{
151 struct ast_datastore *datastore = NULL;
152 struct speex_direction_info *sdi = NULL;
153 struct speex_info *si = NULL;
154 char source[80];
155
156 /* If the audiohook is stopping it means the channel is shutting down.... but we let the datastore destroy take care of it */
158 return -1;
159 }
160
161 /* We are called with chan already locked */
162 if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) {
163 return -1;
164 }
165
166 si = datastore->data;
167
168 sdi = (direction == AST_AUDIOHOOK_DIRECTION_READ) ? si->rx : si->tx;
169
170 if (!sdi) {
171 return -1;
172 }
173
174 if ((sdi->samples != frame->samples) || (ast_format_get_sample_rate(frame->subclass.format) != si->lastrate)) {
176 if (sdi->state) {
177 speex_preprocess_state_destroy(sdi->state);
178 }
179
180 if (!(sdi->state = speex_preprocess_state_init((sdi->samples = frame->samples), si->lastrate))) {
181 return -1;
182 }
183
184 speex_preprocess_ctl(sdi->state, SPEEX_PREPROCESS_SET_AGC, &sdi->agc);
185
186 if (sdi->agc) {
187 speex_preprocess_ctl(sdi->state, SPEEX_PREPROCESS_SET_AGC_LEVEL, &sdi->agclevel);
188 }
189
190 speex_preprocess_ctl(sdi->state, SPEEX_PREPROCESS_SET_DENOISE, &sdi->denoise);
191 }
192
193 speex_preprocess(sdi->state, frame->data.ptr, NULL);
194 snprintf(source, sizeof(source), "%s/speex", frame->src);
195 if (frame->mallocd & AST_MALLOCD_SRC) {
196 ast_free((char *) frame->src);
197 }
198 frame->src = ast_strdup(source);
199 frame->mallocd |= AST_MALLOCD_SRC;
200
201 return 0;
202}
203
204static int speex_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
205{
206 struct ast_datastore *datastore = NULL;
207 struct speex_info *si = NULL;
208 struct speex_direction_info **sdi = NULL;
209 int is_new = 0;
210
211 if (!chan) {
212 ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
213 return -1;
214 }
215
216 if (strcasecmp(data, "rx") && strcasecmp(data, "tx")) {
217 ast_log(LOG_ERROR, "Invalid argument provided to the %s function\n", cmd);
218 return -1;
219 }
220
221 ast_channel_lock(chan);
222 if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) {
223 ast_channel_unlock(chan);
224
225 if (!(datastore = ast_datastore_alloc(&speex_datastore, NULL))) {
226 return 0;
227 }
228
229 if (!(si = ast_calloc(1, sizeof(*si)))) {
230 ast_datastore_free(datastore);
231 return 0;
232 }
233
236 si->lastrate = 8000;
237 is_new = 1;
238 } else {
239 ast_channel_unlock(chan);
240 si = datastore->data;
241 }
242
243 if (!strcasecmp(data, "rx")) {
244 sdi = &si->rx;
245 } else {
246 sdi = &si->tx;
247 }
248
249 if (!*sdi) {
250 if (!(*sdi = ast_calloc(1, sizeof(**sdi)))) {
251 return 0;
252 }
253 /* Right now, the audiohooks API will _only_ provide us 8 kHz slinear
254 * audio. When it supports 16 kHz (or any other sample rates, we will
255 * have to take that into account here. */
256 (*sdi)->samples = -1;
257 }
258
259 if (!strcasecmp(cmd, "agc")) {
260 if (!sscanf(value, "%30f", &(*sdi)->agclevel))
261 (*sdi)->agclevel = ast_true(value) ? DEFAULT_AGC_LEVEL : 0.0;
262
263 if ((*sdi)->agclevel > 32768.0) {
264 ast_log(LOG_WARNING, "AGC(%s)=%.01f is greater than 32768... setting to 32768 instead\n",
265 ((*sdi == si->rx) ? "rx" : "tx"), (*sdi)->agclevel);
266 (*sdi)->agclevel = 32768.0;
267 }
268
269 (*sdi)->agc = !!((*sdi)->agclevel);
270
271 if ((*sdi)->state) {
272 speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_AGC, &(*sdi)->agc);
273 if ((*sdi)->agc) {
274 speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_AGC_LEVEL, &(*sdi)->agclevel);
275 }
276 }
277 } else if (!strcasecmp(cmd, "denoise")) {
278 (*sdi)->denoise = (ast_true(value) != 0);
279
280 if ((*sdi)->state) {
281 speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_DENOISE, &(*sdi)->denoise);
282 }
283 }
284
285 if (!(*sdi)->agc && !(*sdi)->denoise) {
286 if ((*sdi)->state)
287 speex_preprocess_state_destroy((*sdi)->state);
288
289 ast_free(*sdi);
290 *sdi = NULL;
291 }
292
293 if (!si->rx && !si->tx) {
294 if (is_new) {
295 is_new = 0;
296 } else {
297 ast_channel_lock(chan);
298 ast_channel_datastore_remove(chan, datastore);
299 ast_channel_unlock(chan);
302 }
303
304 ast_datastore_free(datastore);
305 }
306
307 if (is_new) {
308 datastore->data = si;
309 ast_channel_lock(chan);
310 ast_channel_datastore_add(chan, datastore);
311 ast_channel_unlock(chan);
313 }
314
315 return 0;
316}
317
318static int speex_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
319{
320 struct ast_datastore *datastore = NULL;
321 struct speex_info *si = NULL;
322 struct speex_direction_info *sdi = NULL;
323
324 if (!chan) {
325 ast_log(LOG_ERROR, "%s cannot be used without a channel!\n", cmd);
326 return -1;
327 }
328
329 ast_channel_lock(chan);
330 if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) {
331 ast_channel_unlock(chan);
332 return -1;
333 }
334 ast_channel_unlock(chan);
335
336 si = datastore->data;
337
338 if (!strcasecmp(data, "tx"))
339 sdi = si->tx;
340 else if (!strcasecmp(data, "rx"))
341 sdi = si->rx;
342 else {
343 ast_log(LOG_ERROR, "%s(%s) must either \"tx\" or \"rx\"\n", cmd, data);
344 return -1;
345 }
346
347 if (!strcasecmp(cmd, "agc"))
348 snprintf(buf, len, "%.01f", sdi ? sdi->agclevel : 0.0);
349 else
350 snprintf(buf, len, "%d", sdi ? sdi->denoise : 0);
351
352 return 0;
353}
354
356 .name = "AGC",
357 .write = speex_write,
358 .read = speex_read,
359 .read_max = 22,
360};
361
363 .name = "DENOISE",
364 .write = speex_write,
365 .read = speex_read,
366 .read_max = 22,
367};
368
369static int unload_module(void)
370{
373 return 0;
374}
375
376static int load_module(void)
377{
380 }
381
385 }
386
388}
389
390AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Noise reduction and Automatic Gain Control (AGC)");
if(!yyg->yy_init)
Definition: ast_expr2f.c:854
Asterisk main include file. File version handling, generic pbx functions.
#define ast_free(a)
Definition: astmm.h:180
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define ast_log
Definition: astobj2.c:42
Audiohooks Architecture.
@ AST_AUDIOHOOK_MANIPULATE_ALL_RATES
Definition: audiohook.h:75
int ast_audiohook_init(struct ast_audiohook *audiohook, enum ast_audiohook_type type, const char *source, enum ast_audiohook_init_flags flags)
Initialize an audiohook structure.
Definition: audiohook.c:100
int ast_audiohook_remove(struct ast_channel *chan, struct ast_audiohook *audiohook)
Remove an audiohook from a specified channel.
Definition: audiohook.c:749
ast_audiohook_direction
Definition: audiohook.h:48
@ AST_AUDIOHOOK_DIRECTION_READ
Definition: audiohook.h:49
int ast_audiohook_detach(struct ast_audiohook *audiohook)
Detach audiohook from channel.
Definition: audiohook.c:578
int ast_audiohook_attach(struct ast_channel *chan, struct ast_audiohook *audiohook)
Attach audiohook to channel.
Definition: audiohook.c:512
int ast_audiohook_destroy(struct ast_audiohook *audiohook)
Destroys an audiohook structure.
Definition: audiohook.c:124
@ AST_AUDIOHOOK_TYPE_MANIPULATE
Definition: audiohook.h:38
@ AST_AUDIOHOOK_STATUS_DONE
Definition: audiohook.h:45
General Asterisk PBX channel definitions.
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2414
int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore *datastore)
Remove a datastore from a channel.
Definition: channel.c:2423
#define ast_channel_lock(chan)
Definition: channel.h:2970
#define ast_channel_unlock(chan)
Definition: channel.h:2971
struct ast_datastore * ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, const char *uid)
Find a datastore on a channel.
Definition: channel.c:2428
#define ast_datastore_alloc(info, uid)
Definition: datastore.h:85
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
Definition: datastore.c:68
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
unsigned int ast_format_get_sample_rate(const struct ast_format *format)
Get the sample rate of a media format.
Definition: format.c:379
direction
static int speex_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition: func_speex.c:318
static struct ast_custom_function agc_function
Definition: func_speex.c:355
#define DEFAULT_AGC_LEVEL
Definition: func_speex.c:49
static struct ast_custom_function denoise_function
Definition: func_speex.c:362
static int speex_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
Definition: func_speex.c:204
static void destroy_callback(void *data)
Definition: func_speex.c:119
static int load_module(void)
Definition: func_speex.c:376
static int speex_callback(struct ast_audiohook *audiohook, struct ast_channel *chan, struct ast_frame *frame, enum ast_audiohook_direction direction)
Definition: func_speex.c:149
static int unload_module(void)
Definition: func_speex.c:369
static const struct ast_datastore_info speex_datastore
Definition: func_speex.c:144
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
#define AST_MALLOCD_SRC
@ AST_FRAME_VOICE
#define LOG_ERROR
#define LOG_WARNING
Asterisk module definitions.
#define AST_MODULE_INFO_STANDARD(keystr, desc)
Definition: module.h:581
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
Core PBX routines and definitions.
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1559
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
#define NULL
Definition: resample.c:96
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true"....
Definition: utils.c:2199
ast_audiohook_manipulate_callback manipulate_callback
Definition: audiohook.h:118
enum ast_audiohook_status status
Definition: audiohook.h:108
Main Channel structure associated with a channel.
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
const char * name
Definition: pbx.h:119
Structure for a data store type.
Definition: datastore.h:31
const char * type
Definition: datastore.h:32
Structure for a data store object.
Definition: datastore.h:64
void * data
Definition: datastore.h:66
struct ast_format * format
Data structure associated with a single frame of data.
union ast_frame::@228 data
struct ast_frame_subclass subclass
enum ast_frame_type frametype
const char * src
SpeexPreprocessState * state
Definition: func_speex.c:106
struct speex_direction_info * rx
Definition: func_speex.c:116
struct ast_audiohook audiohook
Definition: func_speex.c:114
int lastrate
Definition: func_speex.c:115
struct speex_direction_info * tx
Definition: func_speex.c:116
int value
Definition: syslog.c:37
Utility functions.