3 * Copyright (C) 2006 Josep Torra <j.torra@telefonica.net>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
25 #include <gst/audio/audio.h>
29 GST_DEBUG_CATEGORY_STATIC (gst_pitch_debug);
30 #define GST_CAT_DEFAULT gst_pitch_debug
33 #define WANTED RATE * 2
35 /* Filter signals and args */
46 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
49 GST_STATIC_CAPS ("audio/x-raw-int, "
51 "channels = (int) 1, "
52 "endianness = (int) BYTE_ORDER, "
53 "width = (int) 16, " "depth = (int) 16, " "signed = (boolean) true")
56 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
59 GST_STATIC_CAPS ("audio/x-raw-int, "
60 "rate = (int) [ 1, MAX ], "
61 "channels = (int) [1, MAX], "
62 "endianness = (int) BYTE_ORDER, "
63 "width = (int) 16, " "depth = (int) 16, " "signed = (boolean) true")
66 #define DEBUG_INIT(bla) \
67 GST_DEBUG_CATEGORY_INIT (gst_pitch_debug, "Pitch", 0, "fundamental frequency plugin");
69 GST_BOILERPLATE_FULL (GstPitch, gst_pitch, GstBaseTransform,
70 GST_TYPE_BASE_TRANSFORM, DEBUG_INIT);
72 static void gst_pitch_set_property (GObject * object, guint prop_id,
73 const GValue * value, GParamSpec * pspec);
74 static void gst_pitch_get_property (GObject * object, guint prop_id,
75 GValue * value, GParamSpec * pspec);
76 static void gst_pitch_dispose (GObject * object);
78 static gboolean gst_pitch_set_caps (GstBaseTransform * trans, GstCaps * in,
80 static gboolean gst_pitch_start (GstBaseTransform * trans);
82 static GstFlowReturn gst_pitch_transform_ip (GstBaseTransform * trans,
85 /* GObject vmethod implementations */
88 gst_pitch_base_init (gpointer klass)
90 static GstElementDetails element_details = {
92 "Filter/Analyzer/Audio",
93 "Run an FFT on the audio signal, output fundamental frequency",
94 "Josep Torra <j.torra@telefonica.net>"
96 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
98 gst_element_class_add_pad_template (element_class,
99 gst_static_pad_template_get (&src_template));
100 gst_element_class_add_pad_template (element_class,
101 gst_static_pad_template_get (&sink_template));
102 gst_element_class_set_details (element_class, &element_details);
106 gst_pitch_class_init (GstPitchClass * klass)
108 GObjectClass *gobject_class;
109 GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS (klass);
111 gobject_class = (GObjectClass *) klass;
112 gobject_class->set_property = gst_pitch_set_property;
113 gobject_class->get_property = gst_pitch_get_property;
114 gobject_class->dispose = gst_pitch_dispose;
116 trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_pitch_set_caps);
117 trans_class->start = GST_DEBUG_FUNCPTR (gst_pitch_start);
118 trans_class->transform_ip = GST_DEBUG_FUNCPTR (gst_pitch_transform_ip);
119 trans_class->passthrough_on_same_caps = TRUE;
121 g_object_class_install_property (gobject_class, PROP_SIGNAL_FFREQ,
122 g_param_spec_boolean ("message", "Message",
123 "Post a fundamental frequency message for each passed interval",
124 TRUE, G_PARAM_READWRITE));
126 g_object_class_install_property (gobject_class, PROP_SIGNAL_MINFREQ,
127 g_param_spec_int ("minfreq", "MinFreq",
128 "Initial scan frequency, default 30 Hz",
129 1, G_MAXINT, 30, G_PARAM_READWRITE));
131 g_object_class_install_property (gobject_class, PROP_SIGNAL_MAXFREQ,
132 g_param_spec_int ("maxfreq", "MaxFreq",
133 "Final scan frequency, default 1500 Hz",
134 1, G_MAXINT, 1500, G_PARAM_READWRITE));
137 GST_BASE_TRANSFORM_CLASS (klass)->transform_ip =
138 GST_DEBUG_FUNCPTR (gst_pitch_transform_ip);
142 gst_pitch_init (GstPitch * filter, GstPitchClass * klass)
144 filter->adapter = gst_adapter_new ();
146 filter->minfreq = 30;
147 filter->maxfreq = 1500;
148 filter->message = TRUE;
152 gst_pitch_dispose (GObject * object)
154 GstPitch *filter = GST_PITCH (object);
156 if (filter->adapter) {
157 g_object_unref (filter->adapter);
158 filter->adapter = NULL;
161 g_free (filter->fft_cfg);
162 g_free (filter->signal);
163 g_free (filter->spectrum);
167 G_OBJECT_CLASS (parent_class)->dispose (object);
171 gst_pitch_set_property (GObject * object, guint prop_id,
172 const GValue * value, GParamSpec * pspec)
174 GstPitch *filter = GST_PITCH (object);
177 case PROP_SIGNAL_FFREQ:
178 filter->message = g_value_get_boolean (value);
180 case PROP_SIGNAL_MINFREQ:
181 filter->minfreq = g_value_get_int (value);
183 case PROP_SIGNAL_MAXFREQ:
184 filter->maxfreq = g_value_get_int (value);
187 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
193 gst_pitch_get_property (GObject * object, guint prop_id,
194 GValue * value, GParamSpec * pspec)
196 GstPitch *filter = GST_PITCH (object);
199 case PROP_SIGNAL_FFREQ:
200 g_value_set_boolean (value, filter->message);
202 case PROP_SIGNAL_MINFREQ:
203 g_value_set_int (value, filter->minfreq);
205 case PROP_SIGNAL_MAXFREQ:
206 g_value_set_int (value, filter->maxfreq);
209 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
215 gst_pitch_set_caps (GstBaseTransform * trans, GstCaps * in, GstCaps * out)
217 GstPitch *filter = GST_PITCH (trans);
219 filter->fft_cfg = kiss_fft_alloc (RATE, 0, NULL, NULL);
221 (kiss_fft_cpx *) g_malloc (RATE * sizeof (kiss_fft_cpx));
223 (kiss_fft_cpx *) g_malloc (RATE * sizeof (kiss_fft_cpx));
229 gst_pitch_start (GstBaseTransform * trans)
231 GstPitch *filter = GST_PITCH (trans);
233 gst_adapter_clear (filter->adapter);
239 gst_pitch_message_new (GstPitch * filter)
242 gint i, min_i, max_i;
243 gint frequency, frequency_module, module;
245 /* Extract fundamental frequency */
247 frequency_module = 0;
248 min_i = filter->minfreq;
249 max_i = filter->maxfreq;
250 GST_DEBUG_OBJECT (filter, "min_freq = %d, max_freq = %d", filter->minfreq,
252 GST_DEBUG_OBJECT (filter, "min_i = %d, max_i = %d", min_i, max_i);
253 for (i = min_i; (i <= max_i) && (i < RATE); i++) {
254 module = (filter->spectrum[i].r * filter->spectrum[i].r);
255 module += (filter->spectrum[i].i * filter->spectrum[i].i);
258 GST_LOG_OBJECT (filter, "module[%d] = %d", i, module);
260 if (module > frequency_module) {
261 frequency_module = module;
266 GST_DEBUG_OBJECT (filter, "preparing message, frequency = %d ", frequency);
268 s = gst_structure_new ("pitch", "frequency", G_TYPE_INT, frequency, NULL);
270 return gst_message_new_element (GST_OBJECT (filter), s);
273 /* GstBaseTransform vmethod implementations */
275 /* this function does the actual processing
278 gst_pitch_transform_ip (GstBaseTransform * trans, GstBuffer * in)
280 GstPitch *filter = GST_PITCH (trans);
285 GST_DEBUG_OBJECT (filter, "transform : %ld bytes", GST_BUFFER_SIZE (in));
286 gst_adapter_push (filter->adapter, gst_buffer_ref (in));
288 /* required number of bytes */
289 avail = gst_adapter_available (filter->adapter);
290 GST_DEBUG_OBJECT (filter, "avail: %d wanted: %d", avail, WANTED);
292 if (avail > WANTED) {
294 /* copy sample data in the complex vector */
295 samples = (gint16 *) gst_adapter_peek (filter->adapter, WANTED);
297 for (i = 0; i < RATE; i++) {
298 filter->signal[i].r = (kiss_fft_scalar) (samples[i]);
301 /* flush half second of data to implement sliding window */
302 gst_adapter_flush (filter->adapter, WANTED >> 1);
304 GST_DEBUG ("perform fft");
305 kiss_fft (filter->fft_cfg, filter->signal, filter->spectrum);
307 if (filter->message) {
308 GstMessage *m = gst_pitch_message_new (filter);
309 gst_element_post_message (GST_ELEMENT (filter), m);
317 /* entry point to initialize the plug-in
318 * initialize the plug-in itself
319 * register the element factories and pad templates
320 * register the features
322 * exchange the string 'plugin' with your elemnt name
324 /* static */ gboolean
325 plugin_pitch_init (GstPlugin * plugin)
327 return gst_element_register (plugin, "pitch", GST_RANK_NONE, GST_TYPE_PITCH);
330 /* this is the structure that gstreamer looks for to register plugins
332 * exchange the strings 'plugin' and 'Template plugin' with you plugin name and
336 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
339 "Run an FFT on the audio signal, output fundamental frequency",
340 plugin_init, VERSION, "LGPL", "GStreamer", "http://gstreamer.net/")