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