freeswitch/src/mod/applications/mod_stress/mod_stress.cpp

269 lines
9.0 KiB
C++

/*
* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
* Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org>
*
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
*
* The Initial Developer of the Original Code is
* Anthony Minessale II <anthm@freeswitch.org>
* Portions created by the Initial Developer are Copyright (C)
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Anthony Minessale II <anthm@freeswitch.org>
*
* mod_stress.cpp -- Detect Voice Stress
*
*/
#include <stdexcept>
#include <stdio.h>
#include "FFTReal.h"
using namespace std;
#include <switch.h>
SWITCH_MODULE_LOAD_FUNCTION(mod_stress_load);
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_stress_shutdown);
SWITCH_MODULE_DEFINITION(mod_stress, mod_stress_load, mod_stress_shutdown, NULL);
struct stress_helper {
switch_core_session_t *session;
int read;
uint32_t frame_size;
FFTReal *fft;
float *data;
float *result;
float *pow_spectrum;
float bind;
int start;
int end;
float avg_tremor_pwr;
float avg_total_pwr;
float total_pwr;
float tremor_ratio;
float stress;
uint32_t rate;
switch_buffer_t *audio_buffer;
int16_t *audio;
};
static switch_bool_t stress_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type)
{
struct stress_helper *sth = (struct stress_helper *) user_data;
switch (type) {
case SWITCH_ABC_TYPE_INIT:
{
switch_codec_t *read_codec = switch_core_session_get_read_codec(sth->session);
sth->rate = read_codec->implementation->actual_samples_per_second;
if (sth->rate == 8000) {
sth->frame_size = 8192;
} else if (sth->rate == 16000) {
sth->frame_size = 16384;
} else if (sth->rate == 32000) {
sth->frame_size = 32768;
} else {
return SWITCH_FALSE;
}
sth->data = (float *) switch_core_session_alloc(sth->session, sizeof(*sth->data) * sth->frame_size);
sth->result = (float *) switch_core_session_alloc(sth->session, sizeof(*sth->result) * sth->frame_size);
sth->pow_spectrum = (float *) switch_core_session_alloc(sth->session, sizeof(*sth->pow_spectrum) * sth->frame_size);
sth->audio = (int16_t *) switch_core_session_alloc(sth->session, sizeof(*sth->audio) * sth->frame_size);
sth->fft = new FFTReal (sth->frame_size);
switch_buffer_create_dynamic(&sth->audio_buffer, sth->frame_size, sth->frame_size * 3, 0);
sth->bind = (float) sth->rate / sth->frame_size;
sth->start = (int) (8.0 / sth->bind);
sth->end = (int) (14.0 / sth->bind);
}
break;
case SWITCH_ABC_TYPE_CLOSE:
{
switch_buffer_destroy(&sth->audio_buffer);
delete sth->fft;
}
break;
case SWITCH_ABC_TYPE_READ:
case SWITCH_ABC_TYPE_WRITE:
break;
case SWITCH_ABC_TYPE_READ_REPLACE:
case SWITCH_ABC_TYPE_WRITE_REPLACE:
{
switch_frame_t *frame;
if (sth->read) {
frame = switch_core_media_bug_get_read_replace_frame(bug);
} else {
frame = switch_core_media_bug_get_write_replace_frame(bug);
}
if (!switch_test_flag(frame, SFF_CNG)) {
switch_buffer_write(sth->audio_buffer, frame->data, frame->datalen);
}
sth->stress = 0.0;
if (switch_buffer_inuse(sth->audio_buffer) >= sth->frame_size * sizeof(int16_t)) {
switch_size_t bytes;
uint32_t samples, i;
const float threshold = 1.5;
bytes = switch_buffer_read(sth->audio_buffer, sth->audio, sth->frame_size * sizeof(int16_t));
samples = bytes / sizeof(int16_t);
switch_short_to_float(sth->audio, sth->data, samples);
sth->fft->do_fft(sth->result, sth->data);
for (i = 0; i < samples; ++i) {
sth->pow_spectrum[i] = pow(fabs(sth->result[i]), 2) / (float) samples;
}
sth->avg_tremor_pwr = 0.0;
sth->avg_total_pwr = 0.0;
sth->total_pwr = 0.0;
for (i = sth->start; i <= sth->end; ++i) {
sth->avg_tremor_pwr += sth->pow_spectrum[i];
}
sth->avg_tremor_pwr /= ((sth->end - sth->start) + 1);
for (i = 0; i < samples; ++i) {
sth->total_pwr += sth->pow_spectrum[i];
}
sth->avg_total_pwr = sth->total_pwr / samples;
if (sth->total_pwr < threshold) {
sth->tremor_ratio = 0.0;
} else {
sth->tremor_ratio = sth->avg_tremor_pwr / sth->avg_total_pwr;
}
if (sth->total_pwr >= 1.0) {
float d = pow(sth->tremor_ratio, 4);
if (d > 0.0) {
sth->stress = (10.0 / d) / 10000;
if (sth->stress >= 20000.0) {
sth->stress = 20000.0;
}
}
}
}
if (sth->stress) {
switch_event_t *event, *dup;
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(switch_core_media_bug_get_session(bug)), SWITCH_LOG_DEBUG, "Stress %0.2f\n", sth->stress);
if (switch_event_create(&event, SWITCH_EVENT_DETECTED_SPEECH) == SWITCH_STATUS_SUCCESS) {
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Speech-Type", "stress-level");
switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Stress-Level", "%0.2f", sth->stress);
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Unique-ID", switch_core_session_get_uuid(sth->session));
if (switch_event_dup(&dup, event) == SWITCH_STATUS_SUCCESS) {
switch_event_fire(&dup);
}
if (switch_core_session_queue_event(sth->session, &event) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(switch_core_media_bug_get_session(bug)), SWITCH_LOG_ERROR, "Event queue failed!\n");
switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "delivery-failure", "true");
switch_event_fire(&event);
}
}
}
}
default:
break;
}
return SWITCH_TRUE;
}
SWITCH_STANDARD_APP(stress_start_function)
{
switch_media_bug_t *bug;
switch_status_t status;
switch_channel_t *channel = switch_core_session_get_channel(session);
struct stress_helper *sth;
char *argv[6];
int argc;
char *lbuf = NULL;
int x = 0;
if ((bug = (switch_media_bug_t *) switch_channel_get_private(channel, "_stress_"))) {
if (!zstr(data) && !strcasecmp(data, "stop")) {
switch_channel_set_private(channel, "_stress_", NULL);
switch_core_media_bug_remove(session, &bug);
} else {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_WARNING, "Cannot run 2 at once on the same channel!\n");
}
return;
}
sth = (struct stress_helper *) switch_core_session_alloc(session, sizeof(*sth));
assert(sth != NULL);
if (data && (lbuf = switch_core_session_strdup(session, data))
&& (argc = switch_separate_string(lbuf, ' ', argv, (sizeof(argv) / sizeof(argv[0]))))) {
if (!strncasecmp(argv[x], "read", 4)) {
sth->read = 1;
}
}
sth->session = session;
if ((status = switch_core_media_bug_add(session, "stress", NULL, stress_callback, sth, 0,
sth->read ? SMBF_READ_REPLACE : SMBF_WRITE_REPLACE, &bug)) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Failure!\n");
return;
}
switch_channel_set_private(channel, "_stress_", bug);
}
SWITCH_MODULE_LOAD_FUNCTION(mod_stress_load)
{
switch_application_interface_t *app_interface;
*module_interface = switch_loadable_module_create_module_interface(pool, modname);
SWITCH_ADD_APP(app_interface, "stress", "Analyze the stream for voice stress", "Analyze the stream for voice stress",
stress_start_function, "[read|write|stop]", SAF_NONE);
/* indicate that the module should continue to be loaded */
return SWITCH_STATUS_SUCCESS;
}
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_stress_shutdown)
{
return SWITCH_STATUS_UNLOAD;
}
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
*/