00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 #include "asterisk.h"
00039
00040 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 135680 $")
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;
00053 int agc;
00054 int denoise;
00055 int samples;
00056 float agclevel;
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
00100
00101 if (audiohook->status == AST_AUDIOHOOK_STATUS_DONE || frame->frametype != AST_FRAME_VOICE) {
00102 return 0;
00103 }
00104
00105 ast_channel_lock(chan);
00106 if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) {
00107 ast_channel_unlock(chan);
00108 return 0;
00109 }
00110 ast_channel_unlock(chan);
00111
00112 si = datastore->data;
00113
00114 sdi = (direction == AST_AUDIOHOOK_DIRECTION_READ) ? si->rx : si->tx;
00115
00116 if (!sdi) {
00117 return 0;
00118 }
00119
00120 if (sdi->samples != frame->samples) {
00121 if (sdi->state) {
00122 speex_preprocess_state_destroy(sdi->state);
00123 }
00124
00125 if (!(sdi->state = speex_preprocess_state_init((sdi->samples = frame->samples), 8000))) {
00126 return -1;
00127 }
00128
00129 speex_preprocess_ctl(sdi->state, SPEEX_PREPROCESS_SET_AGC, &sdi->agc);
00130
00131 if (sdi->agc) {
00132 speex_preprocess_ctl(sdi->state, SPEEX_PREPROCESS_SET_AGC_LEVEL, &sdi->agclevel);
00133 }
00134
00135 speex_preprocess_ctl(sdi->state, SPEEX_PREPROCESS_SET_DENOISE, &sdi->denoise);
00136 }
00137
00138 speex_preprocess(sdi->state, frame->data.ptr, NULL);
00139
00140 return 0;
00141 }
00142
00143 static int speex_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
00144 {
00145 struct ast_datastore *datastore = NULL;
00146 struct speex_info *si = NULL;
00147 struct speex_direction_info **sdi = NULL;
00148 int is_new = 0;
00149
00150 ast_channel_lock(chan);
00151 if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) {
00152 ast_channel_unlock(chan);
00153
00154 if (!(datastore = ast_datastore_alloc(&speex_datastore, NULL))) {
00155 return 0;
00156 }
00157
00158 if (!(si = ast_calloc(1, sizeof(*si)))) {
00159 ast_datastore_free(datastore);
00160 return 0;
00161 }
00162
00163 ast_audiohook_init(&si->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "speex");
00164 si->audiohook.manipulate_callback = speex_callback;
00165
00166 is_new = 1;
00167 } else {
00168 ast_channel_unlock(chan);
00169 si = datastore->data;
00170 }
00171
00172 if (!strcasecmp(data, "rx")) {
00173 sdi = &si->rx;
00174 } else if (!strcasecmp(data, "tx")) {
00175 sdi = &si->tx;
00176 } else {
00177 ast_log(LOG_ERROR, "Invalid argument provided to the %s function\n", cmd);
00178
00179 if (is_new) {
00180 ast_datastore_free(datastore);
00181 return -1;
00182 }
00183 }
00184
00185 if (!*sdi) {
00186 if (!(*sdi = ast_calloc(1, sizeof(**sdi)))) {
00187 return 0;
00188 }
00189
00190
00191
00192 (*sdi)->samples = -1;
00193 }
00194
00195 if (!strcasecmp(cmd, "agc")) {
00196 if (!sscanf(value, "%f", &(*sdi)->agclevel))
00197 (*sdi)->agclevel = ast_true(value) ? DEFAULT_AGC_LEVEL : 0.0;
00198
00199 if ((*sdi)->agclevel > 32768.0) {
00200 ast_log(LOG_WARNING, "AGC(%s)=%.01f is greater than 32768... setting to 32768 instead\n",
00201 ((*sdi == si->rx) ? "rx" : "tx"), (*sdi)->agclevel);
00202 (*sdi)->agclevel = 32768.0;
00203 }
00204
00205 (*sdi)->agc = !!((*sdi)->agclevel);
00206
00207 if ((*sdi)->state) {
00208 speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_AGC, &(*sdi)->agc);
00209 if ((*sdi)->agc) {
00210 speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_AGC_LEVEL, &(*sdi)->agclevel);
00211 }
00212 }
00213 } else if (!strcasecmp(cmd, "denoise")) {
00214 (*sdi)->denoise = (ast_true(value) != 0);
00215
00216 if ((*sdi)->state) {
00217 speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_DENOISE, &(*sdi)->denoise);
00218 }
00219 }
00220
00221 if (!(*sdi)->agc && !(*sdi)->denoise) {
00222 if ((*sdi)->state)
00223 speex_preprocess_state_destroy((*sdi)->state);
00224
00225 ast_free(*sdi);
00226 *sdi = NULL;
00227 }
00228
00229 if (!si->rx && !si->tx) {
00230 if (is_new) {
00231 is_new = 0;
00232 } else {
00233 ast_channel_lock(chan);
00234 ast_channel_datastore_remove(chan, datastore);
00235 ast_channel_unlock(chan);
00236 ast_audiohook_remove(chan, &si->audiohook);
00237 ast_audiohook_detach(&si->audiohook);
00238 }
00239
00240 ast_datastore_free(datastore);
00241 }
00242
00243 if (is_new) {
00244 datastore->data = si;
00245 ast_channel_lock(chan);
00246 ast_channel_datastore_add(chan, datastore);
00247 ast_channel_unlock(chan);
00248 ast_audiohook_attach(chan, &si->audiohook);
00249 }
00250
00251 return 0;
00252 }
00253
00254 static int speex_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00255 {
00256 struct ast_datastore *datastore = NULL;
00257 struct speex_info *si = NULL;
00258 struct speex_direction_info *sdi = NULL;
00259
00260 if (!chan) {
00261 ast_log(LOG_ERROR, "%s cannot be used without a channel!\n", cmd);
00262 return -1;
00263 }
00264
00265 ast_channel_lock(chan);
00266 if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) {
00267 ast_channel_unlock(chan);
00268 return -1;
00269 }
00270 ast_channel_unlock(chan);
00271
00272 si = datastore->data;
00273
00274 if (!strcasecmp(data, "tx"))
00275 sdi = si->tx;
00276 else if (!strcasecmp(data, "rx"))
00277 sdi = si->rx;
00278 else {
00279 ast_log(LOG_ERROR, "%s(%s) must either \"tx\" or \"rx\"\n", cmd, data);
00280 return -1;
00281 }
00282
00283 if (!strcasecmp(cmd, "agc"))
00284 snprintf(buf, len, "%.01f", sdi ? sdi->agclevel : 0.0);
00285 else
00286 snprintf(buf, len, "%d", sdi ? sdi->denoise : 0);
00287
00288 return 0;
00289 }
00290
00291 static struct ast_custom_function agc_function = {
00292 .name = "AGC",
00293 .synopsis = "Apply automatic gain control to audio on a channel",
00294 .desc =
00295 " The AGC function will apply automatic gain control to audio on the channel\n"
00296 "that this function is executed on. Use rx for audio received from the channel\n"
00297 "and tx to apply AGC to the audio being sent to the channel. When using this\n"
00298 "function, you set a target audio level. It is primarily intended for use with\n"
00299 "analog lines, but could be useful for other channels, as well. The target volume\n"
00300 "is set with a number between 1 and 32768. Larger numbers are louder.\n"
00301 " Example Usage:\n"
00302 " Set(AGC(rx)=8000)\n"
00303 " Set(AGC(tx)=8000)\n"
00304 " Set(AGC(rx)=off)\n"
00305 " Set(AGC(tx)=off)\n"
00306 "",
00307 .write = speex_write,
00308 .read = speex_read
00309 };
00310
00311 static struct ast_custom_function denoise_function = {
00312 .name = "DENOISE",
00313 .synopsis = "Apply noise reduction to audio on a channel",
00314 .desc =
00315 " The DENOISE function will apply noise reduction to audio on the channel\n"
00316 "that this function is executed on. It is especially useful for noisy analog\n"
00317 "lines, especially when adjusting gains or using AGC. Use rx for audio\n"
00318 "received from the channel and tx to apply the filter to the audio being sent\n"
00319 "to the channel.\n"
00320 " Example Usage:\n"
00321 " Set(DENOISE(rx)=on)\n"
00322 " Set(DENOISE(tx)=on)\n"
00323 " Set(DENOISE(rx)=off)\n"
00324 " Set(DENOISE(tx)=off)\n"
00325 "",
00326 .write = speex_write,
00327 .read = speex_read
00328 };
00329
00330 static int unload_module(void)
00331 {
00332 ast_custom_function_unregister(&agc_function);
00333 ast_custom_function_unregister(&denoise_function);
00334 return 0;
00335 }
00336
00337 static int load_module(void)
00338 {
00339 if (ast_custom_function_register(&agc_function)) {
00340 return AST_MODULE_LOAD_DECLINE;
00341 }
00342
00343 if (ast_custom_function_register(&denoise_function)) {
00344 ast_custom_function_unregister(&agc_function);
00345 return AST_MODULE_LOAD_DECLINE;
00346 }
00347
00348 return AST_MODULE_LOAD_SUCCESS;
00349 }
00350
00351 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Noise reduction and Automatic Gain Control (AGC)");