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