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: 411313 $")
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 (!chan) {
00204 ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
00205 return -1;
00206 }
00207
00208 if (strcasecmp(data, "rx") && strcasecmp(data, "tx")) {
00209 ast_log(LOG_ERROR, "Invalid argument provided to the %s function\n", cmd);
00210 return -1;
00211 }
00212
00213 ast_channel_lock(chan);
00214 if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) {
00215 ast_channel_unlock(chan);
00216
00217 if (!(datastore = ast_datastore_alloc(&speex_datastore, NULL))) {
00218 return 0;
00219 }
00220
00221 if (!(si = ast_calloc(1, sizeof(*si)))) {
00222 ast_datastore_free(datastore);
00223 return 0;
00224 }
00225
00226 ast_audiohook_init(&si->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "speex");
00227 si->audiohook.manipulate_callback = speex_callback;
00228
00229 is_new = 1;
00230 } else {
00231 ast_channel_unlock(chan);
00232 si = datastore->data;
00233 }
00234
00235 if (!strcasecmp(data, "rx")) {
00236 sdi = &si->rx;
00237 } else {
00238 sdi = &si->tx;
00239 }
00240
00241 if (!*sdi) {
00242 if (!(*sdi = ast_calloc(1, sizeof(**sdi)))) {
00243 return 0;
00244 }
00245
00246
00247
00248 (*sdi)->samples = -1;
00249 }
00250
00251 if (!strcasecmp(cmd, "agc")) {
00252 if (!sscanf(value, "%30f", &(*sdi)->agclevel))
00253 (*sdi)->agclevel = ast_true(value) ? DEFAULT_AGC_LEVEL : 0.0;
00254
00255 if ((*sdi)->agclevel > 32768.0) {
00256 ast_log(LOG_WARNING, "AGC(%s)=%.01f is greater than 32768... setting to 32768 instead\n",
00257 ((*sdi == si->rx) ? "rx" : "tx"), (*sdi)->agclevel);
00258 (*sdi)->agclevel = 32768.0;
00259 }
00260
00261 (*sdi)->agc = !!((*sdi)->agclevel);
00262
00263 if ((*sdi)->state) {
00264 speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_AGC, &(*sdi)->agc);
00265 if ((*sdi)->agc) {
00266 speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_AGC_LEVEL, &(*sdi)->agclevel);
00267 }
00268 }
00269 } else if (!strcasecmp(cmd, "denoise")) {
00270 (*sdi)->denoise = (ast_true(value) != 0);
00271
00272 if ((*sdi)->state) {
00273 speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_DENOISE, &(*sdi)->denoise);
00274 }
00275 }
00276
00277 if (!(*sdi)->agc && !(*sdi)->denoise) {
00278 if ((*sdi)->state)
00279 speex_preprocess_state_destroy((*sdi)->state);
00280
00281 ast_free(*sdi);
00282 *sdi = NULL;
00283 }
00284
00285 if (!si->rx && !si->tx) {
00286 if (is_new) {
00287 is_new = 0;
00288 } else {
00289 ast_channel_lock(chan);
00290 ast_channel_datastore_remove(chan, datastore);
00291 ast_channel_unlock(chan);
00292 ast_audiohook_remove(chan, &si->audiohook);
00293 ast_audiohook_detach(&si->audiohook);
00294 }
00295
00296 ast_datastore_free(datastore);
00297 }
00298
00299 if (is_new) {
00300 datastore->data = si;
00301 ast_channel_lock(chan);
00302 ast_channel_datastore_add(chan, datastore);
00303 ast_channel_unlock(chan);
00304 ast_audiohook_attach(chan, &si->audiohook);
00305 }
00306
00307 return 0;
00308 }
00309
00310 static int speex_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
00311 {
00312 struct ast_datastore *datastore = NULL;
00313 struct speex_info *si = NULL;
00314 struct speex_direction_info *sdi = NULL;
00315
00316 if (!chan) {
00317 ast_log(LOG_ERROR, "%s cannot be used without a channel!\n", cmd);
00318 return -1;
00319 }
00320
00321 ast_channel_lock(chan);
00322 if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) {
00323 ast_channel_unlock(chan);
00324 return -1;
00325 }
00326 ast_channel_unlock(chan);
00327
00328 si = datastore->data;
00329
00330 if (!strcasecmp(data, "tx"))
00331 sdi = si->tx;
00332 else if (!strcasecmp(data, "rx"))
00333 sdi = si->rx;
00334 else {
00335 ast_log(LOG_ERROR, "%s(%s) must either \"tx\" or \"rx\"\n", cmd, data);
00336 return -1;
00337 }
00338
00339 if (!strcasecmp(cmd, "agc"))
00340 snprintf(buf, len, "%.01f", sdi ? sdi->agclevel : 0.0);
00341 else
00342 snprintf(buf, len, "%d", sdi ? sdi->denoise : 0);
00343
00344 return 0;
00345 }
00346
00347 static struct ast_custom_function agc_function = {
00348 .name = "AGC",
00349 .write = speex_write,
00350 .read = speex_read,
00351 .read_max = 22,
00352 };
00353
00354 static struct ast_custom_function denoise_function = {
00355 .name = "DENOISE",
00356 .write = speex_write,
00357 .read = speex_read,
00358 .read_max = 22,
00359 };
00360
00361 static int unload_module(void)
00362 {
00363 ast_custom_function_unregister(&agc_function);
00364 ast_custom_function_unregister(&denoise_function);
00365 return 0;
00366 }
00367
00368 static int load_module(void)
00369 {
00370 if (ast_custom_function_register(&agc_function)) {
00371 return AST_MODULE_LOAD_DECLINE;
00372 }
00373
00374 if (ast_custom_function_register(&denoise_function)) {
00375 ast_custom_function_unregister(&agc_function);
00376 return AST_MODULE_LOAD_DECLINE;
00377 }
00378
00379 return AST_MODULE_LOAD_SUCCESS;
00380 }
00381
00382 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Noise reduction and Automatic Gain Control (AGC)");