Asterisk - The Open Source Telephony Project  21.4.1
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  <synopsis>
54  Apply automatic gain control to audio on a channel.
55  </synopsis>
56  <syntax>
57  <parameter name="channeldirection" required="true">
58  <para>This can be either <literal>rx</literal> or <literal>tx</literal></para>
59  </parameter>
60  </syntax>
61  <description>
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  <example title="Apply automatic gain control">
70  exten => 1,1,Set(AGC(rx)=8000)
71  exten => 1,2,Set(AGC(tx)=off)
72  </example>
73  </description>
74  </function>
75  <function name="DENOISE" language="en_US">
76  <synopsis>
77  Apply noise reduction to audio on a channel.
78  </synopsis>
79  <syntax>
80  <parameter name="channeldirection" required="true">
81  <para>This can be either <literal>rx</literal> or <literal>tx</literal>
82  the values that can be set to this are either <literal>on</literal> and
83  <literal>off</literal></para>
84  </parameter>
85  </syntax>
86  <description>
87  <para>The DENOISE function will apply noise reduction to audio on the channel
88  that it is executed on. It is very useful for noisy analog lines, especially
89  when adjusting gains or using AGC. Use <literal>rx</literal> for audio received from the channel
90  and <literal>tx</literal> to apply the filter to the audio being sent to the channel.</para>
91  <example title="Apply noise reduction">
92  exten => 1,1,Set(DENOISE(rx)=on)
93  exten => 1,2,Set(DENOISE(tx)=off)
94  </example>
95  </description>
96  </function>
97  ***/
98 
100  SpeexPreprocessState *state; /*!< speex preprocess state object */
101  int agc; /*!< audio gain control is enabled or not */
102  int denoise; /*!< denoise is enabled or not */
103  int samples; /*!< n of 8Khz samples in last frame */
104  float agclevel; /*!< audio gain control level [1.0 - 32768.0] */
105 };
106 
107 struct speex_info {
108  struct ast_audiohook audiohook;
109  int lastrate;
110  struct speex_direction_info *tx, *rx;
111 };
112 
113 static void destroy_callback(void *data)
114 {
115  struct speex_info *si = data;
116 
117  ast_audiohook_destroy(&si->audiohook);
118 
119  if (si->rx && si->rx->state) {
120  speex_preprocess_state_destroy(si->rx->state);
121  }
122 
123  if (si->tx && si->tx->state) {
124  speex_preprocess_state_destroy(si->tx->state);
125  }
126 
127  if (si->rx) {
128  ast_free(si->rx);
129  }
130 
131  if (si->tx) {
132  ast_free(si->tx);
133  }
134 
135  ast_free(data);
136 };
137 
138 static const struct ast_datastore_info speex_datastore = {
139  .type = "speex",
140  .destroy = destroy_callback
141 };
142 
143 static int speex_callback(struct ast_audiohook *audiohook, struct ast_channel *chan, struct ast_frame *frame, enum ast_audiohook_direction direction)
144 {
145  struct ast_datastore *datastore = NULL;
146  struct speex_direction_info *sdi = NULL;
147  struct speex_info *si = NULL;
148  char source[80];
149 
150  /* If the audiohook is stopping it means the channel is shutting down.... but we let the datastore destroy take care of it */
151  if (audiohook->status == AST_AUDIOHOOK_STATUS_DONE || frame->frametype != AST_FRAME_VOICE) {
152  return -1;
153  }
154 
155  /* We are called with chan already locked */
156  if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) {
157  return -1;
158  }
159 
160  si = datastore->data;
161 
162  sdi = (direction == AST_AUDIOHOOK_DIRECTION_READ) ? si->rx : si->tx;
163 
164  if (!sdi) {
165  return -1;
166  }
167 
168  if ((sdi->samples != frame->samples) || (ast_format_get_sample_rate(frame->subclass.format) != si->lastrate)) {
169  si->lastrate = ast_format_get_sample_rate(frame->subclass.format);
170  if (sdi->state) {
171  speex_preprocess_state_destroy(sdi->state);
172  }
173 
174  if (!(sdi->state = speex_preprocess_state_init((sdi->samples = frame->samples), si->lastrate))) {
175  return -1;
176  }
177 
178  speex_preprocess_ctl(sdi->state, SPEEX_PREPROCESS_SET_AGC, &sdi->agc);
179 
180  if (sdi->agc) {
181  speex_preprocess_ctl(sdi->state, SPEEX_PREPROCESS_SET_AGC_LEVEL, &sdi->agclevel);
182  }
183 
184  speex_preprocess_ctl(sdi->state, SPEEX_PREPROCESS_SET_DENOISE, &sdi->denoise);
185  }
186 
187  speex_preprocess(sdi->state, frame->data.ptr, NULL);
188  snprintf(source, sizeof(source), "%s/speex", frame->src);
189  if (frame->mallocd & AST_MALLOCD_SRC) {
190  ast_free((char *) frame->src);
191  }
192  frame->src = ast_strdup(source);
193  frame->mallocd |= AST_MALLOCD_SRC;
194 
195  return 0;
196 }
197 
198 static int speex_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
199 {
200  struct ast_datastore *datastore = NULL;
201  struct speex_info *si = NULL;
202  struct speex_direction_info **sdi = NULL;
203  int is_new = 0;
204 
205  if (!chan) {
206  ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
207  return -1;
208  }
209 
210  if (strcasecmp(data, "rx") && strcasecmp(data, "tx")) {
211  ast_log(LOG_ERROR, "Invalid argument provided to the %s function\n", cmd);
212  return -1;
213  }
214 
215  ast_channel_lock(chan);
216  if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) {
217  ast_channel_unlock(chan);
218 
219  if (!(datastore = ast_datastore_alloc(&speex_datastore, NULL))) {
220  return 0;
221  }
222 
223  if (!(si = ast_calloc(1, sizeof(*si)))) {
224  ast_datastore_free(datastore);
225  return 0;
226  }
227 
229  si->audiohook.manipulate_callback = speex_callback;
230  si->lastrate = 8000;
231  is_new = 1;
232  } else {
233  ast_channel_unlock(chan);
234  si = datastore->data;
235  }
236 
237  if (!strcasecmp(data, "rx")) {
238  sdi = &si->rx;
239  } else {
240  sdi = &si->tx;
241  }
242 
243  if (!*sdi) {
244  if (!(*sdi = ast_calloc(1, sizeof(**sdi)))) {
245  return 0;
246  }
247  /* Right now, the audiohooks API will _only_ provide us 8 kHz slinear
248  * audio. When it supports 16 kHz (or any other sample rates, we will
249  * have to take that into account here. */
250  (*sdi)->samples = -1;
251  }
252 
253  if (!strcasecmp(cmd, "agc")) {
254  if (!sscanf(value, "%30f", &(*sdi)->agclevel))
255  (*sdi)->agclevel = ast_true(value) ? DEFAULT_AGC_LEVEL : 0.0;
256 
257  if ((*sdi)->agclevel > 32768.0) {
258  ast_log(LOG_WARNING, "AGC(%s)=%.01f is greater than 32768... setting to 32768 instead\n",
259  ((*sdi == si->rx) ? "rx" : "tx"), (*sdi)->agclevel);
260  (*sdi)->agclevel = 32768.0;
261  }
262 
263  (*sdi)->agc = !!((*sdi)->agclevel);
264 
265  if ((*sdi)->state) {
266  speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_AGC, &(*sdi)->agc);
267  if ((*sdi)->agc) {
268  speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_AGC_LEVEL, &(*sdi)->agclevel);
269  }
270  }
271  } else if (!strcasecmp(cmd, "denoise")) {
272  (*sdi)->denoise = (ast_true(value) != 0);
273 
274  if ((*sdi)->state) {
275  speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_DENOISE, &(*sdi)->denoise);
276  }
277  }
278 
279  if (!(*sdi)->agc && !(*sdi)->denoise) {
280  if ((*sdi)->state)
281  speex_preprocess_state_destroy((*sdi)->state);
282 
283  ast_free(*sdi);
284  *sdi = NULL;
285  }
286 
287  if (!si->rx && !si->tx) {
288  if (is_new) {
289  is_new = 0;
290  } else {
291  ast_channel_lock(chan);
292  ast_channel_datastore_remove(chan, datastore);
293  ast_channel_unlock(chan);
294  ast_audiohook_remove(chan, &si->audiohook);
295  ast_audiohook_detach(&si->audiohook);
296  }
297 
298  ast_datastore_free(datastore);
299  }
300 
301  if (is_new) {
302  datastore->data = si;
303  ast_channel_lock(chan);
304  ast_channel_datastore_add(chan, datastore);
305  ast_channel_unlock(chan);
306  ast_audiohook_attach(chan, &si->audiohook);
307  }
308 
309  return 0;
310 }
311 
312 static int speex_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
313 {
314  struct ast_datastore *datastore = NULL;
315  struct speex_info *si = NULL;
316  struct speex_direction_info *sdi = NULL;
317 
318  if (!chan) {
319  ast_log(LOG_ERROR, "%s cannot be used without a channel!\n", cmd);
320  return -1;
321  }
322 
323  ast_channel_lock(chan);
324  if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) {
325  ast_channel_unlock(chan);
326  return -1;
327  }
328  ast_channel_unlock(chan);
329 
330  si = datastore->data;
331 
332  if (!strcasecmp(data, "tx"))
333  sdi = si->tx;
334  else if (!strcasecmp(data, "rx"))
335  sdi = si->rx;
336  else {
337  ast_log(LOG_ERROR, "%s(%s) must either \"tx\" or \"rx\"\n", cmd, data);
338  return -1;
339  }
340 
341  if (!strcasecmp(cmd, "agc"))
342  snprintf(buf, len, "%.01f", sdi ? sdi->agclevel : 0.0);
343  else
344  snprintf(buf, len, "%d", sdi ? sdi->denoise : 0);
345 
346  return 0;
347 }
348 
349 static struct ast_custom_function agc_function = {
350  .name = "AGC",
351  .write = speex_write,
352  .read = speex_read,
353  .read_max = 22,
354 };
355 
356 static struct ast_custom_function denoise_function = {
357  .name = "DENOISE",
358  .write = speex_write,
359  .read = speex_read,
360  .read_max = 22,
361 };
362 
363 static int unload_module(void)
364 {
365  ast_custom_function_unregister(&agc_function);
366  ast_custom_function_unregister(&denoise_function);
367  return 0;
368 }
369 
370 static int load_module(void)
371 {
372  if (ast_custom_function_register(&agc_function)) {
374  }
375 
376  if (ast_custom_function_register(&denoise_function)) {
377  ast_custom_function_unregister(&agc_function);
379  }
380 
382 }
383 
384 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Noise reduction and Automatic Gain Control (AGC)");
const char * name
Definition: pbx.h:119
const char * type
Definition: datastore.h:32
Main Channel structure associated with a channel.
Asterisk main include file. File version handling, generic pbx functions.
Audiohooks Architecture.
int ast_audiohook_remove(struct ast_channel *chan, struct ast_audiohook *audiohook)
Remove an audiohook from a specified channel.
Definition: audiohook.c:721
Structure for a data store type.
Definition: datastore.h:31
int ast_audiohook_attach(struct ast_channel *chan, struct ast_audiohook *audiohook)
Attach audiohook to channel.
Definition: audiohook.c:484
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
Structure for a data store object.
Definition: datastore.h:64
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:2399
int ast_audiohook_destroy(struct ast_audiohook *audiohook)
Destroys an audiohook structure.
Definition: audiohook.c:124
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
Definition: datastore.c:68
struct ast_frame_subclass subclass
Utility functions.
ast_audiohook_manipulate_callback manipulate_callback
Definition: audiohook.h:118
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
General Asterisk PBX channel definitions.
const char * src
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
Core PBX routines and definitions.
int ast_audiohook_detach(struct ast_audiohook *audiohook)
Detach audiohook from channel.
Definition: audiohook.c:550
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
Definition: utils.c:2199
static void destroy_callback(void *data)
Helper function used by datastores to destroy the speech structure upon hangup.
union ast_frame::@224 data
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
ast_audiohook_direction
Definition: audiohook.h:48
void * data
Definition: datastore.h: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
Data structure associated with a single frame of data.
enum ast_audiohook_status status
Definition: audiohook.h:108
enum ast_frame_type frametype
#define AST_MALLOCD_SRC
struct ast_format * format
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2385
SpeexPreprocessState * state
Definition: func_speex.c:100
int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore *datastore)
Remove a datastore from a channel.
Definition: channel.c:2394
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1558