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
32 /* Filter signals and args */
43 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
46 GST_STATIC_CAPS ("audio/x-raw-int, "
47 "rate = (int) [ 1, MAX ], "
48 "channels = (int) [1, MAX], "
49 "endianness = (int) BYTE_ORDER, "
50 "width = (int) 16, " "depth = (int) 16, " "signed = (boolean) true")
53 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
56 GST_STATIC_CAPS ("audio/x-raw-int, "
57 "rate = (int) [ 1, MAX ], "
58 "channels = (int) [1, MAX], "
59 "endianness = (int) BYTE_ORDER, "
60 "width = (int) 16, " "depth = (int) 16, " "signed = (boolean) true")
63 #define DEBUG_INIT(bla) \
64 GST_DEBUG_CATEGORY_INIT (gst_pitch_debug, "Pitch", 0, "fundamental frequency plugin");
66 GST_BOILERPLATE_FULL (GstPitch, gst_pitch, GstBaseTransform,
67 GST_TYPE_BASE_TRANSFORM, DEBUG_INIT);
69 static void gst_pitch_set_property (GObject * object, guint prop_id,
70 const GValue * value, GParamSpec * pspec);
71 static void gst_pitch_get_property (GObject * object, guint prop_id,
72 GValue * value, GParamSpec * pspec);
73 static void gst_pitch_dispose (GObject * object);
75 static gboolean gst_pitch_set_caps (GstBaseTransform * trans, GstCaps * in,
77 static gboolean gst_pitch_start (GstBaseTransform * trans);
79 static GstFlowReturn gst_pitch_transform_ip (GstBaseTransform * trans,
82 /* GObject vmethod implementations */
85 gst_pitch_base_init (gpointer klass)
87 static GstElementDetails element_details = {
89 "Filter/Analyzer/Audio",
90 "Run an FFT on the audio signal, output fundamental frequency",
91 "Josep Torra <j.torra@telefonica.net>"
93 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
95 gst_element_class_add_pad_template (element_class,
96 gst_static_pad_template_get (&src_template));
97 gst_element_class_add_pad_template (element_class,
98 gst_static_pad_template_get (&sink_template));
99 gst_element_class_set_details (element_class, &element_details);
103 gst_pitch_class_init (GstPitchClass * klass)
105 GObjectClass *gobject_class;
106 GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS (klass);
108 gobject_class = (GObjectClass *) klass;
109 gobject_class->set_property = gst_pitch_set_property;
110 gobject_class->get_property = gst_pitch_get_property;
111 gobject_class->dispose = gst_pitch_dispose;
113 trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_pitch_set_caps);
114 trans_class->start = GST_DEBUG_FUNCPTR (gst_pitch_start);
115 trans_class->transform_ip = GST_DEBUG_FUNCPTR (gst_pitch_transform_ip);
116 trans_class->passthrough_on_same_caps = TRUE;
118 g_object_class_install_property (gobject_class, PROP_SIGNAL_FFREQ,
119 g_param_spec_boolean ("message", "Message",
120 "Post a fundamental frequency message for each passed interval",
121 TRUE, G_PARAM_READWRITE));
123 g_object_class_install_property (gobject_class, PROP_SIGNAL_INTERVAL,
124 g_param_spec_uint64 ("interval", "Interval",
125 "Interval of time between message posts (in nanoseconds)",
126 1, G_MAXUINT64, GST_SECOND / 10, G_PARAM_READWRITE));
128 g_object_class_install_property (gobject_class, PROP_SIGNAL_MINFREQ,
129 g_param_spec_int ("minfreq", "MinFreq",
130 "Initial scan frequency, default 30 Hz",
131 1, G_MAXINT, 30, G_PARAM_READWRITE));
133 g_object_class_install_property (gobject_class, PROP_SIGNAL_MAXFREQ,
134 g_param_spec_int ("maxfreq", "MaxFreq",
135 "Final scan frequency, default 1500 Hz",
136 1, G_MAXINT, 1500, G_PARAM_READWRITE));
138 g_object_class_install_property (gobject_class, PROP_NFFT,
139 g_param_spec_int ("nfft", "NFFT",
140 "Number of samples taken for FFT",
141 1, G_MAXINT, 1024, G_PARAM_READWRITE));
143 GST_BASE_TRANSFORM_CLASS (klass)->transform_ip =
144 GST_DEBUG_FUNCPTR (gst_pitch_transform_ip);
148 gst_pitch_init (GstPitch * filter, GstPitchClass * klass)
150 filter->adapter = gst_adapter_new ();
152 filter->minfreq = 30;
153 filter->maxfreq = 1500;
155 filter->message = TRUE;
156 filter->interval = GST_SECOND / 10;
158 filter->fft_cfg = kiss_fft_alloc (filter->nfft, 0, NULL, NULL);
160 (kiss_fft_cpx *) g_malloc (filter->nfft * sizeof (kiss_fft_cpx));
162 (kiss_fft_cpx *) g_malloc (filter->nfft * sizeof (kiss_fft_cpx));
166 gst_pitch_dispose (GObject * object)
168 GstPitch *filter = GST_PITCH (object);
170 if (filter->adapter) {
171 g_object_unref (filter->adapter);
172 filter->adapter = NULL;
175 g_free (filter->fft_cfg);
176 g_free (filter->signal);
177 g_free (filter->spectrum);
181 G_OBJECT_CLASS (parent_class)->dispose (object);
185 gst_pitch_set_property (GObject * object, guint prop_id,
186 const GValue * value, GParamSpec * pspec)
188 GstPitch *filter = GST_PITCH (object);
191 case PROP_SIGNAL_FFREQ:
192 filter->message = g_value_get_boolean (value);
194 case PROP_SIGNAL_INTERVAL:
195 filter->interval = gst_guint64_to_gdouble (g_value_get_uint64 (value));
197 case PROP_SIGNAL_MINFREQ:
198 filter->minfreq = g_value_get_int (value);
200 case PROP_SIGNAL_MAXFREQ:
201 filter->maxfreq = g_value_get_int (value);
204 filter->nfft = g_value_get_int (value);
205 g_free (filter->fft_cfg);
206 g_free (filter->signal);
207 g_free (filter->spectrum);
208 filter->fft_cfg = kiss_fft_alloc (filter->nfft, 0, NULL, NULL);
210 (kiss_fft_cpx *) g_malloc (filter->nfft * sizeof (kiss_fft_cpx));
212 (kiss_fft_cpx *) g_malloc (filter->nfft * sizeof (kiss_fft_cpx));
216 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
222 gst_pitch_get_property (GObject * object, guint prop_id,
223 GValue * value, GParamSpec * pspec)
225 GstPitch *filter = GST_PITCH (object);
228 case PROP_SIGNAL_FFREQ:
229 g_value_set_boolean (value, filter->message);
231 case PROP_SIGNAL_INTERVAL:
232 g_value_set_uint64 (value, filter->interval);
234 case PROP_SIGNAL_MINFREQ:
235 g_value_set_int (value, filter->minfreq);
237 case PROP_SIGNAL_MAXFREQ:
238 g_value_set_int (value, filter->maxfreq);
241 g_value_set_int (value, filter->nfft);
245 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
251 gst_pitch_set_caps (GstBaseTransform * trans, GstCaps * in, GstCaps * out)
253 GstPitch *filter = GST_PITCH (trans);
254 GstStructure *structure;
256 structure = gst_caps_get_structure (in, 0);
257 gst_structure_get_int (structure, "rate", &filter->rate);
258 gst_structure_get_int (structure, "width", &filter->width);
259 gst_structure_get_int (structure, "channels", &filter->channels);
265 gst_pitch_start (GstBaseTransform * trans)
267 GstPitch *filter = GST_PITCH (trans);
269 gst_adapter_clear (filter->adapter);
270 filter->num_frames = 0;
276 gst_pitch_message_new (GstPitch * filter)
279 gint i, min_i, max_i;
280 gint frequency, frequency_module, module;
282 /* Extract fundamental frequency */
284 frequency_module = 0;
285 min_i = filter->minfreq * filter->nfft / filter->rate;
286 max_i = filter->maxfreq * filter->nfft / filter->rate;
287 GST_DEBUG_OBJECT (filter, "min_freq = %d, max_freq = %d", filter->minfreq,
289 GST_DEBUG_OBJECT (filter, "min_i = %d, max_i = %d", min_i, max_i);
290 for (i = min_i; (i <= max_i) && (i < filter->nfft); i++) {
291 module = (filter->spectrum[i].r * filter->spectrum[i].r);
292 module += (filter->spectrum[i].i * filter->spectrum[i].i);
295 GST_DEBUG_OBJECT (filter, "module[%d] = %d", i, module);
297 if (module > frequency_module) {
298 frequency_module = module;
303 frequency = frequency * filter->rate / filter->nfft;
305 GST_DEBUG_OBJECT (filter, "preparing message, frequency = %d ", frequency);
307 s = gst_structure_new ("pitch", "frequency", G_TYPE_INT, frequency, NULL);
309 return gst_message_new_element (GST_OBJECT (filter), s);
312 /* GstBaseTransform vmethod implementations */
314 /* this function does the actual processing
317 gst_pitch_transform_ip (GstBaseTransform * trans, GstBuffer * in)
319 GstPitch *filter = GST_PITCH (trans);
325 GST_DEBUG ("transform : %ld bytes", GST_BUFFER_SIZE (in));
327 gst_adapter_push (filter->adapter, gst_buffer_ref (in));
328 /* required number of bytes */
329 wanted = filter->channels * filter->nfft * 2;
331 while (gst_adapter_available (filter->adapter) > wanted) {
333 GST_DEBUG (" adapter loop");
334 samples = (gint16 *) gst_adapter_take (filter->adapter, wanted);
336 for (i = 0, j = 0; i < filter->nfft; i++) {
337 for (k = 0, acc = 0; k < filter->channels; k++)
339 filter->signal[i].r = (kiss_fft_scalar) (acc / filter->channels);
344 kiss_fft (filter->fft_cfg, filter->signal, filter->spectrum);
346 GST_DEBUG (" send message? %d", filter->num_frames);
347 filter->num_frames += filter->nfft;
348 /* do we need to message ? */
349 if (filter->num_frames >=
350 GST_CLOCK_TIME_TO_FRAMES (filter->interval, filter->rate)) {
351 if (filter->message) {
352 GstMessage *m = gst_pitch_message_new (filter);
354 GST_DEBUG (" sending message");
355 gst_element_post_message (GST_ELEMENT (filter), m);
357 filter->num_frames = 0;
365 /* entry point to initialize the plug-in
366 * initialize the plug-in itself
367 * register the element factories and pad templates
368 * register the features
370 * exchange the string 'plugin' with your elemnt name
372 /* static */ gboolean
373 plugin_pitch_init (GstPlugin * plugin)
375 return gst_element_register (plugin, "pitch", GST_RANK_NONE, GST_TYPE_PITCH);
378 /* this is the structure that gstreamer looks for to register plugins
380 * exchange the strings 'plugin' and 'Template plugin' with you plugin name and
384 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
387 "Run an FFT on the audio signal, output fundamental frequency",
388 plugin_init, VERSION, "LGPL", "GStreamer", "http://gstreamer.net/")