2
* Copyright (c) Linux community.
4
* This library is free software; you can redistribute it and/or
5
* modify it under the terms of the GNU Library General Public
6
* License as published by the Free Software Foundation; either
7
* version 2 of the License, or (at your option) any later version.
9
* This library is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
* Library General Public License for more details.
14
* You should have received a copy of the GNU Library General Public
15
* License along with this library; if not, write to the
16
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17
* Boston, MA 02111-1307, USA.
20
#include "audio-sources.h"
25
#include "gst-pipeline.h"
26
#include "gst-recorder.h"
34
// GStreamer pipeline for VAD
35
static GstElement *g_vad_pipeline = NULL;
37
// Debug flag (--debug-signal or -d argument)
38
static gboolean g_debug_flag = FALSE;
40
// Parameters for VAD-pipeline
41
static PipelineParms *g_curr_parms = NULL;
43
// Time to wait between each test
44
#define TRIGGER_TIME_MS 150 // in milliseconds
46
static GstElement *vad_create_pipeline(PipelineParms *parms);
47
static void vad_save_parms(PipelineParms *new_parms);
48
static gboolean vad_is_running();
49
static void vad_shutdown_pipeline();
50
static gboolean vad_message_handler(GstBus * bus, GstMessage * message, gpointer data);
52
void vad_module_init() {
53
LOG_DEBUG("Init gst-vad.c.\n");
56
g_vad_pipeline = NULL;
61
void vad_module_exit() {
62
LOG_DEBUG("Clean up gst-vad.c.\n");
67
void vad_set_debug_flag(gboolean on_off) {
68
// Set debug flag. Please see application options:
69
// $ audio-recorder --help
70
// VAD process will now print level values in a terminal window.
71
g_debug_flag = on_off;
74
void vad_save_parms(PipelineParms *new_parms) {
76
pipeline_free_parms(g_curr_parms);
80
if (!new_parms) return;
83
g_curr_parms = new_parms;
86
void vad_start_VAD() {
87
PipelineParms *parms = g_malloc0(sizeof(PipelineParms));
89
// Get audio source and device list
91
parms->dev_list = audio_sources_get_device_NEW(&(parms->source));
93
gboolean changed = TRUE;
95
changed = g_strcmp0(parms->source, g_curr_parms->source);
96
changed = changed || (!str_lists_equal(parms->dev_list, g_curr_parms->dev_list));
105
// Already running (or values were unchanged)?
106
if (vad_is_running()) {
109
pipeline_free_parms(parms);
114
// Create new GStreamer pipeline for VAD
115
g_vad_pipeline = vad_create_pipeline(parms);
118
vad_save_parms(parms);
121
void vad_stop_VAD() {
123
vad_shutdown_pipeline();
125
// Reset static variables
126
vad_message_handler(NULL, NULL, NULL);
128
// Clear PipelineParms
129
vad_save_parms(NULL);
132
static gboolean vad_is_running() {
134
if (!GST_IS_OBJECT(g_vad_pipeline)) return FALSE;
137
// Ref: http://www.gstreamer.net/data/doc/gstreamer/head/gstreamer/html/GstElement.html#gst-element-get-state
138
GstState status, pending;
139
gst_element_get_state(g_vad_pipeline, &status, &pending, 0);
141
return (status != GST_STATE_NULL);
144
static void vad_check_triggers(GstClockTime timestamp, GstClockTimeDiff time_diff,
145
gdouble rms_dB_left, gdouble rms_dB_right, gdouble peak_dB) {
146
// From dB (decibel) to normalized value between [0 - 1.0].
148
// Normalized value [0 - 1.0] = exp(dB / 20).
149
// Decibel value = log(normalized value) * 20.
151
// Calc normalized values
152
gdouble rms_left = exp(rms_dB_left/20.0);
153
gdouble rms_right = exp(rms_dB_right/20.0);
154
gdouble peak = exp(peak_dB/20.0);
160
// Left channel gave a value?
161
if (rms_left > 0.001) {
166
// Right channel gave a value?
167
if (rms_right > 0.001) {
173
gdouble rms_avg = 0.0;
175
rms_avg = sum / count;
178
// Print values in a terminal window?
179
// Got --debug-signal or -d argument?
181
gdouble rms_dB_avg = rms_dB_left > -120 ? ((rms_dB_left + rms_dB_right)/2.0) : rms_dB_right;
182
g_print("Audio level. Time:%" GST_TIME_FORMAT ", RMS:%3.2f dB, normalized RMS:%3.2f, peak value:%3.2f.\n",
183
GST_TIME_ARGS(timestamp), rms_dB_avg, rms_avg, peak);
186
// Evaluate triggers related to RMS value
187
timer_evaluate_triggers(time_diff, rms_avg);
190
static gboolean vad_message_handler(GstBus * bus, GstMessage * message, gpointer data) {
191
static GstClockTime g_last_timestamp = 0L;
194
// Reset static variables
195
g_last_timestamp = 0L;
200
if (GST_MESSAGE_TYPE(message) != GST_MESSAGE_ELEMENT) {
204
const GstStructure *s = gst_message_get_structure(message);
205
const gchar *name = gst_structure_get_name(s);
207
GstClockTime timestamp;
208
if (!gst_structure_get_clock_time(s, "timestamp", ×tamp)) {
209
LOG_ERROR("vad_message_handler: Cannot read timestamp.\n");
212
// Wait at least TRIGGER_TIME_MS milliseconds
213
GstClockTimeDiff diff = GST_CLOCK_DIFF(g_last_timestamp, timestamp);
214
if (diff < TRIGGER_TIME_MS * GST_MSECOND) {
218
g_last_timestamp = timestamp;
220
if (g_str_equal(name, "level")) {
221
// Ref: http://www.gstreamer.net/data/doc/gstreamer/head/gst-plugins-good-plugins/html/gst-plugins-good-plugins-level.html
224
//level field:endtime
225
//level field:timestamp
226
//level field:stream-time
227
//level field:running-time
228
//level field:duration
233
const GValue *list = gst_structure_get_value(s, "rms");
234
const GValue *value = NULL;
236
gint channels = gst_value_list_get_size(list);
237
if (channels < 1) goto LBL_1;
239
gdouble rms_dB_left = -G_MAXDOUBLE; // left channel
240
gdouble rms_dB_right = -G_MAXDOUBLE; // right channel
242
guint count = gst_value_list_get_size(list);
245
value = gst_value_list_get_value(list, 0);
246
rms_dB_left = g_value_get_double(value);
250
value = gst_value_list_get_value(list, 1);
251
rms_dB_right = g_value_get_double(value);
255
list = gst_structure_get_value(s, "peak");
256
value = gst_value_list_get_value (list, 0);
257
gdouble peak_dB = g_value_get_double(value);
259
// Evaluate timer triggers
260
vad_check_triggers(timestamp, diff, rms_dB_left, rms_dB_right, peak_dB);
264
// We handled the message we want, and ignored the ones we didn't want. so the core can unref the message for us
268
static void vad_shutdown_pipeline() {
269
// Shutdown VAD-pipeline
270
if (!GST_IS_PIPELINE(g_vad_pipeline)) {
274
LOG_VAD("Shutdown VAD pipeline.\n");
276
// Switch to GST_STATE_NULL
277
gst_element_set_state(g_vad_pipeline, GST_STATE_NULL);
280
gst_object_unref(GST_OBJECT(g_vad_pipeline));
281
g_vad_pipeline = NULL;
287
static GstElement *vad_create_pipeline(PipelineParms *parms) {
288
if (!parms) return NULL;
290
#if defined(DEBUG_VAD) || defined(DEBUG_ALL)
291
LOG_VAD("Start VAD for \"%s\"\n", parms->source);
292
str_list_print("Monitor devices", parms->dev_list);
295
gchar *err_msg = NULL;
296
GstElement *pipeline = pipeline_create_VAD(parms, &err_msg);
299
if (!GST_IS_PIPELINE(pipeline) || err_msg) {
303
// Add message handler
304
GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
305
gst_bus_add_signal_watch(bus);
307
// Monitor "level" messages
308
g_signal_connect(bus, "message::element", G_CALLBACK(vad_message_handler), NULL);
310
gst_object_unref(bus);
313
if (gst_element_set_state(pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
314
err_msg = g_strdup(_("Cannot start reading from the stream/pipeline.\n"));
317
LOG_DEBUG("Pipeline for VAD (Voice Activity Detection) is running and OK.\n");
325
err_msg = g_strdup_printf(_("Cannot create audio pipeline. %s.\n"), "(VAD pipeline)");
331
if (G_IS_OBJECT(pipeline)) {
332
gst_element_set_state(pipeline, GST_STATE_NULL);
333
gst_object_unref(GST_OBJECT(pipeline));
340
gboolean vad_get_debug_flag() {
341
// --debug-signal or -d flag