1 /*
2 * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
3 * Copyright (C) 2008, Eric des Courtis <eric.des.courtis@benbria.com>
4 *
5 * Version: MPL 1.1
6 *
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
11 *
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
16 *
17 * Eric des Courtis <eric.des.courtis@benbria.com>
18 * Copyright (C) Benbria. All Rights Reserved.
19 *
20 * Contributor(s):
21 *
22 * Eric des Courtis <eric.des.courtis@benbria.com>
23 *
24 * Special thanks to the following companies for their help:
25 * - JohnnyVoIP
26 * - Magor Communications Corporation
27 *
28 * Special thanks to the following people for their help:
29 * - The FreeSWITCH Team
30 * - Matt Battig
31 * - Dean Swan
32 * - Lucas Cornelisse
33 * - Kevin Green
34 *
35 * mod_vmd.c -- Voicemail Detection Module
36 *
37 * This module detects voicemail beeps at any frequency in O(1) time.
38 *
39 */
40
41 #include <switch.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <math.h>
46 #ifdef WIN32
47 #include <float.h>
48 #define ISNAN(x) (!!(_isnan(x)))
49 #else
50 #define ISNAN(x) (isnan(x))
51 #endif
52
53 /*! Number of points for beep detection. */
54 #define POINTS 32
55
56 /*! Number of valid points required for beep detection. */
57 #define VALID 22
58
59 /*! Maximum number of invalid points to declare beep has stopped. */
60 #define MAX_CHIRP 22
61
62 /*! Minimum time for a beep. */
63 #define MIN_TIME 8000
64
65 /*! Minimum amplitude of the signal. */
66 #define MIN_AMPL 0.10
67
68 /*! Minimum beep frequency. */
69 #define MIN_FREQ (600)
70
71 /*! Maximum beep frequency. */
72 #define MAX_FREQ (1100)
73
74 /*! \brief Helper for amplitude calculation
75 *
76 * The function is defined as \f$\psi{(x)} = {x^2_1} - {x_2} {x_0}\f$
77 *
78 * @author Eric des Courtis
79 * @param x An array of 3 or more samples.
80 * @return The value of \f$\psi{(x)}\f$.
81 */
82 #define PSI(x) (x[1]*x[1]-x[2]*x[0])
83
84 /*! Sample rate NOTE: this should be dynamic in the future. */
85 #define F (8000)
86
87 /*! \brief Conversion of frequency to Hz
88 *
89 * \f$F = \frac{f}{{2}{\pi}}\f$
90 */
91 #define TO_HZ(f) ((F * f) / (2.0 * M_PI))
92
93 /* Number of points in discreet energy separation. */
94 #define P (5)
95
96 /* Maximum signed value of int16_t
97 * DEPRECATED */
98 #define ADJUST (32768)
99 /* Same as above times two
100 * DEPRECATED */
101 #define ADJUST_MAX (65536)
102
103 /*! Discreet energy separation tolerance to error. */
104 #define TOLERANCE (0.20)
105
106 /*! Maximum value within tolerance. */
107 #define TOLERANCE_T(m) (m + (m * TOLERANCE))
108
109 /*! Minimum value within tolerance. */
110 #define TOLERANCE_B(m) (m - (m * TOLERANCE))
111
112 /*! Syntax of the API call. */
113 #define VMD_SYNTAX "<uuid> <command>"
114
115 /*! Number of expected parameters in api call. */
116 #define VMD_PARAMS 2
117
118 /*! FreeSWITCH CUSTOM event type. */
119 #define VMD_EVENT_BEEP "vmd::beep"
120
121 /* Prototypes */
122 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_vmd_shutdown);
123 SWITCH_STANDARD_API(vmd_api_main);
124
125 SWITCH_MODULE_LOAD_FUNCTION(mod_vmd_load);
126 SWITCH_MODULE_DEFINITION(mod_vmd, mod_vmd_load, NULL, NULL);
127 SWITCH_STANDARD_APP(vmd_start_function);
128
129 /*! Type that holds state information about the beep. */
130 typedef enum vmd_state {
131 BEEP_DETECTED, BEEP_NOT_DETECTED
132 } vmd_state_t;
133
134 /*! Type that holds data for 5 points of discreet energy separation */
135 typedef struct vmd_point {
136 double freq;
137 double ampl;
138 } vmd_point_t;
139
140 /*! Type that holds codec information. */
141 typedef struct vmd_codec_info {
142 /*! The sampling rate of the audio stream. */
143 int rate;
144 /*! The number of channels. */
145 int channels;
146 } vmd_codec_info_t;
147
148 /*! Type that holds session information pertinent to the vmd module. */
149 typedef struct vmd_session_info {
150 /*! State of the session. */
151 vmd_state_t state;
152 /*! Snapshot of DESA samples. */
153 vmd_point_t points[POINTS+1];
154 /*! Internal FreeSWITCH session. */
155 switch_core_session_t *session;
156 /*! Codec information for the session. */
157 vmd_codec_info_t vmd_codec;
158 /*! Current position in the snapshot. */
159 unsigned int pos;
160 /*! Frequency aproximation of a detected beep. */
161 double beep_freq;
162 /*! A count of how long a distinct beep was detected
163 * by the discreet energy separation algorithm. */
164 switch_size_t timestamp;
165 /*! The MIN_TIME to use for this call */
166 int minTime;
167 } vmd_session_info_t;
168
169 static switch_bool_t process_data(vmd_session_info_t *vmd_info, switch_frame_t *frame);
170 static switch_bool_t vmd_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type);
171 static double freq_estimator(double *x);
172 static double ampl_estimator(double *x);
173 static void convert_pts(int16_t *i_pts, double *d_pts, int16_t max);
174 static void find_beep(vmd_session_info_t *vmd_info, switch_frame_t *frame);
175 static double median(double *m, int n);
176
177 /*
178 #define PRINT(a) do{ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, a); }while(0)
179 #define PRINT2(a, b) do{ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, a, b); }while(0)
180 */
181
182 /*! \brief The callback function that is called when new audio data becomes available
183 *
184 * @author Eric des Courtis
185 * @param bug A reference to the media bug.
186 * @param user_data The session information for this call.
187 * @param type The switch callback type.
188 * @return The success or failure of the function.
189 */
vmd_callback(switch_media_bug_t * bug,void * user_data,switch_abc_type_t type)190 static switch_bool_t vmd_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type)
191 {
192 vmd_session_info_t *vmd_info;
193 switch_codec_t *read_codec;
194 switch_frame_t *frame;
195
196 vmd_info = (vmd_session_info_t *) user_data;
197 if (vmd_info == NULL) {
198 return SWITCH_FALSE;
199 }
200
201 switch (type) {
202
203 case SWITCH_ABC_TYPE_INIT:
204 read_codec = switch_core_session_get_read_codec(vmd_info->session);
205 vmd_info->vmd_codec.rate = read_codec->implementation->samples_per_second;
206 vmd_info->vmd_codec.channels = read_codec->implementation->number_of_channels;
207 break;
208
209 case SWITCH_ABC_TYPE_READ_REPLACE:
210 frame = switch_core_media_bug_get_read_replace_frame(bug);
211 return process_data(vmd_info, frame);
212
213 default:
214 break;
215 }
216
217 return SWITCH_TRUE;
218 }
219
220 /*! \brief Process and convert data to be used by the find_beep() function
221 *
222 * @author Eric des Courtis
223 * @param vmd_info The session information associated with the call.
224 * @param frame The audio data.
225 * @return The success or failure of the function.
226 */
process_data(vmd_session_info_t * vmd_info,switch_frame_t * frame)227 static switch_bool_t process_data(vmd_session_info_t *vmd_info, switch_frame_t *frame)
228 {
229 uint32_t i;
230 unsigned int j;
231 double pts[P];
232 int16_t *data;
233 int16_t max;
234
235 //len = frame->samples * sizeof(int16_t);
236 data = (int16_t *) frame->data;
237
238 for (max = (int16_t) abs(data[0]), i = 1; i < frame->samples; i++) {
239 if ((int16_t) abs(data[i]) > max) {
240 max = (int16_t) abs(data[i]);
241 }
242 }
243
244 /*
245 if (vmd_info->data_len != len){
246 vmd_info->data_len = len;
247 if (vmd_info->data != NULL) free(vmd_info->data);
248 vmd_info->data = (int16_t *)malloc(len);
249 if (vmd_info->data == NULL) return SWITCH_FALSE;
250 }
251
252 (void)memcpy(vmd_info->data, data, len);
253 for(i = 2; i < frame->samples; i++){
254 vmd_info->data[i] =
255 0.0947997 * data[i]
256 -
257 0.0947997 * data[i - 2]
258 -
259 1.4083405 * vmd_info->data[i - 1]
260 +
261 0.8104005 * vmd_info->data[i - 2];
262 }
263 */
264
265 for (i = 0, j = vmd_info->pos; i < frame->samples; j++, j %= POINTS, i += 5) {
266 /* convert_pts(vmd_info->data + i, pts); */
267 convert_pts(data + i, pts, max);
268 vmd_info->points[j].freq = TO_HZ(freq_estimator(pts));
269 vmd_info->points[j].ampl = ampl_estimator(pts);
270 vmd_info->pos = j % POINTS;
271 find_beep(vmd_info, frame);
272 }
273
274 return SWITCH_TRUE;
275 }
276
277 /*! \brief Find voicemail beep in the audio stream
278 *
279 * @author Eric des Courtis
280 * @param vmd_info The session information associated with the call.
281 * @param frame The audio data.
282 * @return The success or failure of the function.
283 */
find_beep(vmd_session_info_t * vmd_info,switch_frame_t * frame)284 static void find_beep(vmd_session_info_t *vmd_info, switch_frame_t *frame)
285 {
286 int i;
287 int c;
288 double m[POINTS];
289 double med;
290 unsigned int j = (vmd_info->pos + 1) % POINTS;
291 unsigned int k = j;
292 switch_event_t *event;
293 switch_status_t status;
294 switch_event_t *event_copy;
295 switch_channel_t *channel = switch_core_session_get_channel(vmd_info->session);
296
297 switch (vmd_info->state) {
298 case BEEP_DETECTED:
299 for (c = 0, i = 0; i < POINTS; j++, j %= POINTS, i++) {
300 vmd_info->timestamp++;
301 if (vmd_info->points[j].freq < TOLERANCE_T(vmd_info->beep_freq) && vmd_info->points[j].freq > TOLERANCE_B(vmd_info->beep_freq)) {
302 c++;
303 vmd_info->beep_freq = (vmd_info->beep_freq * 0.95) + (vmd_info->points[j].freq * 0.05);
304 }
305 }
306
307 if (c < (POINTS - MAX_CHIRP)) {
308 vmd_info->state = BEEP_NOT_DETECTED;
309 if (vmd_info->timestamp < (switch_size_t) vmd_info->minTime) {
310 break;
311 }
312
313 status = switch_event_create_subclass(&event, SWITCH_EVENT_CUSTOM, VMD_EVENT_BEEP);
314 if (status != SWITCH_STATUS_SUCCESS) {
315 return;
316 }
317
318 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Beep-Status", "stop");
319 switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Beep-Time", "%d", (int) vmd_info->timestamp / POINTS);
320 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Unique-ID", switch_core_session_get_uuid(vmd_info->session));
321 switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Frequency", "%6.4lf", vmd_info->beep_freq);
322 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "call-command", "vmd");
323
324 if ((switch_event_dup(&event_copy, event)) != SWITCH_STATUS_SUCCESS) {
325 return;
326 }
327
328 switch_core_session_queue_event(vmd_info->session, &event);
329 switch_event_fire(&event_copy);
330
331 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(vmd_info->session), SWITCH_LOG_INFO, "<<< VMD - Beep Detected >>>\n");
332 switch_channel_set_variable(channel, "vmd_detect", "TRUE");
333
334 vmd_info->timestamp = 0;
335 }
336
337 break;
338
339 case BEEP_NOT_DETECTED:
340
341 for (i = 0; i < POINTS; k++, k %= POINTS, i++) {
342 m[i] = vmd_info->points[k].freq;
343 if (ISNAN(m[i])) {
344 m[i] = 0.0;
345 }
346 }
347
348 med = median(m, POINTS);
349 if (ISNAN(med)) {
350 for (i = 0; i < POINTS; i++) {
351 if (!ISNAN(m[i])) {
352 med = m[i];
353 break;
354 }
355 }
356 }
357
358 for (c = 0, i = 0; i < POINTS; j++, j %= POINTS, i++) {
359 if (vmd_info->points[j].freq < TOLERANCE_T(med) && vmd_info->points[j].freq > TOLERANCE_B(med)) {
360 if (vmd_info->points[j].ampl > MIN_AMPL && vmd_info->points[j].freq > MIN_FREQ && vmd_info->points[j].freq < MAX_FREQ) {
361 c++;
362 }
363 }
364 }
365
366 if (c >= VALID) {
367 vmd_info->state = BEEP_DETECTED;
368 vmd_info->beep_freq = med;
369 vmd_info->timestamp = 0;
370 }
371
372 break;
373 }
374 }
375
376 /*! \brief Find the median of an array of doubles
377 *
378 * @param m Array of frequency samples.
379 * @param n Number of samples in the array.
380 * @return The median.
381 */
median(double * m,int n)382 static double median(double *m, int n)
383 {
384 int i;
385 int less;
386 int greater;
387 int equal;
388 double min;
389 double max;
390 double guess;
391 double maxltguess;
392 double mingtguess;
393
394 min = max = m[0];
395
396 for (i = 1; i < n; i++) {
397 if (m[i] < min)
398 min = m[i];
399 if (m[i] > max)
400 max = m[i];
401 }
402
403 for (;;) {
404 guess = (min + max) / 2;
405 less = 0;
406 greater = 0;
407 equal = 0;
408 maxltguess = min;
409 mingtguess = max;
410
411 for (i = 0; i < n; i++) {
412 if (m[i] < guess) {
413 less++;
414 if (m[i] > maxltguess) {
415 maxltguess = m[i];
416 }
417 } else if (m[i] > guess) {
418 greater++;
419 if (m[i] < mingtguess) {
420 mingtguess = m[i];
421 }
422 } else {
423 equal++;
424 }
425 }
426
427 if (less <= (n + 1) / 2 && greater <= (n + 1) / 2) {
428 break;
429 } else if (less > greater) {
430 max = maxltguess;
431 } else {
432 min = mingtguess;
433 }
434 }
435
436 if (less >= (n + 1) / 2) {
437 return maxltguess;
438 } else if (less + equal >= (n + 1) / 2) {
439 return guess;
440 }
441
442 return mingtguess;
443 }
444
445 /*! \brief Convert many points for Signed L16 to relative floating point
446 *
447 * @author Eric des Courtis
448 * @param i_pts Array of P 16 bit integer audio samples.
449 * @param d_pts Array of P double floating point audio samples.
450 * @param max The maximum value in the entire audio frame.
451 * @return Nothing.
452 */
convert_pts(int16_t * i_pts,double * d_pts,int16_t max)453 static void convert_pts(int16_t *i_pts, double *d_pts, int16_t max)
454 {
455 int i;
456 for (i = 0; i < P; i++) {
457 /*! Signed L16 to relative floating point conversion */
458 d_pts[i] = ((((double) (i_pts[i]) + (double) max) / (double) (2 * max)) - 0.5) * 2.0;
459 }
460 }
461
462 /*! \brief Amplitude estimator for DESA-2
463 *
464 * The function is defined as \f$A = \sqrt{\frac{\psi{(x)}}{\sin{\Omega^2}}}\f$
465 *
466 * @author Eric des Courtis
467 * @param x An array of 5 evenly spaced audio samples \f$x_0, x_1, x_2, x_3, x_4\f$.
468 * @return The estimated amplitude.
469 */
ampl_estimator(double * x)470 double ampl_estimator(double *x)
471 {
472 double freq_sq;
473
474 freq_sq = freq_estimator(x);
475 freq_sq *= freq_sq;
476
477 return sqrt(PSI(x) / sin(freq_sq));
478 }
479
480 /*! \brief The DESA-2 algorithm
481 *
482 * The function is defined as \f$f = \frac{1}{2}\arccos{\frac{{{x^2_2} -
483 * {x_0}{x_4}} - {{x^2_1} -
484 * {x_0}{x_2}} - {{x^2_3} -
485 * {x_2}{x_4}}}
486 * {{2}({x^2_2} - {x_1}{x_3})}}\f$
487 *
488 * @author Eric des Courtis
489 * @param x An array for 5 evenly spaced audio samples \f$x_0, x_1, x_2, x_3, x_4\f$.
490 * @return A frequency estimate.
491 */
freq_estimator(double * x)492 double freq_estimator(double *x)
493 {
494 return 0.5 * acos((((x[2] * x[2]) - (x[0] * x[4]))
495 - ((x[1] * x[1]) - (x[0] * x[2]))
496 - ((x[3] * x[3]) - (x[2] * x[4])))
497 / (2.0 * ((x[2] * x[2]) - (x[1] * x[3])))
498
499 );
500 }
501
502 /*! \brief FreeSWITCH module loading function
503 *
504 * @author Eric des Courtis
505 * @return Load success or failure.
506 */
SWITCH_MODULE_LOAD_FUNCTION(mod_vmd_load)507 SWITCH_MODULE_LOAD_FUNCTION(mod_vmd_load)
508 {
509 switch_application_interface_t *app_interface;
510 switch_api_interface_t *api_interface;
511
512 if (switch_event_reserve_subclass(VMD_EVENT_BEEP) != SWITCH_STATUS_SUCCESS) {
513 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Couldn't register subclass %s!\n", VMD_EVENT_BEEP);
514 return SWITCH_STATUS_TERM;
515 }
516
517 /* connect my internal structure to the blank pointer passed to me */
518 *module_interface = switch_loadable_module_create_module_interface(pool, modname);
519
520 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Voicemail detection enabled\n");
521 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "mod_vmd deprecated. Please migrate to mod_avmd\n");
522
523 SWITCH_ADD_APP(app_interface, "vmd", "Detect beeps", "Detect voicemail beeps", vmd_start_function, "[start] [stop]", SAF_NONE);
524
525 SWITCH_ADD_API(api_interface, "vmd", "Detected voicemail beeps", vmd_api_main, VMD_SYNTAX);
526
527 /* indicate that the module should continue to be loaded */
528 return SWITCH_STATUS_SUCCESS;
529 }
530
531 /*! \brief FreeSWITCH application handler function.
532 * This handles calls made from applications such as LUA and the dialplan
533 *
534 * @author Eric des Courtis
535 * @return Success or failure of the function.
536 */
SWITCH_STANDARD_APP(vmd_start_function)537 SWITCH_STANDARD_APP(vmd_start_function)
538 {
539 switch_media_bug_t *bug;
540 switch_status_t status;
541 switch_channel_t *channel;
542 vmd_session_info_t *vmd_info;
543 int i;
544 const char *minTimeString;
545 int mintime = 0;
546
547 if (session == NULL)
548 return;
549
550 channel = switch_core_session_get_channel(session);
551
552 /* Is this channel already set? */
553 bug = (switch_media_bug_t *) switch_channel_get_private(channel, "_vmd_");
554 /* If yes */
555 if (bug != NULL) {
556 /* If we have a stop remove audio bug */
557 if (strcasecmp(data, "stop") == 0) {
558 switch_channel_set_private(channel, "_vmd_", NULL);
559 switch_core_media_bug_remove(session, &bug);
560 return;
561 }
562
563 /* We have already started */
564 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Cannot run 2 at once on the same channel!\n");
565
566 return;
567 }
568
569 vmd_info = (vmd_session_info_t *) switch_core_session_alloc(session, sizeof(vmd_session_info_t)
570 );
571
572 vmd_info->state = BEEP_NOT_DETECTED;
573 vmd_info->session = session;
574 vmd_info->pos = 0;
575 /*
576 vmd_info->data = NULL;
577 vmd_info->data_len = 0;
578 */
579 for (i = 0; i < POINTS; i++) {
580 vmd_info->points[i].freq = 0.0;
581 vmd_info->points[i].ampl = 0.0;
582 }
583
584 status = switch_core_media_bug_add(session, "vmd", NULL, vmd_callback, vmd_info, 0, SMBF_READ_REPLACE, &bug);
585
586 if (status != SWITCH_STATUS_SUCCESS) {
587 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Failure hooking to stream\n");
588 return;
589 }
590
591 switch_channel_set_private(channel, "_vmd_", bug);
592
593 if ((minTimeString = switch_channel_get_variable(channel, "vmd_min_time")) && (mintime = atoi(minTimeString))) {
594 vmd_info->minTime = mintime;
595 } else {
596 vmd_info->minTime = MIN_TIME;
597 }
598
599 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "MIN_TIME for call: %d\n", vmd_info->minTime);
600 }
601
602 /*! \brief Called when the module shuts down
603 *
604 * @author Eric des Courtis
605 * @return The success or failure of the function.
606 */
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_vmd_shutdown)607 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_vmd_shutdown)
608 {
609
610 switch_event_free_subclass(VMD_EVENT_BEEP);
611 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Voicemail detection disabled\n");
612
613 return SWITCH_STATUS_SUCCESS;
614 }
615
616 /*! \brief FreeSWITCH API handler function.
617 * This function handles API calls such as the ones from mod_event_socket and in some cases
618 * scripts such as LUA scripts.
619 *
620 * @author Eric des Courtis
621 * @return The success or failure of the function.
622 */
SWITCH_STANDARD_API(vmd_api_main)623 SWITCH_STANDARD_API(vmd_api_main)
624 {
625 switch_core_session_t *vmd_session = NULL;
626 switch_media_bug_t *bug;
627 vmd_session_info_t *vmd_info;
628 switch_channel_t *channel;
629 switch_status_t status;
630 int argc;
631 char *argv[VMD_PARAMS];
632 char *ccmd = NULL;
633 char *uuid;
634 char *command;
635 int i;
636
637 /* No command? Display usage */
638 if (zstr(cmd)) {
639 stream->write_function(stream, "-USAGE: %s\n", VMD_SYNTAX);
640 return SWITCH_STATUS_SUCCESS;
641 }
642
643 /* Duplicated contents of original string */
644 ccmd = strdup(cmd);
645 /* Separate the arguments */
646 argc = switch_separate_string(ccmd, ' ', argv, VMD_PARAMS);
647
648 /* If we don't have the expected number of parameters
649 * display usage */
650 if (argc != VMD_PARAMS) {
651 stream->write_function(stream, "-USAGE: %s\n", VMD_SYNTAX);
652 goto end;
653 }
654
655 uuid = argv[0];
656 command = argv[1];
657
658 /* using uuid locate a reference to the FreeSWITCH session */
659 vmd_session = switch_core_session_locate(uuid);
660
661 /* If the session was not found exit */
662 if (vmd_session == NULL) {
663 stream->write_function(stream, "-USAGE: %s\n", VMD_SYNTAX);
664 goto end;
665 }
666
667 /* Get current channel of the session to tag the session
668 * This indicates that our module is present */
669 channel = switch_core_session_get_channel(vmd_session);
670
671 /* Is this channel already set? */
672 bug = (switch_media_bug_t *) switch_channel_get_private(channel, "_vmd_");
673 /* If yes */
674 if (bug != NULL) {
675 /* If we have a stop remove audio bug */
676 if (strcasecmp(command, "stop") == 0) {
677 switch_channel_set_private(channel, "_vmd_", NULL);
678 switch_core_media_bug_remove(vmd_session, &bug);
679 switch_safe_free(ccmd);
680 stream->write_function(stream, "+OK\n");
681 goto end;
682 }
683
684 /* We have already started */
685 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Cannot run 2 at once on the same channel!\n");
686 goto end;
687 }
688
689 /* If we don't see the expected start exit */
690 if (strcasecmp(command, "start") != 0) {
691 stream->write_function(stream, "-USAGE: %s\n", VMD_SYNTAX);
692 goto end;
693 }
694
695 /* Allocate memory attached to this FreeSWITCH session for
696 * use in the callback routine and to store state information */
697 vmd_info = (vmd_session_info_t *) switch_core_session_alloc(vmd_session, sizeof(vmd_session_info_t)
698 );
699
700 /* Set initial values and states */
701 vmd_info->state = BEEP_NOT_DETECTED;
702 vmd_info->session = vmd_session;
703 vmd_info->pos = 0;
704 /*
705 vmd_info->data = NULL;
706 vmd_info->data_len = 0;
707 */
708
709 for (i = 0; i < POINTS; i++) {
710 vmd_info->points[i].freq = 0.0;
711 vmd_info->points[i].ampl = 0.0;
712 }
713
714 /* Add a media bug that allows me to intercept the
715 * reading leg of the audio stream */
716 status = switch_core_media_bug_add(vmd_session, "vmd", NULL, vmd_callback, vmd_info, 0, SMBF_READ_REPLACE, &bug);
717
718 /* If adding a media bug fails exit */
719 if (status != SWITCH_STATUS_SUCCESS) {
720 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Failure hooking to stream\n");
721 goto end;
722 }
723
724 /* Set the vmd tag to detect an existing vmd media bug */
725 switch_channel_set_private(channel, "_vmd_", bug);
726
727 /* Everything went according to plan! Notify the user */
728 stream->write_function(stream, "+OK\n");
729
730
731 end:
732
733 if (vmd_session) {
734 switch_core_session_rwunlock(vmd_session);
735 }
736
737 switch_safe_free(ccmd);
738
739 return SWITCH_STATUS_SUCCESS;
740 }
741
742
743 /* For Emacs:
744 * Local Variables:
745 * mode:c
746 * indent-tabs-mode:t
747 * tab-width:4
748 * c-basic-offset:4
749 * End:
750 * For VIM:
751 * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
752 */
753