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