/*
* This file is part of MotionPlus.
*
* MotionPlus is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MotionPlus is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with MotionPlus. If not, see .
*
*
*/
#include "motionplus.hpp"
#include "conf.hpp"
#include "logger.hpp"
#include "util.hpp"
#include "sound.hpp"
#if defined(HAVE_FFTW3) && (defined(HAVE_ALSA) || defined(HAVE_PULSE))
static float HammingWindow(int n1, int N2){
return 0.54F - 0.46F * (float)(cos((2 * M_PI * n1)) / (N2 - 1));
}
static float HannWindow(int n1, int N2){
return 0.5F * (float)(1 - (cos(2 * M_PI * n1 * N2)));
}
static void snd_init_values(ctx_dev *snd)
{
ctx_snd_info *info = snd->snd_info;
snd->snd_info->snd_fftw->bin_max = 0;
snd->snd_info->snd_fftw->bin_min = 0;
snd->snd_info->snd_fftw->bin_size = 0;
snd->snd_info->snd_fftw->ff_in = NULL;
snd->snd_info->snd_fftw->ff_out = NULL;
snd->snd_info->snd_fftw->ff_plan = NULL;
info->sample_rate = 0;
info->channels = 0;
info->vol_count = 0;
info->vol_max = 0;
info->vol_min = 9999;
info->buffer = NULL;
info->buffer_size = 0;
info->pulse_server = "";
}
static void snd_init_alerts(ctx_snd_alert *tmp_alert)
{
tmp_alert->alert_id = 0;
tmp_alert->alert_nm = "";
tmp_alert->freq_high = 10000;
tmp_alert->freq_low = 0;
tmp_alert->volume_count = 0;
tmp_alert->volume_level = 0;
tmp_alert->trigger_count = 0;
tmp_alert->trigger_threshold = 10;
tmp_alert->trigger_duration = 10;
clock_gettime(CLOCK_MONOTONIC, &tmp_alert->trigger_time);
}
static void snd_edit_alerts(ctx_dev *snd)
{
ctx_snd_info *info = snd->snd_info;
std::list::iterator it_a0;
std::list::iterator it_a1;
int indx, id_chk, id_cnt;
bool validids;
validids = true;
for (it_a0=info->alerts.begin(); it_a0!=info->alerts.end(); it_a0++) {
id_chk = it_a0->alert_id;
id_cnt = 0;
for (it_a1=info->alerts.begin(); it_a1!=info->alerts.end(); it_a1++) {
if (id_chk == it_a1->alert_id) {
id_cnt++;
}
}
if (id_cnt > 1) {
validids = false;
}
}
if (validids == false) {
MOTPLS_LOG(NTC, TYPE_ALL, NO_ERRNO, "Sound alert ids must be unique.");
MOTPLS_LOG(NTC, TYPE_ALL, NO_ERRNO, "Creating new sound alert ids.");
indx = 0;
for (it_a0=info->alerts.begin(); it_a0!=info->alerts.end(); it_a0++) {
it_a0->alert_id = indx;
indx++;
}
}
for (it_a0=info->alerts.begin(); it_a0!=info->alerts.end(); it_a0++) {
if (it_a0->alert_nm == "") {
it_a0->alert_nm = "sound_alert" + std::to_string(it_a0->alert_id);
}
if (it_a0->volume_level < info->vol_min) {
info->vol_min = it_a0->volume_level;
}
MOTPLS_LOG(INF, TYPE_ALL, NO_ERRNO, "Sound Alert Parameters:");
MOTPLS_LOG(INF, TYPE_ALL, NO_ERRNO, " alert_id: %d",it_a0->alert_id);
MOTPLS_LOG(INF, TYPE_ALL, NO_ERRNO, " alert_nm %s",it_a0->alert_nm.c_str());
MOTPLS_LOG(INF, TYPE_ALL, NO_ERRNO, " freq_low: %.4f",it_a0->freq_low);
MOTPLS_LOG(INF, TYPE_ALL, NO_ERRNO, " freq_high: %.4f",it_a0->freq_high);
MOTPLS_LOG(INF, TYPE_ALL, NO_ERRNO, " volume_count: %d",it_a0->volume_count);
MOTPLS_LOG(INF, TYPE_ALL, NO_ERRNO, " volume_level: %d",it_a0->volume_level);
MOTPLS_LOG(INF, TYPE_ALL, NO_ERRNO, " trigger_threshold: %d",it_a0->trigger_threshold);
MOTPLS_LOG(INF, TYPE_ALL, NO_ERRNO, " trigger_duration: %d",it_a0->trigger_duration);
}
}
static void snd_load_alerts(ctx_dev *snd)
{
ctx_snd_info *info = snd->snd_info;
ctx_snd_alert tmp_alert;
std::list parm_val;
std::list::iterator it_a;
ctx_params *tmp_params;
p_it it;
conf_edit_get(snd->conf, "snd_alerts", parm_val, PARM_CAT_18);
tmp_params = new ctx_params;
for (it_a=parm_val.begin(); it_a!=parm_val.end(); it_a++) {
tmp_params->update_params = true;
util_parms_parse(tmp_params,"snd_alerts", it_a->c_str());
snd_init_alerts(&tmp_alert);
for (it = tmp_params->params_array.begin();
it != tmp_params->params_array.end(); it++) {
if (it->param_name == "alert_id") {
tmp_alert.alert_id = mtoi(it->param_value);
}
if (it->param_name == "alert_nm") {
tmp_alert.alert_nm = it->param_value;
}
if (it->param_name == "freq_low") {
tmp_alert.freq_low = mtof(it->param_value);
}
if (it->param_name == "freq_high") {
tmp_alert.freq_high = mtof(it->param_value);
}
if (it->param_name == "volume_count") {
tmp_alert.volume_count = mtoi(it->param_value);
}
if (it->param_name == "volume_level") {
tmp_alert.volume_level = mtoi(it->param_value);
}
if (it->param_name == "trigger_threshold") {
tmp_alert.trigger_threshold = mtoi(it->param_value);
}
if (it->param_name == "trigger_duration") {
tmp_alert.trigger_duration = mtoi(it->param_value);
}
}
info->alerts.push_back(tmp_alert);
}
delete tmp_params;
snd_edit_alerts(snd);
}
static void snd_load_params(ctx_dev *snd)
{
ctx_snd_info *info = snd->snd_info;
p_it it;
p_lst *lst;
snd->snd_info->params->update_params = true;
util_parms_parse(snd->snd_info->params,"snd_params", snd->conf->snd_params);
util_parms_add_default(snd->snd_info->params,"source","alsa");
util_parms_add_default(snd->snd_info->params,"channels","1");
util_parms_add_default(snd->snd_info->params,"frames","2048");
util_parms_add_default(snd->snd_info->params,"sample_rate","44100");
lst = &snd->snd_info->params->params_array;
for (it = lst->begin(); it != lst->end(); it++) {
MOTPLS_LOG(NTC, TYPE_ALL, NO_ERRNO, "%s : %s"
, it->param_name.c_str(), it->param_value.c_str());
}
for (it = lst->begin(); it != lst->end(); it++) {
if (it->param_name == "source") {
info->source = it->param_value;
}
if (it->param_name == "channels") {
info->channels = mtoi(it->param_value);
}
if (it->param_name == "frames") {
info->frames = mtoi(it->param_value);
}
if (it->param_name == "sample_rate") {
info->sample_rate = mtoi(it->param_value);
}
if (it->param_name == "pulse_server") {
info->pulse_server = it->param_value;
}
}
}
/************ Start ALSA *******************/
#ifdef HAVE_ALSA
static void snd_alsa_list_subdev(ctx_dev *snd)
{
ctx_snd_alsa *alsa = snd->snd_info->snd_alsa;
int indx, retcd, cnt;
MOTPLS_LOG(NTC, TYPE_ALL, NO_ERRNO, _("Card %i(%s): %s [%s]")
, alsa->card_id, alsa->device_nm.c_str()
, snd_ctl_card_info_get_id(alsa->card_info)
, snd_ctl_card_info_get_name(alsa->card_info));
MOTPLS_LOG(NTC, TYPE_ALL, NO_ERRNO, _(" Device %i (%s,%d): %s [%s]")
, alsa->device_id, alsa->device_nm.c_str()
, alsa->device_id
, snd_pcm_info_get_id(alsa->pcm_info)
, snd_pcm_info_get_name(alsa->pcm_info));
cnt = (int)snd_pcm_info_get_subdevices_count(alsa->pcm_info);
MOTPLS_LOG(NTC, TYPE_ALL, NO_ERRNO, _(" Subdevices: %i/%i")
, snd_pcm_info_get_subdevices_avail(alsa->pcm_info),cnt);
for (indx=0; indxpcm_info,indx);
retcd = snd_ctl_pcm_info(alsa->ctl_hdl, alsa->pcm_info);
if (retcd < 0) {
MOTPLS_LOG(ERR, TYPE_ALL, NO_ERRNO
, _("control digital audio playback info (%i): %s")
, alsa->card_id, snd_strerror(retcd));
} else {
MOTPLS_LOG(NTC, TYPE_ALL, NO_ERRNO
, _(" Subdevice #%i: %s"), indx
, snd_pcm_info_get_subdevice_name(alsa->pcm_info));
}
}
}
static void snd_alsa_list_card(ctx_dev *snd)
{
ctx_snd_alsa *alsa = snd->snd_info->snd_alsa;
int retcd;
retcd = snd_ctl_card_info(alsa->ctl_hdl, alsa->card_info);
if (retcd < 0) {
snd_ctl_close(alsa->ctl_hdl);
MOTPLS_LOG(ERR, TYPE_ALL, NO_ERRNO
, _("control hardware info (%i): %s")
, alsa->card_id, snd_strerror(retcd));
return;
}
alsa->device_id = -1;
retcd = snd_ctl_pcm_next_device(alsa->ctl_hdl, &alsa->device_id);
if (retcd < 0) {
MOTPLS_LOG(ERR, TYPE_ALL, NO_ERRNO, _("snd_ctl_pcm_next_device"));
return;
}
while (alsa->device_id >= 0) {
snd_pcm_info_set_device(alsa->pcm_info, alsa->device_id);
snd_pcm_info_set_subdevice(alsa->pcm_info, 0);
snd_pcm_info_set_stream(alsa->pcm_info, SND_PCM_STREAM_CAPTURE);
retcd = snd_ctl_pcm_info(alsa->ctl_hdl, alsa->pcm_info);
if (retcd == 0) {
snd_alsa_list_subdev(snd);
} else if (retcd != -ENOENT){
MOTPLS_LOG(ERR, TYPE_ALL, NO_ERRNO
, _("control digital audio info (%i): %s")
, alsa->card_id, snd_strerror(retcd));
}
retcd = snd_ctl_pcm_next_device(alsa->ctl_hdl, &alsa->device_id);
if (retcd < 0) {
MOTPLS_LOG(ERR, TYPE_ALL, NO_ERRNO, _("snd_ctl_pcm_next_device"));
}
}
}
static void snd_alsa_list(ctx_dev *snd)
{
ctx_snd_alsa *alsa = snd->snd_info->snd_alsa;
int retcd;
if (snd->device_status == STATUS_CLOSED) {
return;
}
snd_ctl_card_info_alloca(&alsa->card_info);
snd_pcm_info_alloca(&alsa->pcm_info);
alsa->card_id = -1;
retcd = snd_card_next(&alsa->card_id);
if ((retcd < 0) || (alsa->card_id == -1)) {
MOTPLS_LOG(ERR, TYPE_ALL, NO_ERRNO
, _("no soundcards found..."));
snd->device_status = STATUS_CLOSED;
snd->restart_dev = false;
snd->finish_dev = true;
return;
}
MOTPLS_LOG(NTC, TYPE_ALL, NO_ERRNO, _("Devices"));
while (alsa->card_id >= 0) {
alsa->device_nm="hw:"+std::to_string(alsa->card_id);
retcd = snd_ctl_open(&alsa->ctl_hdl, alsa->device_nm.c_str(), 0);
if (retcd == 0) {
snd_alsa_list_card(snd);
snd_ctl_close(alsa->ctl_hdl);
} else {
MOTPLS_LOG(ERR, TYPE_ALL, NO_ERRNO, _("control open (%i): %s")
, alsa->card_id, snd_strerror(retcd));
}
snd_card_next(&alsa->card_id);
}
}
static void snd_alsa_start(ctx_dev *snd)
{
ctx_snd_alsa *alsa = snd->snd_info->snd_alsa;
ctx_snd_info *info = snd->snd_info;
snd_pcm_hw_params_t *hw_params;
snd_pcm_uframes_t frames_per;
snd_pcm_format_t actl_sndfmt;
unsigned int actl_rate, smpl_rate;
int retcd;
frames_per = info->frames;
smpl_rate = (unsigned int)info->sample_rate;
retcd = snd_pcm_open(&alsa->pcm_dev
, snd->conf->snd_device.c_str(), SND_PCM_STREAM_CAPTURE, 0);
if (retcd < 0) {
MOTPLS_LOG(ERR, TYPE_ALL, NO_ERRNO
, _("error: snd_pcm_open device %s (%s)")
, snd->conf->snd_device.c_str(), snd_strerror (retcd));
snd->device_status = STATUS_CLOSED;
return;
}
retcd = snd_pcm_hw_params_malloc(&hw_params);
if (retcd < 0) {
MOTPLS_LOG(ERR, TYPE_ALL, NO_ERRNO
, _("error: snd_pcm_hw_params_malloc(%s)")
, snd_strerror (retcd));
snd->device_status = STATUS_CLOSED;
return;
}
retcd = snd_pcm_hw_params_any(alsa->pcm_dev, hw_params);
if (retcd < 0) {
MOTPLS_LOG(ERR, TYPE_ALL, NO_ERRNO
, _("error: snd_pcm_hw_params_any(%s)")
, snd_strerror (retcd));
snd->device_status = STATUS_CLOSED;
return;
}
retcd = snd_pcm_hw_params_set_access(alsa->pcm_dev
, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
if (retcd < 0) {
MOTPLS_LOG(ERR, TYPE_ALL, NO_ERRNO
, _("error: snd_pcm_hw_params_set_access(%s)")
, snd_strerror (retcd));
snd->device_status = STATUS_CLOSED;
return;
}
retcd = snd_pcm_hw_params_set_format(alsa->pcm_dev
, hw_params, SND_PCM_FORMAT_S16_LE);
if (retcd < 0) {
MOTPLS_LOG(ERR, TYPE_ALL, NO_ERRNO
, _("error: snd_pcm_hw_params_set_format(%s)")
, snd_strerror (retcd));
snd->device_status = STATUS_CLOSED;
return;
}
retcd = snd_pcm_hw_params_set_rate_near(alsa->pcm_dev
, hw_params, &smpl_rate, 0);
if (retcd < 0) {
MOTPLS_LOG(ERR, TYPE_ALL, NO_ERRNO
, _("error: snd_pcm_hw_params_set_rate_near(%s)")
, snd_strerror (retcd));
snd->device_status = STATUS_CLOSED;
return;
}
retcd = snd_pcm_hw_params_set_channels(alsa->pcm_dev
, hw_params, info->channels);
if (retcd < 0) {
MOTPLS_LOG(ERR, TYPE_ALL, NO_ERRNO
, _("error: snd_pcm_hw_params_set_channels(%s)")
, snd_strerror (retcd));
snd->device_status = STATUS_CLOSED;
return;
}
retcd = snd_pcm_hw_params_set_period_size_near(alsa->pcm_dev
, hw_params, &frames_per, NULL);
if (retcd < 0) {
MOTPLS_LOG(ERR, TYPE_ALL, NO_ERRNO
, _("error: snd_pcm_hw_params_set_period_size_near(%s)")
, snd_strerror (retcd));
snd->device_status = STATUS_CLOSED;
return;
}
retcd = snd_pcm_hw_params(alsa->pcm_dev, hw_params);
if (retcd < 0) {
MOTPLS_LOG(ERR, TYPE_ALL, NO_ERRNO
, _("error: snd_pcm_hw_params(%s)")
, snd_strerror (retcd));
snd->device_status = STATUS_CLOSED;
return;
}
snd_pcm_hw_params_free(hw_params);
retcd = snd_pcm_prepare(alsa->pcm_dev);
if (retcd < 0) {
MOTPLS_LOG(ERR, TYPE_ALL, NO_ERRNO
, _("error: snd_pcm_prepare(%s)")
, snd_strerror (retcd));
snd->device_status = STATUS_CLOSED;
return;
}
/* get actual parms selected */
retcd = snd_pcm_hw_params_get_format(hw_params, &actl_sndfmt);
if (retcd < 0) {
MOTPLS_LOG(ERR, TYPE_ALL, NO_ERRNO
, _("error: snd_pcm_hw_params_get_format(%s)")
, snd_strerror (retcd));
snd->device_status = STATUS_CLOSED;
return;
}
retcd = snd_pcm_hw_params_get_rate(hw_params, &actl_rate, NULL);
if (retcd < 0) {
MOTPLS_LOG(ERR, TYPE_ALL, NO_ERRNO
, _("error: snd_pcm_hw_params_get_rate(%s)")
, snd_strerror (retcd));
snd->device_status = STATUS_CLOSED;
return;
}
retcd = snd_pcm_hw_params_get_period_size(hw_params, &frames_per, NULL);
if (retcd < 0) {
MOTPLS_LOG(ERR, TYPE_ALL, NO_ERRNO
, _("error: snd_pcm_hw_params_get_period_size(%s)")
, snd_strerror (retcd));
snd->device_status = STATUS_CLOSED;
return;
}
MOTPLS_LOG(NTC, TYPE_ALL, NO_ERRNO, _("Actual rate %hu"), actl_rate);
MOTPLS_LOG(NTC, TYPE_ALL, NO_ERRNO, _("Actual frames per %lu"), frames_per);
if (actl_sndfmt <= 5) {
MOTPLS_LOG(NTC, TYPE_ALL, NO_ERRNO, _("Sound format 16"));
} else if (actl_sndfmt <= 9 ) {
MOTPLS_LOG(NTC, TYPE_ALL, NO_ERRNO, _("Sound format 24"));
} else {
MOTPLS_LOG(NTC, TYPE_ALL, NO_ERRNO, _("Sound format 32"));
}
/*************************************************************/
/** allocate and initialize the sound buffers */
/*************************************************************/
info->frames = (int)frames_per;
info->buffer_size = info->frames * 2;
info->buffer = (int16_t*)mymalloc(info->buffer_size * sizeof(int16_t));
memset(info->buffer, 0x00, info->buffer_size * sizeof(int16_t));
MOTPLS_LOG(NTC, TYPE_ALL, NO_ERRNO, "Started.");
snd->device_status =STATUS_OPENED;
}
#endif
static void snd_alsa_init(ctx_dev *snd)
{
#ifdef HAVE_ALSA
ctx_snd_alsa *alsa = snd->snd_info->snd_alsa;
alsa->pcm_dev = NULL;
alsa->pcm_info = NULL;
alsa->card_id = -1;
alsa->card_info = NULL;
alsa->pcm_dev = NULL;
alsa->ctl_hdl = NULL;
alsa->card_info = NULL;
alsa->card_id = 0;
alsa->pcm_info = NULL;
alsa->device_nm = "";
alsa->device_id = 0;
snd_alsa_list(snd);
snd_alsa_start(snd);
#else
snd->device_status = STATUS_CLOSED;
snd->finish_dev = true;
snd->restart_dev = false;
#endif
}
static void snd_alsa_capture(ctx_dev *snd)
{
#ifdef HAVE_ALSA
ctx_snd_info *info = snd->snd_info;
ctx_snd_alsa *alsa = snd->snd_info->snd_alsa;
long int retcd;
retcd = snd_pcm_readi(alsa->pcm_dev, info->buffer, info->frames);
if (retcd != info->frames) {
MOTPLS_LOG(ERR, TYPE_ALL, NO_ERRNO
, _("error: read from audio interface failed (%s)")
, snd_strerror((int)retcd));
snd->device_status = STATUS_CLOSED;
snd->finish_dev = true;
snd->restart_dev = false;
}
#else
snd->device_status = STATUS_CLOSED;
snd->finish_dev = true;
snd->restart_dev = false;
#endif
}
static void snd_alsa_cleanup(ctx_dev *snd)
{
#ifdef HAVE_ALSA
if (snd->snd_info->snd_alsa->pcm_dev != NULL) {
snd_pcm_close(snd->snd_info->snd_alsa->pcm_dev);
snd_config_update_free_global();
}
#else
snd->device_status = STATUS_CLOSED;
snd->finish_dev = true;
snd->restart_dev = false;
#endif
}
/************ End ALSA *******************/
/************ Start PULSE *******************/
static void snd_pulse_init(ctx_dev *snd)
{
#ifdef HAVE_PULSE
ctx_snd_info *info = snd->snd_info;
pa_sample_spec specs;
int errcd;
specs.format = PA_SAMPLE_S16LE;
specs.rate = (uint32_t)info->sample_rate;
specs.channels = (uint8_t)info->channels;
snd->snd_info->snd_pulse->dev = NULL;
snd->snd_info->snd_pulse->dev = pa_simple_new(
(info->pulse_server=="" ? NULL : info->pulse_server.c_str())
, "motionplus", PA_STREAM_RECORD
, (snd->conf->snd_device=="" ? NULL : snd->conf->snd_device.c_str())
, "motionplus", &specs, NULL, NULL, &errcd);
if (snd->snd_info->snd_pulse->dev == NULL) {
MOTPLS_LOG(ERR, TYPE_ALL, NO_ERRNO
, _("Error opening pulse (%s)")
, pa_strerror(errcd));
snd->device_status = STATUS_CLOSED;
snd->finish_dev = true;
snd->restart_dev = false;
return;
}
info->buffer_size = info->frames * 2;
info->buffer = (int16_t*)mymalloc(info->buffer_size * sizeof(int16_t));
memset(info->buffer, 0x00, info->buffer_size * sizeof(int16_t));
MOTPLS_LOG(NTC, TYPE_ALL, NO_ERRNO, "Started.");
snd->device_status =STATUS_OPENED;
#else
snd->device_status = STATUS_CLOSED;
snd->finish_dev = true;
snd->restart_dev = false;
#endif
}
static void snd_pulse_capture(ctx_dev *snd)
{
#ifdef HAVE_PULSE
ctx_snd_info *info = snd->snd_info;
ctx_snd_pulse *pulse = snd->snd_info->snd_pulse;
int errcd, retcd;
retcd = pa_simple_read(pulse->dev, info->buffer
, info->buffer_size, &errcd);
if (retcd < 0) {
MOTPLS_LOG(ERR, TYPE_ALL, NO_ERRNO
, _("Error capturing PulseAudio (%s)")
, pa_strerror(errcd));
snd->device_status = STATUS_CLOSED;
snd->finish_dev = true;
snd->restart_dev = false;
}
#else
snd->device_status = STATUS_CLOSED;
snd->finish_dev = true;
snd->restart_dev = false;
#endif
}
static void snd_pulse_cleanup(ctx_dev *snd)
{
#ifdef HAVE_PULSE
if (snd->snd_info->snd_pulse->dev != NULL) {
pa_simple_free(snd->snd_info->snd_pulse->dev);
}
#else
snd->device_status = STATUS_CLOSED;
snd->finish_dev = true;
snd->restart_dev = false;
#endif
}
/************ End PULSE *******************/
static void snd_fftw_open(ctx_dev *snd)
{
ctx_snd_fftw *fftw = snd->snd_info->snd_fftw;
ctx_snd_info *info = snd->snd_info;
int indx;
if (snd->device_status == STATUS_CLOSED) {
snd->finish_dev = true;
snd->restart_dev = false;
return;
}
MOTPLS_LOG(NTC, TYPE_ALL, NO_ERRNO
, _("Opening FFTW plan"));
fftw->ff_in = (double*) fftw_malloc(
sizeof(fftw_complex) * info->frames);
fftw->ff_out = (fftw_complex*) fftw_malloc(
sizeof(fftw_complex) * info->frames);
fftw->ff_plan = fftw_plan_dft_r2c_1d(
info->frames, fftw->ff_in, fftw->ff_out, FFTW_MEASURE);
for (indx=0; indxframes; indx++) {
fftw->ff_in[indx] = 0;
}
fftw->bin_min = 1;
fftw->bin_max = (info->frames / 2);
fftw->bin_size = ((float)info->sample_rate / (float)info->frames);
}
static void slp_capture(ctx_dev *snd)
{
if (snd->device_status == STATUS_CLOSED) {
snd->finish_dev = true;
snd->restart_dev = false;
return;
}
if (snd->snd_info->source == "alsa") {
snd_alsa_capture(snd);
} else if (snd->snd_info->source == "pulse") {
snd_pulse_capture(snd);
}
}
void snd_cleanup(ctx_dev *snd)
{
if (snd->snd_info->source == "alsa") {
snd_alsa_cleanup(snd);
} else if (snd->snd_info->source == "pulse") {
snd_pulse_cleanup(snd);
}
if (snd->snd_info->buffer != NULL) {
free(snd->snd_info->buffer);
snd->snd_info->buffer = NULL;
}
fftw_destroy_plan(snd->snd_info->snd_fftw->ff_plan);
fftw_free(snd->snd_info->snd_fftw->ff_in);
fftw_free(snd->snd_info->snd_fftw->ff_out);
MOTPLS_LOG(NTC, TYPE_ALL, NO_ERRNO, "Stopped.");
}
static void slp_init(ctx_dev *snd)
{
if ((snd->device_status != STATUS_INIT) &&
(snd->device_status != STATUS_RESET)) {
return;
}
if (snd->device_status == STATUS_RESET) {
snd_cleanup(snd);
}
MOTPLS_LOG(INF, TYPE_ALL, NO_ERRNO,_("Initialize"));
snd_init_values(snd);
snd_load_params(snd);
snd_load_alerts(snd);
if (snd->snd_info->source == "alsa") {
snd_alsa_init(snd);
} else if (snd->snd_info->source == "pulse") {
snd_pulse_init(snd);
} else {
MOTPLS_LOG(ERR, TYPE_ALL, NO_ERRNO,_("Invalid sound source."));
snd->finish_dev = true;
snd->device_status = STATUS_CLOSED;
return;
}
snd_fftw_open(snd);
MOTPLS_LOG(NTC, TYPE_ALL, NO_ERRNO, _("Detecting"));
}
static void snd_check_alerts(ctx_dev *snd)
{
ctx_snd_info *info = snd->snd_info;
double freq_value;
int indx, chkval, chkcnt;
double pMaxIntensity;
int pMaxBinIndex;
double pRealNbr;
double pImaginaryNbr;
double pIntensity;
bool trigger;
std::list::iterator it;
struct timespec trig_ts;
for (indx=0;indx frames;indx++){
if (snd->conf->snd_window == "hamming") {
snd->snd_info->snd_fftw->ff_in[indx] = info->buffer[indx] * HammingWindow(indx, info->frames);
} else if (snd->conf->snd_window == "hann") {
snd->snd_info->snd_fftw->ff_in[indx] = info->buffer[indx] * HannWindow(indx, info->frames);
} else {
snd->snd_info->snd_fftw->ff_in[indx] = info->buffer[indx];
}
}
fftw_execute(snd->snd_info->snd_fftw->ff_plan);
pMaxIntensity = 0;
pMaxBinIndex = 0;
for (indx = snd->snd_info->snd_fftw->bin_min; indx <= snd->snd_info->snd_fftw->bin_max; indx++){
pRealNbr = snd->snd_info->snd_fftw->ff_out[indx][0];
pImaginaryNbr = snd->snd_info->snd_fftw->ff_out[indx][1];
pIntensity = pRealNbr * pRealNbr + pImaginaryNbr * pImaginaryNbr;
if (pIntensity > pMaxIntensity){
pMaxIntensity = pIntensity;
pMaxBinIndex = indx;
}
}
freq_value = (snd->snd_info->snd_fftw->bin_size * pMaxBinIndex * info->channels);
if (snd->conf->snd_show) {
MOTPLS_LOG(INF, TYPE_ALL, NO_ERRNO
, _("Freq: %.4f threshold: %d count: %d maximum: %d")
, freq_value, info->vol_min
, info->vol_count, info->vol_max);
}
for (it=info->alerts.begin(); it!=info->alerts.end(); it++) {
trigger = false;
if ((freq_value >= it->freq_low) && (freq_value <= it->freq_high)) {
chkcnt = 0;
for(indx=0; indx < info->frames; indx++) {
chkval = abs((int)info->buffer[indx] / 256);
if (chkval >= it->volume_level) {
chkcnt++;
}
}
if (chkcnt >= it->volume_count) {
trigger = true;
}
}
if (trigger) {
clock_gettime(CLOCK_MONOTONIC, &trig_ts);
if ((trig_ts.tv_sec - it->trigger_time.tv_sec) > it->trigger_duration) {
it->trigger_count = 1;
} else {
it->trigger_count++;
}
clock_gettime(CLOCK_MONOTONIC, &it->trigger_time);
if (it->trigger_count == it->trigger_threshold) {
MOTPLS_LOG(INF, TYPE_ALL, NO_ERRNO
, _("Sound Alert %d-%s : level %d count %d max vol %d")
, it->alert_id ,it->alert_nm.c_str()
, it->volume_level, chkcnt
, info->vol_max);
if (snd->conf->on_sound_alert != "") {
info->trig_freq =std::to_string(freq_value);
info->trig_nbr = std::to_string(it->alert_id);
info->trig_nm = it->alert_nm;
util_exec_command(snd, snd->conf->on_sound_alert.c_str(), NULL);
}
}
}
}
}
static void snd_check_levels(ctx_dev *snd)
{
ctx_snd_info *info = snd->snd_info;
int indx, chkval;
if (snd->device_status == STATUS_CLOSED) {
snd->finish_dev = true;
snd->restart_dev = false;
return;
}
info->vol_max = 0;
info->vol_count = 0;
for(indx=0; indx < info->frames; indx++) {
chkval = abs((int)info->buffer[indx] / 256);
if (chkval > info->vol_max) info->vol_max = chkval ;
if (chkval > info->vol_min) info->vol_count++;
}
if (info->vol_count > 0) {
snd_check_alerts(snd);
}
}
void *snd_loop(void *arg)
{
ctx_dev *snd = (ctx_dev *)arg;
pthread_mutex_lock(&snd->motapp->global_lock);
snd->motapp->threads_running++;
pthread_mutex_unlock(&snd->motapp->global_lock);
mythreadname_set("sl", snd->threadnr, snd->conf->device_name.c_str());
pthread_setspecific(tls_key_threadnr, (void *)((unsigned long)snd->threadnr));
snd->snd_info = new ctx_snd_info;
snd->snd_info->params = new ctx_params;
snd->snd_info->snd_fftw = new ctx_snd_fftw;
snd->snd_info->snd_alsa = new ctx_snd_alsa;
snd->snd_info->snd_pulse = new ctx_snd_pulse;
snd->running_dev = true;
snd->finish_dev = false;
snd->restart_dev = false;
snd->device_status = STATUS_INIT;
while (snd->finish_dev == false) {
slp_init(snd);
slp_capture(snd);
snd_check_levels(snd);
}
MOTPLS_LOG(NTC, TYPE_ALL, NO_ERRNO, _("Capture loop finished"));
snd_cleanup(snd);
pthread_mutex_lock(&snd->motapp->global_lock);
snd->motapp->threads_running--;
pthread_mutex_unlock(&snd->motapp->global_lock);
snd->finish_dev = true;
snd->running_dev = false;
delete snd->snd_info->snd_alsa;
delete snd->snd_info->snd_fftw;
delete snd->snd_info->snd_pulse;
delete snd->snd_info->params;
delete snd->snd_info;
MOTPLS_LOG(NTC, TYPE_ALL, NO_ERRNO, _("Exiting"));
pthread_exit(NULL);
}
#else
void *snd_loop(void *arg)
{
ctx_dev *snd =(ctx_dev *) arg;
snd->restart_dev = false;
snd->finish_dev = true;
snd->running_dev = false;
MOTPLS_LOG(NTC, TYPE_ALL, NO_ERRNO, _("Sound detection functionality not supported"));
pthread_exit(NULL);
}
#endif