From 5b04e77c0ce653150d60f8faa9c6909963b5825c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Fri, 15 Jan 2016 14:31:54 +0100 Subject: [PATCH] spandsp: Add tone generator --- ext/spandsp/Makefile.am | 4 +- ext/spandsp/gstspandsp.c | 4 +- ext/spandsp/gsttonegeneratesrc.c | 415 +++++++++++++++++++++++++++++++ ext/spandsp/gsttonegeneratesrc.h | 89 +++++++ 4 files changed, 509 insertions(+), 3 deletions(-) create mode 100644 ext/spandsp/gsttonegeneratesrc.c create mode 100644 ext/spandsp/gsttonegeneratesrc.h diff --git a/ext/spandsp/Makefile.am b/ext/spandsp/Makefile.am index 95ab3d91ec..405068eb7d 100644 --- a/ext/spandsp/Makefile.am +++ b/ext/spandsp/Makefile.am @@ -1,10 +1,10 @@ plugin_LTLIBRARIES = libgstspandsp.la -libgstspandsp_la_SOURCES = gstspandsp.c gstspanplc.c gstdtmfdetect.c +libgstspandsp_la_SOURCES = gstspandsp.c gstspanplc.c gstdtmfdetect.c gsttonegeneratesrc.c libgstspandsp_la_CFLAGS = $(GST_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(SPANDSP_CFLAGS) libgstspandsp_la_LIBADD = $(SPANDSP_LIBS) $(GST_PLUGINS_BASE_LIBS) \ $(GST_BASE_LIBS) $(GST_LIBS) libgstspandsp_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstspandsp_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) -noinst_HEADERS = gstspanplc.h gstdtmfdetect.h +noinst_HEADERS = gstspanplc.h gstdtmfdetect.h gsttonegeneratesrc.h diff --git a/ext/spandsp/gstspandsp.c b/ext/spandsp/gstspandsp.c index 0fdce79df0..37a0806697 100644 --- a/ext/spandsp/gstspandsp.c +++ b/ext/spandsp/gstspandsp.c @@ -26,13 +26,15 @@ #include "gstspanplc.h" #include "gstdtmfdetect.h" +#include "gsttonegeneratesrc.h" static gboolean plugin_init (GstPlugin * plugin) { return gst_element_register (plugin, "spanplc", GST_RANK_PRIMARY, GST_TYPE_SPAN_PLC) && - gst_dtmf_detect_plugin_init (plugin); + gst_dtmf_detect_plugin_init (plugin) && + gst_tone_generate_src_plugin_init (plugin); } GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, diff --git a/ext/spandsp/gsttonegeneratesrc.c b/ext/spandsp/gsttonegeneratesrc.c new file mode 100644 index 0000000000..2b0cae731d --- /dev/null +++ b/ext/spandsp/gsttonegeneratesrc.c @@ -0,0 +1,415 @@ +/* GStreamer + * Copyright (C) 2016 Iskratel d.o.o. + * Author: Okrslar Ales + * Copyright (C) 2016 Sebastian Dröge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gsttonegeneratesrc.h" + +#undef IT_DBG + +GST_DEBUG_CATEGORY_STATIC (tone_generate_src_debug); +#define GST_CAT_DEFAULT tone_generate_src_debug + +#define DEFAULT_SAMPLES_PER_BUFFER 1024 +#define DEFAULT_FREQ 0 +#define DEFAULT_VOLUME 0 +#define DEFAULT_ON_TIME 1000 +#define DEFAULT_OFF_TIME 1000 +#define DEFAULT_REPEAT FALSE + +enum +{ + PROP_0, + PROP_SAMPLES_PER_BUFFER, + PROP_FREQ, + PROP_VOLUME, + PROP_FREQ2, + PROP_VOLUME2, + PROP_ON_TIME, + PROP_OFF_TIME, + PROP_ON_TIME2, + PROP_OFF_TIME2, + PROP_REPEAT, + PROP_LAST +}; + +static GstStaticPadTemplate gst_tone_generate_src_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw, " + "format = (string) " GST_AUDIO_NE (S16) ", " + "layout = (string) interleaved, " "rate = (int) 8000, " "channels = 1") + ); + +#define gst_tone_generate_src_parent_class parent_class +G_DEFINE_TYPE (GstToneGenerateSrc, gst_tone_generate_src, GST_TYPE_PUSH_SRC); + +static void gst_tone_generate_src_finalize (GObject * object); +static void gst_tone_generate_src_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_tone_generate_src_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static gboolean gst_tone_generate_src_start (GstBaseSrc * basesrc); +static gboolean gst_tone_generate_src_stop (GstBaseSrc * basesrc); +static GstFlowReturn gst_tone_generate_src_fill (GstPushSrc * basesrc, + GstBuffer * buffer); + +static void +gst_tone_generate_src_class_init (GstToneGenerateSrcClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstBaseSrcClass *gstbasesrc_class; + GstPushSrcClass *gstpushsrc_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstbasesrc_class = (GstBaseSrcClass *) klass; + gstpushsrc_class = (GstPushSrcClass *) klass; + + gobject_class->set_property = gst_tone_generate_src_set_property; + gobject_class->get_property = gst_tone_generate_src_get_property; + gobject_class->finalize = gst_tone_generate_src_finalize; + + g_object_class_install_property (gobject_class, PROP_SAMPLES_PER_BUFFER, + g_param_spec_int ("samplesperbuffer", "Samples per buffer", + "Number of samples in each outgoing buffer", + 1, G_MAXINT, DEFAULT_SAMPLES_PER_BUFFER, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_FREQ, + g_param_spec_int ("freq", "Frequency", "Frequency of test signal", + 0, 20000, DEFAULT_FREQ, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_VOLUME, + g_param_spec_int ("volume", "Volume", + "Volume of first signal", + -50, 0, DEFAULT_VOLUME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_FREQ2, + g_param_spec_int ("freq2", "Second Frequency", + "Frequency of second telephony tone component", + 0, 20000, DEFAULT_FREQ, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_VOLUME2, + g_param_spec_int ("volume2", "Volume2", + "Volume of second tone signal", + -50, 0, DEFAULT_VOLUME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_ON_TIME, + g_param_spec_int ("on-time", "Signal ON time first period", + "Time of the first period when the tone signal is present", 1, + G_MAXINT, DEFAULT_ON_TIME, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_OFF_TIME, + g_param_spec_int ("off-time", "Signal OFF time first period ", + "Time of the first period when the tone signal is off", 0, G_MAXINT, + DEFAULT_OFF_TIME, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_ON_TIME2, + g_param_spec_int ("on-time2", "Signal ON time second period", + "Time of the second period when the tone signal is present", 1, + G_MAXINT, DEFAULT_ON_TIME, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_OFF_TIME2, + g_param_spec_int ("off-time2", "Signal OFF time first period ", + "Time of the second period when the tone signal is off", 0, G_MAXINT, + DEFAULT_ON_TIME, G_PARAM_READWRITE)); + + g_object_class_install_property (gobject_class, PROP_REPEAT, + g_param_spec_boolean ("repeat", "Repeat the specified tone period ", + "Whether to repeat specified tone indefinitly", DEFAULT_REPEAT, + G_PARAM_READWRITE)); + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_tone_generate_src_src_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "Telephony Tone Generator source", "Source/Audio", + "Creates telephony signals of given frequency, volume, cadence", + "Iskratel "); + + gstbasesrc_class->start = GST_DEBUG_FUNCPTR (gst_tone_generate_src_start); + gstbasesrc_class->stop = GST_DEBUG_FUNCPTR (gst_tone_generate_src_stop); + gstpushsrc_class->fill = GST_DEBUG_FUNCPTR (gst_tone_generate_src_fill); +} + +static void +gst_tone_generate_src_init (GstToneGenerateSrc * src) +{ + src->volume = DEFAULT_VOLUME; + src->freq = DEFAULT_FREQ; + src->on_time = DEFAULT_ON_TIME; + src->off_time = DEFAULT_OFF_TIME; + src->volume2 = DEFAULT_VOLUME; + src->freq2 = DEFAULT_FREQ; + src->on_time2 = DEFAULT_ON_TIME; + src->off_time2 = DEFAULT_OFF_TIME; + src->repeat = DEFAULT_REPEAT; + + gst_base_src_set_format (GST_BASE_SRC (src), GST_FORMAT_TIME); + + src->samples_per_buffer = DEFAULT_SAMPLES_PER_BUFFER; + gst_base_src_set_blocksize (GST_BASE_SRC (src), 2 * src->samples_per_buffer); +} + +static void +gst_tone_generate_src_finalize (GObject * object) +{ + GstToneGenerateSrc *src = GST_TONE_GENERATE_SRC (object); + + if (src->tone_desc) { + tone_gen_descriptor_free (src->tone_desc); + src->tone_desc = NULL; + } + + if (src->tone_state) { + tone_gen_free (src->tone_state); + src->tone_state = NULL; + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gboolean +gst_tone_generate_src_start (GstBaseSrc * basesrc) +{ + GstToneGenerateSrc *src = GST_TONE_GENERATE_SRC (basesrc); + + GST_OBJECT_LOCK (src); + src->properties_changed = FALSE; + GST_OBJECT_UNLOCK (src); + + src->next_sample = 0; + src->next_time = 0; + + return TRUE; +} + +static gboolean +gst_tone_generate_src_stop (GstBaseSrc * basesrc) +{ + GstToneGenerateSrc *src = GST_TONE_GENERATE_SRC (basesrc); + + GST_OBJECT_LOCK (src); + if (src->tone_desc) { + tone_gen_descriptor_free (src->tone_desc); + src->tone_desc = NULL; + } + + if (src->tone_state) { + tone_gen_free (src->tone_state); + src->tone_state = NULL; + } + src->properties_changed = FALSE; + GST_OBJECT_UNLOCK (src); + + return TRUE; +} + +static GstFlowReturn +gst_tone_generate_src_fill (GstPushSrc * basesrc, GstBuffer * buffer) +{ + GstToneGenerateSrc *src; + GstClockTime next_time; + gint64 next_sample; + gint bytes, samples; + GstMapInfo map; + const gint samplerate = 8000, bpf = 2; + + src = GST_TONE_GENERATE_SRC (basesrc); + + bytes = gst_buffer_get_size (buffer); + samples = bytes / bpf; + + /* calculate full buffer */ + next_sample = src->next_sample + samples; + + next_time = gst_util_uint64_scale_int (next_sample, GST_SECOND, samplerate); + + GST_LOG_OBJECT (src, "samplerate %d", samplerate); + GST_LOG_OBJECT (src, "next_sample %" G_GINT64_FORMAT ", ts %" GST_TIME_FORMAT, + next_sample, GST_TIME_ARGS (next_time)); + + GST_BUFFER_OFFSET (buffer) = src->next_sample; + GST_BUFFER_OFFSET_END (buffer) = next_sample; + GST_BUFFER_TIMESTAMP (buffer) = src->next_time; + GST_BUFFER_DURATION (buffer) = next_time - src->next_time; + + gst_object_sync_values (GST_OBJECT (src), GST_BUFFER_TIMESTAMP (buffer)); + + src->next_time = next_time; + src->next_sample = next_sample; + + GST_LOG_OBJECT (src, "generating %u samples at ts %" GST_TIME_FORMAT, + samples, GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer))); + + gst_buffer_map (buffer, &map, GST_MAP_WRITE); + + GST_OBJECT_LOCK (src); + if (!src->tone_state || src->properties_changed) { + src->tone_desc = tone_gen_descriptor_init (src->tone_desc, + src->freq, + src->volume, + src->freq2, + src->volume2, + src->on_time, + src->off_time, src->on_time2, src->off_time2, src->repeat); + + src->tone_state = tone_gen_init (src->tone_state, src->tone_desc); + src->properties_changed = FALSE; + } + + tone_gen (src->tone_state, (int16_t *) map.data, samples); + GST_OBJECT_UNLOCK (src); + + gst_buffer_unmap (buffer, &map); + + return GST_FLOW_OK; +} + +static void +gst_tone_generate_src_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstToneGenerateSrc *src = GST_TONE_GENERATE_SRC (object); + + switch (prop_id) { + case PROP_SAMPLES_PER_BUFFER: + src->samples_per_buffer = g_value_get_int (value); + gst_base_src_set_blocksize (GST_BASE_SRC_CAST (src), + 2 * src->samples_per_buffer); + break; + case PROP_FREQ: + GST_OBJECT_LOCK (src); + src->freq = g_value_get_int (value); + src->properties_changed = TRUE; + GST_OBJECT_UNLOCK (src); + break; + case PROP_VOLUME: + GST_OBJECT_LOCK (src); + src->volume = g_value_get_int (value); + src->properties_changed = TRUE; + GST_OBJECT_UNLOCK (src); + break; + case PROP_FREQ2: + GST_OBJECT_LOCK (src); + src->freq2 = g_value_get_int (value); + src->properties_changed = TRUE; + GST_OBJECT_UNLOCK (src); + break; + case PROP_VOLUME2: + GST_OBJECT_LOCK (src); + src->volume2 = g_value_get_int (value); + src->properties_changed = TRUE; + GST_OBJECT_UNLOCK (src); + break; + case PROP_ON_TIME: + GST_OBJECT_LOCK (src); + src->on_time = g_value_get_int (value); + src->properties_changed = TRUE; + GST_OBJECT_UNLOCK (src); + break; + case PROP_ON_TIME2: + GST_OBJECT_LOCK (src); + src->on_time2 = g_value_get_int (value); + src->properties_changed = TRUE; + GST_OBJECT_UNLOCK (src); + break; + case PROP_OFF_TIME: + GST_OBJECT_LOCK (src); + src->off_time = g_value_get_int (value); + src->properties_changed = TRUE; + GST_OBJECT_UNLOCK (src); + break; + case PROP_OFF_TIME2: + GST_OBJECT_LOCK (src); + src->off_time2 = g_value_get_int (value); + src->properties_changed = TRUE; + GST_OBJECT_UNLOCK (src); + break; + case PROP_REPEAT: + GST_OBJECT_LOCK (src); + src->repeat = g_value_get_boolean (value); + src->properties_changed = TRUE; + GST_OBJECT_UNLOCK (src); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_tone_generate_src_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstToneGenerateSrc *src = GST_TONE_GENERATE_SRC (object); + + switch (prop_id) { + case PROP_SAMPLES_PER_BUFFER: + g_value_set_int (value, src->samples_per_buffer); + break; + case PROP_FREQ: + g_value_set_int (value, src->freq); + break; + case PROP_VOLUME: + g_value_set_int (value, src->volume); + break; + case PROP_FREQ2: + g_value_set_int (value, src->freq2); + break; + case PROP_VOLUME2: + g_value_set_int (value, src->volume2); + break; + case PROP_ON_TIME: + g_value_set_int (value, src->on_time); + break; + case PROP_OFF_TIME: + g_value_set_int (value, src->off_time); + break; + case PROP_ON_TIME2: + g_value_set_int (value, src->on_time2); + break; + case PROP_OFF_TIME2: + g_value_set_int (value, src->off_time2); + break; + case PROP_REPEAT: + g_value_set_boolean (value, src->repeat); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +gboolean +gst_tone_generate_src_plugin_init (GstPlugin * plugin) +{ + GST_DEBUG_CATEGORY_INIT (tone_generate_src_debug, "tonegeneratesrc", 0, + "Telephony Tone Test Source"); + + return gst_element_register (plugin, "tonegeneratesrc", + GST_RANK_NONE, GST_TYPE_TONE_GENERATE_SRC); +} diff --git a/ext/spandsp/gsttonegeneratesrc.h b/ext/spandsp/gsttonegeneratesrc.h new file mode 100644 index 0000000000..200e13d43a --- /dev/null +++ b/ext/spandsp/gsttonegeneratesrc.h @@ -0,0 +1,89 @@ +/* GStreamer + * Copyright (C) 2016 Iskratel d.o.o. + * Author: Okrslar Ales + * Copyright (C) 2016 Sebastian Dröge + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_TONE_GENERATE_SRC_H__ +#define __GST_TONE_GENERATE_SRC_H__ + + +#include +#include + +#include +#include + +G_BEGIN_DECLS + +#define GST_TYPE_TONE_GENERATE_SRC \ + (gst_tone_generate_src_get_type()) +#define GST_TONE_GENERATE_SRC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_TONE_GENERATE_SRC,GstToneGenerateSrc)) +#define GST_TONE_GENERATE_SRC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_TONE_GENERATE_SRC,GstToneGenerateSrcClass)) +#define GST_IS_TONE_GENERATE_SRC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_TONE_GENERATE_SRC)) +#define GST_IS_TONE_GENERATE_SRC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_TONE_GENERATE_SRC)) + +typedef struct _GstToneGenerateSrc GstToneGenerateSrc; +typedef struct _GstToneGenerateSrcClass GstToneGenerateSrcClass; + +/** + * GstToneGenerateSrc: + * + * tonegeneratesrc object structure. + */ +struct _GstToneGenerateSrc { + GstPushSrc parent; + + /* parameters */ + gint volume; /* The level of the first frequency, in dBm0 */ + gint volume2; /* The level of the second frequency, in dBm0, or the percentage modulation depth for an AM modulated tone. */ + gint freq; /* The first frequency, in Hz */ + gint freq2; /* 0 for no second frequency, a positive number for the second frequency, in Hz, or a negative number for an AM modulation frequency, in Hz */ + gint on_time; /* On time for the first presence of tone signal. */ + gint off_time; /* Off time between first and second presence of tone signal. */ + gint on_time2; /* On time for the second presence of tone signal. */ + gint off_time2; /* Off time after the second presence of tone signal. */ + gboolean repeat; /* 0/1 if the tone repeates itself or not. */ + + /* audio parameters */ + gint samples_per_buffer; + + /*< private >*/ + GstClockTime next_time; /* next timestamp */ + gint64 next_sample; /* next sample to send */ + + /* SpanDSP */ + tone_gen_state_t *tone_state; + tone_gen_descriptor_t *tone_desc; + gboolean properties_changed; +}; + +struct _GstToneGenerateSrcClass { + GstPushSrcClass parent_class; +}; + +GType gst_tone_generate_src_get_type (void); +gboolean gst_tone_generate_src_plugin_init (GstPlugin *plugin); + +G_END_DECLS + +#endif /* __GST_TONE_GENERATE_SRC_H__ */