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
00039
00040 #include "asterisk.h"
00041
00042 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 411314 $")
00043
00044 #include <speex/speex_preprocess.h>
00045 #include "asterisk/module.h"
00046 #include "asterisk/channel.h"
00047 #include "asterisk/pbx.h"
00048 #include "asterisk/utils.h"
00049 #include "asterisk/audiohook.h"
00050
00051 #define DEFAULT_AGC_LEVEL 8000.0
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099 struct speex_direction_info {
00100 SpeexPreprocessState *state;
00101 int agc;
00102 int denoise;
00103 int samples;
00104 float agclevel;
00105 };
00106
00107 struct speex_info {
00108 struct ast_audiohook audiohook;
00109 int lastrate;
00110 struct speex_direction_info *tx, *rx;
00111 };
00112
00113 static void destroy_callback(void *data)
00114 {
00115 struct speex_info *si = data;
00116
00117 ast_audiohook_destroy(&si->audiohook);
00118
00119 if (si->rx && si->rx->state) {
00120 speex_preprocess_state_destroy(si->rx->state);
00121 }
00122
00123 if (si->tx && si->tx->state) {
00124 speex_preprocess_state_destroy(si->tx->state);
00125 }
00126
00127 if (si->rx) {
00128 ast_free(si->rx);
00129 }
00130
00131 if (si->tx) {
00132 ast_free(si->tx);
00133 }
00134
00135 ast_free(data);
00136 };
00137
00138 static const struct ast_datastore_info speex_datastore = {
00139 .type = "speex",
00140 .destroy = destroy_callback
00141 };
00142
00143 static int speex_callback(struct ast_audiohook *audiohook, struct ast_channel *chan, struct ast_frame *frame, enum ast_audiohook_direction direction)
00144 {
00145 struct ast_datastore *datastore = NULL;
00146 struct speex_direction_info *sdi = NULL;
00147 struct speex_info *si = NULL;
00148 char source[80];
00149
00150
00151 if (audiohook->status == AST_AUDIOHOOK_STATUS_DONE || frame->frametype != AST_FRAME_VOICE) {
00152 return -1;
00153 }
00154
00155
00156 if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) {
00157 return -1;
00158 }
00159
00160 si = datastore->data;
00161
00162 sdi = (direction == AST_AUDIOHOOK_DIRECTION_READ) ? si->rx : si->tx;
00163
00164 if (!sdi) {
00165 return -1;
00166 }
00167
00168 if ((sdi->samples != frame->samples) || (ast_format_rate(&frame->subclass.format) != si->lastrate)) {
00169 si->lastrate = ast_format_rate(&frame->subclass.format);
00170 if (sdi->state) {
00171 speex_preprocess_state_destroy(sdi->state);
00172 }
00173
00174 if (!(sdi->state = speex_preprocess_state_init((sdi->samples = frame->samples), si->lastrate))) {
00175 return -1;
00176 }
00177
00178 speex_preprocess_ctl(sdi->state, SPEEX_PREPROCESS_SET_AGC, &sdi->agc);
00179
00180 if (sdi->agc) {
00181 speex_preprocess_ctl(sdi->state, SPEEX_PREPROCESS_SET_AGC_LEVEL, &sdi->agclevel);
00182 }
00183
00184 speex_preprocess_ctl(sdi->state, SPEEX_PREPROCESS_SET_DENOISE, &sdi->denoise);
00185 }
00186
00187 speex_preprocess(sdi->state, frame->data.ptr, NULL);
00188 snprintf(source, sizeof(source), "%s/speex", frame->src);
00189 if (frame->mallocd & AST_MALLOCD_SRC) {
00190 ast_free((char *) frame->src);
00191 }
00192 frame->src = ast_strdup(source);
00193 frame->mallocd |= AST_MALLOCD_SRC;
00194
00195 return 0;
00196 }
00197
00198 static int speex_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
00199 {
00200 struct ast_datastore *datastore = NULL;
00201 struct speex_info *si = NULL;
00202 struct speex_direction_info **sdi = NULL;
00203 int is_new = 0;
00204
00205 if (!chan) {
00206 ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
00207 return -1;
00208 }
00209
00210 if (strcasecmp(data, "rx") && strcasecmp(data, "tx")) {
00211 ast_log(LOG_ERROR, "Invalid argument provided to the %s function\n", cmd);
00212 return -1;
00213 }
00214
00215 ast_channel_lock(chan);
00216 if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) {
00217 ast_channel_unlock(chan);
00218
00219 if (!(datastore = ast_datastore_alloc(&speex_datastore, NULL))) {
00220 return 0;
00221 }
00222
00223 if (!(si = ast_calloc(1, sizeof(*si)))) {
00224 ast_datastore_free(datastore);
00225 return 0;
00226 }
00227
00228 ast_audiohook_init(&si->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "speex", AST_AUDIOHOOK_MANIPULATE_ALL_RATES);
00229 si->audiohook.manipulate_callback = speex_callback;
00230 si->lastrate = 8000;
00231 is_new = 1;
00232 } else {
00233 ast_channel_unlock(chan);
00234 si = datastore->data;
00235 }
00236
00237 if (!strcasecmp(data, "rx")) {
00238 sdi = &si->rx;
00239 } else {
00240 sdi = &si->tx;
00241 }
00242
00243 if (!*sdi) {
00244 if (!(*sdi = ast_calloc(1, sizeof(**sdi)))) {
00245 return 0;
00246 }
00247
00248
00249
00250 (*sdi)->samples = -1;
00251 }
00252
00253 if (!strcasecmp(cmd, "agc")) {
00254 if (!sscanf(value, "%30f", &(*sdi)->agclevel))
00255 (*sdi)->agclevel = ast_true(value) ? DEFAULT_AGC_LEVEL : 0.0;
00256
00257 if ((*sdi)->agclevel > 32768.0) {
00258 ast_log(LOG_WARNING, "AGC(%s)=%.01f is greater than 32768... setting to 32768 instead\n",
00259 ((*sdi == si->rx) ? "rx" : "tx"), (*sdi)->agclevel);
00260 (*sdi)->agclevel = 32768.0;
00261 }
00262
00263 (*sdi)->agc = !!((*sdi)->agclevel);
00264
00265 if ((*sdi)->state) {
00266 speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_AGC, &(*sdi)->agc);
00267 if ((*sdi)->agc) {
00268 speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_AGC_LEVEL, &(*sdi)->agclevel);
00269 }
00270 }
00271 } else if (!strcasecmp(cmd, "denoise")) {
00272 (*sdi)->denoise = (ast_true(value) != 0);
00273
00274 if ((*sdi)->state) {
00275 speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_DENOISE, &(*sdi)->denoise);
00276 }
00277 }
00278
00279 if (!(*sdi)->agc && !(*sdi)->denoise) {
00280 if ((*sdi)->state)
00281 speex_preprocess_state_destroy((*sdi)->state);
00282
00283 ast_free(*sdi);
00284 *sdi = NULL;
00285 }
00286
00287 if (!si->rx && !si->tx) {
00288 if (is_new) {
00289 is_new = 0;
00290 } else {
00291 ast_channel_lock(chan);
00292 ast_channel_datastore_remove(chan, datastore);
00293 ast_channel_unlock(chan);
00294 ast_audiohook_remove(chan, &si->audiohook);
00295 ast_audiohook_detach(&si->audiohook);
00296 }
00297
00298 ast_datastore_free(datastore);
00299 }
00300
00301 if (is_new) {
00302 datastore->data = si;
00303 ast_channel_lock(chan);
00304 ast_channel_datastore_add(chan, datastore);
00305 ast_channel_unlock(chan);
00306 ast_audiohook_attach(chan, &si->audiohook);
00307 }
00308
00309 return 0;
00310 }
00311
00312 static int speex_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00313 {
00314 struct ast_datastore *datastore = NULL;
00315 struct speex_info *si = NULL;
00316 struct speex_direction_info *sdi = NULL;
00317
00318 if (!chan) {
00319 ast_log(LOG_ERROR, "%s cannot be used without a channel!\n", cmd);
00320 return -1;
00321 }
00322
00323 ast_channel_lock(chan);
00324 if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) {
00325 ast_channel_unlock(chan);
00326 return -1;
00327 }
00328 ast_channel_unlock(chan);
00329
00330 si = datastore->data;
00331
00332 if (!strcasecmp(data, "tx"))
00333 sdi = si->tx;
00334 else if (!strcasecmp(data, "rx"))
00335 sdi = si->rx;
00336 else {
00337 ast_log(LOG_ERROR, "%s(%s) must either \"tx\" or \"rx\"\n", cmd, data);
00338 return -1;
00339 }
00340
00341 if (!strcasecmp(cmd, "agc"))
00342 snprintf(buf, len, "%.01f", sdi ? sdi->agclevel : 0.0);
00343 else
00344 snprintf(buf, len, "%d", sdi ? sdi->denoise : 0);
00345
00346 return 0;
00347 }
00348
00349 static struct ast_custom_function agc_function = {
00350 .name = "AGC",
00351 .write = speex_write,
00352 .read = speex_read,
00353 .read_max = 22,
00354 };
00355
00356 static struct ast_custom_function denoise_function = {
00357 .name = "DENOISE",
00358 .write = speex_write,
00359 .read = speex_read,
00360 .read_max = 22,
00361 };
00362
00363 static int unload_module(void)
00364 {
00365 ast_custom_function_unregister(&agc_function);
00366 ast_custom_function_unregister(&denoise_function);
00367 return 0;
00368 }
00369
00370 static int load_module(void)
00371 {
00372 if (ast_custom_function_register(&agc_function)) {
00373 return AST_MODULE_LOAD_DECLINE;
00374 }
00375
00376 if (ast_custom_function_register(&denoise_function)) {
00377 ast_custom_function_unregister(&agc_function);
00378 return AST_MODULE_LOAD_DECLINE;
00379 }
00380
00381 return AST_MODULE_LOAD_SUCCESS;
00382 }
00383
00384 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Noise reduction and Automatic Gain Control (AGC)");