1 /*
2 * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
3 * Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org>
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 * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
18 *
19 * The Initial Developer of the Original Code is
20 * Anthony Minessale II <anthm@freeswitch.org>
21 * Portions created by the Initial Developer are Copyright (C)
22 * the Initial Developer. All Rights Reserved.
23 *
24 * Contributor(s):
25 *
26 * Anthony Minessale II <anthm@freeswitch.org>
27 *
28 * mod_stress.cpp -- Detect Voice Stress
29 *
30 */
31
32 #include <stdexcept>
33 #include <stdio.h>
34 #include "FFTReal.h"
35 using namespace std;
36
37 #include <switch.h>
38
39 SWITCH_MODULE_LOAD_FUNCTION(mod_stress_load);
40 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_stress_shutdown);
41 SWITCH_MODULE_DEFINITION(mod_stress, mod_stress_load, mod_stress_shutdown, NULL);
42
43 struct stress_helper {
44 switch_core_session_t *session;
45 int read;
46 uint32_t frame_size;
47 FFTReal *fft;
48 float *data;
49 float *result;
50 float *pow_spectrum;
51 float bind;
52 int start;
53 int end;
54 float avg_tremor_pwr;
55 float avg_total_pwr;
56 float total_pwr;
57 float tremor_ratio;
58 float stress;
59 uint32_t rate;
60 switch_buffer_t *audio_buffer;
61 int16_t *audio;
62 };
63
stress_callback(switch_media_bug_t * bug,void * user_data,switch_abc_type_t type)64 static switch_bool_t stress_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type)
65 {
66 struct stress_helper *sth = (struct stress_helper *) user_data;
67
68 switch (type) {
69 case SWITCH_ABC_TYPE_INIT:
70 {
71 switch_codec_t *read_codec = switch_core_session_get_read_codec(sth->session);
72
73 sth->rate = read_codec->implementation->actual_samples_per_second;
74
75 if (sth->rate == 8000) {
76 sth->frame_size = 8192;
77 } else if (sth->rate == 16000) {
78 sth->frame_size = 16384;
79 } else if (sth->rate == 32000) {
80 sth->frame_size = 32768;
81 } else {
82 return SWITCH_FALSE;
83 }
84
85 sth->data = (float *) switch_core_session_alloc(sth->session, sizeof(*sth->data) * sth->frame_size);
86 sth->result = (float *) switch_core_session_alloc(sth->session, sizeof(*sth->result) * sth->frame_size);
87 sth->pow_spectrum = (float *) switch_core_session_alloc(sth->session, sizeof(*sth->pow_spectrum) * sth->frame_size);
88 sth->audio = (int16_t *) switch_core_session_alloc(sth->session, sizeof(*sth->audio) * sth->frame_size);
89
90 sth->fft = new FFTReal (sth->frame_size);
91 switch_buffer_create_dynamic(&sth->audio_buffer, sth->frame_size, sth->frame_size * 3, 0);
92
93 sth->bind = (float) sth->rate / sth->frame_size;
94 sth->start = (int) (8.0 / sth->bind);
95 sth->end = (int) (14.0 / sth->bind);
96
97 }
98 break;
99 case SWITCH_ABC_TYPE_CLOSE:
100 {
101 switch_buffer_destroy(&sth->audio_buffer);
102 delete sth->fft;
103 }
104 break;
105 case SWITCH_ABC_TYPE_READ:
106 case SWITCH_ABC_TYPE_WRITE:
107 break;
108 case SWITCH_ABC_TYPE_READ_REPLACE:
109 case SWITCH_ABC_TYPE_WRITE_REPLACE:
110 {
111 switch_frame_t *frame;
112
113 if (sth->read) {
114 frame = switch_core_media_bug_get_read_replace_frame(bug);
115 } else {
116 frame = switch_core_media_bug_get_write_replace_frame(bug);
117 }
118
119 if (!switch_test_flag(frame, SFF_CNG)) {
120 switch_buffer_write(sth->audio_buffer, frame->data, frame->datalen);
121 }
122
123 sth->stress = 0.0;
124
125 if (switch_buffer_inuse(sth->audio_buffer) >= sth->frame_size * sizeof(int16_t)) {
126 switch_size_t bytes;
127 uint32_t samples, i;
128 const float threshold = 1.5;
129
130 bytes = switch_buffer_read(sth->audio_buffer, sth->audio, sth->frame_size * sizeof(int16_t));
131 samples = bytes / sizeof(int16_t);
132
133 switch_short_to_float(sth->audio, sth->data, samples);
134 sth->fft->do_fft(sth->result, sth->data);
135
136 for (i = 0; i < samples; ++i) {
137 sth->pow_spectrum[i] = pow(fabs(sth->result[i]), 2) / (float) samples;
138 }
139
140 sth->avg_tremor_pwr = 0.0;
141 sth->avg_total_pwr = 0.0;
142 sth->total_pwr = 0.0;
143
144 for (i = sth->start; i <= sth->end; ++i) {
145 sth->avg_tremor_pwr += sth->pow_spectrum[i];
146 }
147 sth->avg_tremor_pwr /= ((sth->end - sth->start) + 1);
148
149 for (i = 0; i < samples; ++i) {
150 sth->total_pwr += sth->pow_spectrum[i];
151 }
152 sth->avg_total_pwr = sth->total_pwr / samples;
153
154 if (sth->total_pwr < threshold) {
155 sth->tremor_ratio = 0.0;
156 } else {
157 sth->tremor_ratio = sth->avg_tremor_pwr / sth->avg_total_pwr;
158 }
159
160 if (sth->total_pwr >= 1.0) {
161 float d = pow(sth->tremor_ratio, 4);
162 if (d > 0.0) {
163 sth->stress = (10.0 / d) / 10000;
164 if (sth->stress >= 20000.0) {
165 sth->stress = 20000.0;
166 }
167 }
168 }
169 }
170
171 if (sth->stress) {
172 switch_event_t *event, *dup;
173 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(switch_core_media_bug_get_session(bug)), SWITCH_LOG_DEBUG, "Stress %0.2f\n", sth->stress);
174
175 if (switch_event_create(&event, SWITCH_EVENT_DETECTED_SPEECH) == SWITCH_STATUS_SUCCESS) {
176 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Speech-Type", "stress-level");
177 switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Stress-Level", "%0.2f", sth->stress);
178 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Unique-ID", switch_core_session_get_uuid(sth->session));
179 if (switch_event_dup(&dup, event) == SWITCH_STATUS_SUCCESS) {
180 switch_event_fire(&dup);
181 }
182 if (switch_core_session_queue_event(sth->session, &event) != SWITCH_STATUS_SUCCESS) {
183 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(switch_core_media_bug_get_session(bug)), SWITCH_LOG_ERROR, "Event queue failed!\n");
184 switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "delivery-failure", "true");
185 switch_event_fire(&event);
186 }
187 }
188 }
189 }
190 default:
191 break;
192 }
193
194 return SWITCH_TRUE;
195 }
196
SWITCH_STANDARD_APP(stress_start_function)197 SWITCH_STANDARD_APP(stress_start_function)
198 {
199 switch_media_bug_t *bug;
200 switch_status_t status;
201 switch_channel_t *channel = switch_core_session_get_channel(session);
202 struct stress_helper *sth;
203 char *argv[6];
204 int argc;
205 char *lbuf = NULL;
206 int x = 0;
207
208 if ((bug = (switch_media_bug_t *) switch_channel_get_private(channel, "_stress_"))) {
209 if (!zstr(data) && !strcasecmp(data, "stop")) {
210 switch_channel_set_private(channel, "_stress_", NULL);
211 switch_core_media_bug_remove(session, &bug);
212 } else {
213 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Cannot run 2 at once on the same channel!\n");
214 }
215 return;
216 }
217
218 sth = (struct stress_helper *) switch_core_session_alloc(session, sizeof(*sth));
219 assert(sth != NULL);
220
221
222 if (data && (lbuf = switch_core_session_strdup(session, data))
223 && (argc = switch_separate_string(lbuf, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) {
224 if (!strncasecmp(argv[x], "read", 4)) {
225 sth->read = 1;
226 }
227 }
228
229 sth->session = session;
230
231 if ((status = switch_core_media_bug_add(session, "stress", NULL, stress_callback, sth, 0,
232 sth->read ? SMBF_READ_REPLACE : SMBF_WRITE_REPLACE, &bug)) != SWITCH_STATUS_SUCCESS) {
233 switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Failure!\n");
234 return;
235 }
236
237 switch_channel_set_private(channel, "_stress_", bug);
238
239 }
240
SWITCH_MODULE_LOAD_FUNCTION(mod_stress_load)241 SWITCH_MODULE_LOAD_FUNCTION(mod_stress_load)
242 {
243 switch_application_interface_t *app_interface;
244
245 *module_interface = switch_loadable_module_create_module_interface(pool, modname);
246
247 SWITCH_ADD_APP(app_interface, "stress", "Analyze the stream for voice stress", "Analyze the stream for voice stress",
248 stress_start_function, "[read|write|stop]", SAF_NONE);
249
250 /* indicate that the module should continue to be loaded */
251 return SWITCH_STATUS_SUCCESS;
252 }
253
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_stress_shutdown)254 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_stress_shutdown)
255 {
256 return SWITCH_STATUS_UNLOAD;
257 }
258
259 /* For Emacs:
260 * Local Variables:
261 * mode:c
262 * indent-tabs-mode:t
263 * tab-width:4
264 * c-basic-offset:4
265 * End:
266 * For VIM:
267 * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
268 */
269