00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014 #include <asterisk.h>
00015
00016 #include <stdio.h>
00017 #include <asterisk/file.h>
00018 #include <asterisk/logger.h>
00019 #include <asterisk/channel.h>
00020 #include <asterisk/pbx.h>
00021 #include <asterisk/module.h>
00022 #include <asterisk/lock.h>
00023 #include <stdlib.h>
00024 #include <unistd.h>
00025 #include <string.h>
00026 #include <math.h>
00027
00028 #ifndef PI
00029 #define PI (4*atan(1))
00030 #endif
00031
00032 static char *app = "Mwanalyze";
00033 static char *synopsis = "Spectral analyses based on a given frequency (slin)";
00034 static char *tdescription =
00035 "Mwanalyze(frequency|timeslice|duration|treshold): Analyzes audio stream for tone of given frequency.\n"
00036 "It computes the amplitude, the average ripple per timeslice, and the number of\n"
00037 "timeslices with ripple greater then given treshold.\n"
00038 "frequency is an integer divisible by 4 read as divider of 8 kHz (thus max frequency is 2 kHz).\n"
00039 "timeslice is an integer read in units of 1/8000 sec.\n"
00040 "duration is an integer read in units of timeslice.\n"
00041 "treshold is an integer meaning units of slin.\n"
00042 "Example: Mwanalyze(8|8000|60|328) will examine a 1000 Hz tone for a minute and\n"
00043 "reporting e.g. how many seconds had a ripple of more then 1% of slin's fullscale.\n" "When application is terminated, read channel variables mwa_ampitude, mwa_ripple, and mwa_bad_timeslices!\n";
00044
00045 static int mwanalyze_exec(struct ast_channel *chan, void *data)
00046 {
00047 int res = 0;
00048 struct ast_module_user *u;
00049 char *args, *cp, *cp0, linebuf[320];
00050 unsigned int frequency = 0, timeslice = 0, duration = 0, j;
00051 int i, v, treshold = 0;
00052
00053 int original_read_fmt;
00054 int original_write_fmt;
00055 struct ast_frame *inf = NULL;
00056 struct ast_frame outf;
00057 unsigned char waste[AST_FRIENDLY_OFFSET];
00058 short buf[640];
00059 unsigned int indexp, len, ts_index, ts_offset, phase;
00060 short sin_x, cos_x, val;
00061 long long sum_sin, sum_cos, total_deviation = 0, sum_sin0 = 0, sum_cos0 = 0;
00062 int total_samples, *periods_in_timeslice, total_bad_timeslices;
00063 double coeff_sin, coeff_cos, amplitude, timeslice_ripple, total_ripple, modell_val, avg_val;
00064 short *tone, *lastperiod;
00065 long long *avg_period;
00066
00067
00068
00069
00070 if ((data == NULL) || (!((char *) data)[0])) {
00071 ast_log(LOG_WARNING, "Mwanalyze needs arguments frequency, timeslice, duration and treshold.\n");
00072 return -1;
00073 }
00074
00075 if ((args = (char *) malloc(strlen((char *) data))) == NULL) {
00076 ast_log(LOG_WARNING, "Mwanalyze cannot allocate enough memory.\n");
00077 return -1;
00078 }
00079
00080
00081 strcpy(args, (char *) data);
00082 cp = args;
00083 for (i = 0; i < 4; i++) {
00084 cp0 = cp;
00085 cp = strchr(cp, '|');
00086 if (cp) {
00087 *cp++ = 0;
00088 } else if (i < 3) {
00089 ast_log(LOG_WARNING, "Mwanalyze needs arguments frequency, timeslice, duration and treshold.\n");
00090 free(args);
00091 return -1;
00092 }
00093
00094 if (sscanf(cp0, "%d", &v) != 1) {
00095 ast_log(LOG_WARNING, "Mwanalyze needs only integers as arguments.\n");
00096 free(args);
00097 return -1;
00098 }
00099
00100 switch (i) {
00101 case 0:
00102 frequency = v;
00103 case 1:
00104 timeslice = v;
00105 case 2:
00106 duration = v;
00107 case 3:
00108 treshold = v;
00109 }
00110 }
00111
00112
00113 if (frequency % 4)
00114 frequency--;
00115 if (frequency % 4)
00116 frequency--;
00117 if (frequency % 4)
00118 frequency--;
00119 if (frequency < 4) {
00120 frequency = 4;
00121 }
00122 if (timeslice < frequency) {
00123 timeslice = frequency;
00124 }
00125 if (duration < 1) {
00126 duration = 1;
00127 }
00128 if (treshold < 1) {
00129 treshold = 1;
00130 }
00131
00132 if ((tone = (short *) malloc(frequency * sizeof(short))) == NULL) {
00133 ast_log(LOG_WARNING, "Mwanalyze cannot allocate enough memory.\n");
00134 free(args);
00135 return -1;
00136 }
00137 if ((lastperiod = (short *) malloc(frequency * sizeof(short))) == NULL) {
00138 ast_log(LOG_WARNING, "Mwanalyze cannot allocate enough memory.\n");
00139 free(tone);
00140 free(args);
00141 return -1;
00142 }
00143 if ((avg_period = (long long *) malloc(frequency * sizeof(long long))) == NULL) {
00144 ast_log(LOG_WARNING, "Mwanalyze cannot allocate enough memory.\n");
00145 free(lastperiod);
00146 free(tone);
00147 free(args);
00148 return -1;
00149 }
00150 if ((periods_in_timeslice = (int *) malloc(frequency * sizeof(int))) == NULL) {
00151 ast_log(LOG_WARNING, "Mwanalyze cannot allocate enough memory.\n");
00152 free(avg_period);
00153 free(lastperiod);
00154 free(tone);
00155 free(args);
00156 return -1;
00157 }
00158
00159 for (indexp = 0; indexp < frequency; indexp++) {
00160 tone[indexp] = (short) (sin((double) indexp / frequency * 2 * PI) * ((1 << 15) - 1));
00161 }
00162
00163 u = ast_module_user_add(chan);
00164
00165
00166 if ((!res) && (chan->_state != AST_STATE_UP)) {
00167 if ((res = ast_answer(chan)) < 0) {
00168 ast_log(LOG_WARNING, "Could not answer channel '%s'\n", chan->name);
00169 }
00170 }
00171
00172
00173 original_read_fmt = AST_FORMAT_SLINEAR;
00174 if ((!res) && ((original_read_fmt = chan->readformat) != AST_FORMAT_SLINEAR)) {
00175 if ((res = ast_set_read_format(chan, AST_FORMAT_SLINEAR)) < 0) {
00176 ast_log(LOG_WARNING, "Unable to set to linear read mode, giving up.\n");
00177 }
00178 }
00179 original_write_fmt = AST_FORMAT_SLINEAR;
00180 if ((!res) && ((original_write_fmt = chan->writeformat) != AST_FORMAT_SLINEAR)) {
00181 if ((res = ast_set_write_format(chan, AST_FORMAT_SLINEAR)) < 0) {
00182 ast_log(LOG_WARNING, "Unable to set to linear write mode, giving up.\n");
00183 }
00184 }
00185
00186
00187 ts_index = 0;
00188 ts_offset = 0;
00189 phase = 0;
00190 sum_sin = sum_cos = 0.0;
00191 total_samples = 0;
00192 total_ripple = 0.0;
00193 total_bad_timeslices = 0;
00194
00195 while ((ts_index < duration) && (!res) && (ast_waitfor(chan, -1) > -1)) {
00196 if ((inf = ast_read(chan)) == NULL) {
00197
00198 res = -1;
00199 break;
00200 }
00201
00202 if (inf->frametype == AST_FRAME_VOICE) {
00203 len = inf->samples;
00204 if (len > sizeof(buf)) {
00205 ast_log(LOG_WARNING, "Can only exchange %d bytes.\n", (int) sizeof(buf));
00206 len = sizeof(buf);
00207 }
00208
00209
00210 for (i = 0; i < (int) len; i++) {
00211
00212 val = ((short *) inf->data)[i];
00213 sin_x = tone[phase];
00214 cos_x = tone[(phase < 3 * frequency / 4) ? (phase + frequency / 4) : (phase - 3 * frequency / 4)];
00215 sum_sin += sin_x * val;
00216 sum_cos += cos_x * val;
00217
00218
00219 if (ts_offset == 0) {
00220 total_deviation = 0;
00221 sum_sin0 = sum_sin;
00222 sum_cos0 = sum_cos;
00223 }
00224 if (ts_offset >= frequency) {
00225 total_deviation += (val >= lastperiod[phase]) ? (val - lastperiod[phase]) : (lastperiod[phase] - val);
00226 } else {
00227 avg_period[phase] = 0;
00228 periods_in_timeslice[phase] = 0;
00229 }
00230 lastperiod[phase] = val;
00231 avg_period[phase] += val;
00232 periods_in_timeslice[phase]++;
00233
00234
00235
00236 if (++ts_offset == timeslice) {
00237 timeslice_ripple = (double) total_deviation / 2 / timeslice;
00238 coeff_sin = (sum_sin - sum_sin0) * 2.0 / ((1 << 15) - 1) / timeslice;
00239 coeff_cos = (sum_cos - sum_cos0) * 2.0 / ((1 << 15) - 1) / timeslice;
00240
00241 total_deviation = 0;
00242 for (j = 0; j < frequency; j++) {
00243 sin_x = tone[j];
00244 cos_x = tone[(j < 3 * frequency / 4) ? (j + frequency / 4) : (j - 3 * frequency / 4)];
00245 modell_val = coeff_sin * sin_x / ((1 << 15) - 1) + coeff_cos * cos_x / ((1 << 15) - 1);
00246 avg_val = (double) avg_period[j] / periods_in_timeslice[j];
00247 total_deviation += (long long) ((avg_val >= modell_val) ? (avg_val - modell_val) : (modell_val - avg_val));
00248 }
00249 timeslice_ripple += (double) total_deviation / frequency;
00250 total_ripple += timeslice_ripple;
00251 if (timeslice_ripple > treshold) {
00252 total_bad_timeslices++;
00253 }
00254
00255 ts_offset = 0;
00256 ts_index++;
00257 }
00258
00259 if (++phase == frequency) {
00260 phase = 0;
00261 }
00262 total_samples++;
00263 }
00264
00265 waste[0] = 0;
00266 outf.frametype = AST_FRAME_VOICE;
00267 outf.subclass = AST_FORMAT_SLINEAR;
00268 outf.offset = AST_FRIENDLY_OFFSET;
00269 outf.mallocd = 0;
00270 outf.data = buf;
00271 outf.datalen = len * sizeof(short);
00272 outf.samples = len;
00273 outf.src = "app_mwanalyze";
00274 outf.delivery.tv_sec = 0;
00275 outf.delivery.tv_usec = 0;
00276 for (i = 0; i < (int) len; i++) {
00277 if (indexp == frequency) {
00278 indexp = 0;
00279 }
00280 buf[i] = tone[indexp++];
00281 }
00282
00283 if ((res = ast_write(chan, &outf)) < 0) {
00284 ast_log(LOG_WARNING, "Failed to write frame to '%s'.\n", chan->name);
00285 }
00286
00287 }
00288
00289 ast_frfree(inf);
00290 }
00291
00292
00293
00294 if (original_read_fmt != AST_FORMAT_SLINEAR) {
00295 if (ast_set_read_format(chan, original_read_fmt) < 0) {
00296 ast_log(LOG_WARNING, "Unable to restore read mode.\n");
00297 }
00298 }
00299 if (original_write_fmt != AST_FORMAT_SLINEAR) {
00300 if (ast_set_write_format(chan, original_write_fmt) < 0) {
00301 ast_log(LOG_WARNING, "Unable to restore write mode.\n");
00302 }
00303 }
00304
00305
00306 coeff_sin = (total_samples) ? (sum_sin * 2.0 / ((1 << 15) - 1) / total_samples) : 0.0;
00307 coeff_cos = (total_samples) ? (sum_cos * 2.0 / ((1 << 15) - 1) / total_samples) : 0.0;
00308 amplitude = sqrt(coeff_sin * coeff_sin + coeff_cos * coeff_cos);
00309
00310 sprintf(linebuf, "%lg", amplitude);
00311 pbx_builtin_setvar_helper(chan, "mwa_amplitude", linebuf);
00312 sprintf(linebuf, "%lg", (duration) ? (total_ripple / duration) : 0.0);
00313 pbx_builtin_setvar_helper(chan, "mwa_ripple", linebuf);
00314 sprintf(linebuf, "%d", total_bad_timeslices);
00315 pbx_builtin_setvar_helper(chan, "mwa_bad_timeslices", linebuf);
00316
00317 ast_module_user_remove(u);
00318 free(periods_in_timeslice);
00319 free(avg_period);
00320 free(lastperiod);
00321 free(tone);
00322 free(args);
00323 return res;
00324 }
00325
00326
00327 static int unload_module(void)
00328 {
00329 int res;
00330
00331 res = ast_unregister_application(app);
00332
00333 ast_module_user_hangup_all();
00334
00335 return res;
00336 }
00337
00338 static int load_module(void)
00339 {
00340 return ast_register_application(app, mwanalyze_exec, synopsis, tdescription);
00341 }
00342
00343 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Digital Analyzer for Milliwatt Application (slin)");