Wed Jan 8 2020 09:49:47

Asterisk developer's documentation


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  * \extref 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>speexdsp</use>
37  <support_level>core</support_level>
38  ***/
39 
40 #include "asterisk.h"
41 
42 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 411313 $")
43 
44 #include <speex/speex_preprocess.h>
45 #include "asterisk/module.h"
46 #include "asterisk/channel.h"
47 #include "asterisk/pbx.h"
48 #include "asterisk/utils.h"
49 #include "asterisk/audiohook.h"
50 
51 #define DEFAULT_AGC_LEVEL 8000.0
52 
53 /*** DOCUMENTATION
54  <function name="AGC" language="en_US">
55  <synopsis>
56  Apply automatic gain control to audio on a channel.
57  </synopsis>
58  <syntax>
59  <parameter name="channeldirection" required="true">
60  <para>This can be either <literal>rx</literal> or <literal>tx</literal></para>
61  </parameter>
62  </syntax>
63  <description>
64  <para>The AGC function will apply automatic gain control to the audio on the
65  channel that it is executed on. Using <literal>rx</literal> for audio received
66  and <literal>tx</literal> for audio transmitted to the channel. When using this
67  function you set a target audio level. It is primarily intended for use with
68  analog lines, but could be useful for other channels as well. The target volume
69  is set with a number between <literal>1-32768</literal>. The larger the number
70  the louder (more gain) the channel will receive.</para>
71  <para>Examples:</para>
72  <para>exten => 1,1,Set(AGC(rx)=8000)</para>
73  <para>exten => 1,2,Set(AGC(tx)=off)</para>
74  </description>
75  </function>
76  <function name="DENOISE" language="en_US">
77  <synopsis>
78  Apply noise reduction to audio on a channel.
79  </synopsis>
80  <syntax>
81  <parameter name="channeldirection" required="true">
82  <para>This can be either <literal>rx</literal> or <literal>tx</literal>
83  the values that can be set to this are either <literal>on</literal> and
84  <literal>off</literal></para>
85  </parameter>
86  </syntax>
87  <description>
88  <para>The DENOISE function will apply noise reduction to audio on the channel
89  that it is executed on. It is very useful for noisy analog lines, especially
90  when adjusting gains or using AGC. Use <literal>rx</literal> for audio received from the channel
91  and <literal>tx</literal> to apply the filter to the audio being sent to the channel.</para>
92  <para>Examples:</para>
93  <para>exten => 1,1,Set(DENOISE(rx)=on)</para>
94  <para>exten => 1,2,Set(DENOISE(tx)=off)</para>
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 {
110 };
111 
112 static void destroy_callback(void *data)
113 {
114  struct speex_info *si = data;
115 
117 
118  if (si->rx && si->rx->state) {
119  speex_preprocess_state_destroy(si->rx->state);
120  }
121 
122  if (si->tx && si->tx->state) {
123  speex_preprocess_state_destroy(si->tx->state);
124  }
125 
126  if (si->rx) {
127  ast_free(si->rx);
128  }
129 
130  if (si->tx) {
131  ast_free(si->tx);
132  }
133 
134  ast_free(data);
135 };
136 
137 static const struct ast_datastore_info speex_datastore = {
138  .type = "speex",
139  .destroy = destroy_callback
140 };
141 
142 static int speex_callback(struct ast_audiohook *audiohook, struct ast_channel *chan, struct ast_frame *frame, enum ast_audiohook_direction direction)
143 {
144  struct ast_datastore *datastore = NULL;
145  struct speex_direction_info *sdi = NULL;
146  struct speex_info *si = NULL;
147  char source[80];
148 
149  /* If the audiohook is stopping it means the channel is shutting down.... but we let the datastore destroy take care of it */
150  if (audiohook->status == AST_AUDIOHOOK_STATUS_DONE || frame->frametype != AST_FRAME_VOICE) {
151  return -1;
152  }
153 
154  /* We are called with chan already locked */
155  if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) {
156  return -1;
157  }
158 
159  si = datastore->data;
160 
161  sdi = (direction == AST_AUDIOHOOK_DIRECTION_READ) ? si->rx : si->tx;
162 
163  if (!sdi) {
164  return -1;
165  }
166 
167  if (sdi->samples != frame->samples) {
168  if (sdi->state) {
169  speex_preprocess_state_destroy(sdi->state);
170  }
171 
172  if (!(sdi->state = speex_preprocess_state_init((sdi->samples = frame->samples), 8000))) {
173  return -1;
174  }
175 
176  speex_preprocess_ctl(sdi->state, SPEEX_PREPROCESS_SET_AGC, &sdi->agc);
177 
178  if (sdi->agc) {
179  speex_preprocess_ctl(sdi->state, SPEEX_PREPROCESS_SET_AGC_LEVEL, &sdi->agclevel);
180  }
181 
182  speex_preprocess_ctl(sdi->state, SPEEX_PREPROCESS_SET_DENOISE, &sdi->denoise);
183  }
184 
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);
189  }
190  frame->src = ast_strdup(source);
191  frame->mallocd |= AST_MALLOCD_SRC;
192 
193  return 0;
194 }
195 
196 static int speex_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
197 {
198  struct ast_datastore *datastore = NULL;
199  struct speex_info *si = NULL;
200  struct speex_direction_info **sdi = NULL;
201  int is_new = 0;
202 
203  if (!chan) {
204  ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
205  return -1;
206  }
207 
208  if (strcasecmp(data, "rx") && strcasecmp(data, "tx")) {
209  ast_log(LOG_ERROR, "Invalid argument provided to the %s function\n", cmd);
210  return -1;
211  }
212 
213  ast_channel_lock(chan);
214  if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) {
215  ast_channel_unlock(chan);
216 
217  if (!(datastore = ast_datastore_alloc(&speex_datastore, NULL))) {
218  return 0;
219  }
220 
221  if (!(si = ast_calloc(1, sizeof(*si)))) {
222  ast_datastore_free(datastore);
223  return 0;
224  }
225 
228 
229  is_new = 1;
230  } else {
231  ast_channel_unlock(chan);
232  si = datastore->data;
233  }
234 
235  if (!strcasecmp(data, "rx")) {
236  sdi = &si->rx;
237  } else {
238  sdi = &si->tx;
239  }
240 
241  if (!*sdi) {
242  if (!(*sdi = ast_calloc(1, sizeof(**sdi)))) {
243  return 0;
244  }
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;
249  }
250 
251  if (!strcasecmp(cmd, "agc")) {
252  if (!sscanf(value, "%30f", &(*sdi)->agclevel))
253  (*sdi)->agclevel = ast_true(value) ? DEFAULT_AGC_LEVEL : 0.0;
254 
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;
259  }
260 
261  (*sdi)->agc = !!((*sdi)->agclevel);
262 
263  if ((*sdi)->state) {
264  speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_AGC, &(*sdi)->agc);
265  if ((*sdi)->agc) {
266  speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_AGC_LEVEL, &(*sdi)->agclevel);
267  }
268  }
269  } else if (!strcasecmp(cmd, "denoise")) {
270  (*sdi)->denoise = (ast_true(value) != 0);
271 
272  if ((*sdi)->state) {
273  speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_DENOISE, &(*sdi)->denoise);
274  }
275  }
276 
277  if (!(*sdi)->agc && !(*sdi)->denoise) {
278  if ((*sdi)->state)
279  speex_preprocess_state_destroy((*sdi)->state);
280 
281  ast_free(*sdi);
282  *sdi = NULL;
283  }
284 
285  if (!si->rx && !si->tx) {
286  if (is_new) {
287  is_new = 0;
288  } else {
289  ast_channel_lock(chan);
290  ast_channel_datastore_remove(chan, datastore);
291  ast_channel_unlock(chan);
292  ast_audiohook_remove(chan, &si->audiohook);
294  }
295 
296  ast_datastore_free(datastore);
297  }
298 
299  if (is_new) {
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);
305  }
306 
307  return 0;
308 }
309 
310 static int speex_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
311 {
312  struct ast_datastore *datastore = NULL;
313  struct speex_info *si = NULL;
314  struct speex_direction_info *sdi = NULL;
315 
316  if (!chan) {
317  ast_log(LOG_ERROR, "%s cannot be used without a channel!\n", cmd);
318  return -1;
319  }
320 
321  ast_channel_lock(chan);
322  if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) {
323  ast_channel_unlock(chan);
324  return -1;
325  }
326  ast_channel_unlock(chan);
327 
328  si = datastore->data;
329 
330  if (!strcasecmp(data, "tx"))
331  sdi = si->tx;
332  else if (!strcasecmp(data, "rx"))
333  sdi = si->rx;
334  else {
335  ast_log(LOG_ERROR, "%s(%s) must either \"tx\" or \"rx\"\n", cmd, data);
336  return -1;
337  }
338 
339  if (!strcasecmp(cmd, "agc"))
340  snprintf(buf, len, "%.01f", sdi ? sdi->agclevel : 0.0);
341  else
342  snprintf(buf, len, "%d", sdi ? sdi->denoise : 0);
343 
344  return 0;
345 }
346 
348  .name = "AGC",
349  .write = speex_write,
350  .read = speex_read,
351  .read_max = 22,
352 };
353 
355  .name = "DENOISE",
356  .write = speex_write,
357  .read = speex_read,
358  .read_max = 22,
359 };
360 
361 static int unload_module(void)
362 {
363  ast_custom_function_unregister(&agc_function);
364  ast_custom_function_unregister(&denoise_function);
365  return 0;
366 }
367 
368 static int load_module(void)
369 {
370  if (ast_custom_function_register(&agc_function)) {
372  }
373 
374  if (ast_custom_function_register(&denoise_function)) {
375  ast_custom_function_unregister(&agc_function);
377  }
378 
380 }
381 
382 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Noise reduction and Automatic Gain Control (AGC)");
const char * type
Definition: datastore.h:32
#define ast_channel_lock(chan)
Definition: channel.h:2466
Main Channel structure associated with a channel.
Definition: channel.h:742
#define AST_MODULE_INFO_STANDARD(keystr, desc)
Definition: module.h:396
Asterisk main include file. File version handling, generic pbx functions.
#define ast_strdup(a)
Definition: astmm.h:109
void * ptr
Definition: frame.h:160
int ast_audiohook_init(struct ast_audiohook *audiohook, enum ast_audiohook_type type, const char *source)
Initialize an audiohook structure.
Definition: audiohook.c:64
#define LOG_WARNING
Definition: logger.h:144
Audiohooks Architecture.
static int load_module(void)
Definition: func_speex.c:368
int ast_audiohook_remove(struct ast_channel *chan, struct ast_audiohook *audiohook)
Remove an audiohook from a specified channel.
Definition: audiohook.c:541
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:142
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:348
static int speex_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition: func_speex.c:310
Structure for a data store object.
Definition: datastore.h:54
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:2604
int value
Definition: syslog.c:39
int ast_audiohook_destroy(struct ast_audiohook *audiohook)
Destroys an audiohook structure.
Definition: audiohook.c:96
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
Definition: pbx.c:3814
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
Definition: datastore.c:65
Utility functions.
ast_audiohook_manipulate_callback manipulate_callback
Definition: audiohook.h:116
struct ast_audiohook audiohook
Definition: func_speex.c:108
General Asterisk PBX channel definitions.
const char * src
Definition: frame.h:158
static struct ast_custom_function denoise_function
Definition: func_speex.c:354
Data structure associated with a custom dialplan function.
Definition: pbx.h:95
#define AST_MALLOCD_SRC
Definition: frame.h:212
Core PBX routines and definitions.
int ast_audiohook_detach(struct ast_audiohook *audiohook)
Detach audiohook from channel.
Definition: audiohook.c:401
static void destroy_callback(void *data)
Definition: func_speex.c:112
#define LOG_ERROR
Definition: logger.h:155
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is &quot;true&quot;. This function checks to see whether a string passed to it is an indication of an &quot;true&quot; value. It checks to see if the string is &quot;yes&quot;, &quot;true&quot;, &quot;y&quot;, &quot;t&quot;, &quot;on&quot; or &quot;1&quot;.
Definition: utils.c:1533
struct speex_direction_info * tx
Definition: func_speex.c:109
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
void ast_log(int level, const char *file, int line, const char *function, const char *fmt,...)
Used for sending a log message This is the standard logger function. Probably the only way you will i...
Definition: logger.c:1207
struct ast_datastore * ast_datastore_alloc(const struct ast_datastore_info *info, const char *uid)
Definition: datastore.c:98
#define DEFAULT_AGC_LEVEL
Definition: func_speex.c:51
#define ast_channel_unlock(chan)
Definition: channel.h:2467
#define ast_free(a)
Definition: astmm.h:97
static struct ast_custom_function agc_function
Definition: func_speex.c:347
if(yyss+yystacksize-1<=yyssp)
Definition: ast_expr2.c:1874
ast_audiohook_direction
Definition: audiohook.h:49
static int unload_module(void)
Definition: func_speex.c:361
int mallocd
Definition: frame.h:152
void * data
Definition: datastore.h:56
#define ast_calloc(a, b)
Definition: astmm.h:82
struct speex_direction_info * rx
Definition: func_speex.c:109
Data structure associated with a single frame of data.
Definition: frame.h:142
const char * name
Definition: pbx.h:96
enum ast_audiohook_status status
Definition: audiohook.h:107
enum ast_frame_type frametype
Definition: frame.h:144
static int speex_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
Definition: func_speex.c:196
static struct ast_datastore_info speex_datastore
Definition: func_speex.c:137
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:38
Asterisk module definitions.
union ast_frame::@172 data
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2590
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:2599
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1164
#define ASTERISK_FILE_VERSION(file, version)
Register/unregister a source code file with the core.
Definition: asterisk.h:180
int samples
Definition: frame.h:150