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