1 /* vim: set sts=2 sw=2 et: */
4 * Copyright (C) 2006 Josep Torra <j.torra@telefonica.net>
5 * 2008-2010 Jari Tenhunen <jari.tenhunen@iki.fi>
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
27 #include <gst/audio/audio.h>
31 GST_DEBUG_CATEGORY_STATIC (gst_pitch_debug);
32 #define GST_CAT_DEFAULT gst_pitch_debug
35 #define RATESTR "32000"
36 #define WANTED RATE * 2
37 #define ZERO_PADDING_FACTOR 2
38 #define FFT_LEN (RATE * ZERO_PADDING_FACTOR)
40 /* Filter signals and args */
51 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
54 GST_STATIC_CAPS ("audio/x-raw-int, "
55 "rate = (int) " RATESTR ", "
56 "channels = (int) 1, "
57 "endianness = (int) BYTE_ORDER, "
58 "width = (int) 16, " "depth = (int) 16, " "signed = (boolean) true")
61 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
64 GST_STATIC_CAPS ("audio/x-raw-int, "
65 "rate = (int) [ 1, MAX ], "
66 "channels = (int) [1, MAX], "
67 "endianness = (int) BYTE_ORDER, "
68 "width = (int) 16, " "depth = (int) 16, " "signed = (boolean) true")
71 #define DEBUG_INIT(bla) \
72 GST_DEBUG_CATEGORY_INIT (gst_pitch_debug, "Pitch", 0, "fundamental frequency plugin");
74 GST_BOILERPLATE_FULL (GstPitch, gst_pitch, GstBaseTransform,
75 GST_TYPE_BASE_TRANSFORM, DEBUG_INIT);
77 static void gst_pitch_set_property (GObject * object, guint prop_id,
78 const GValue * value, GParamSpec * pspec);
79 static void gst_pitch_get_property (GObject * object, guint prop_id,
80 GValue * value, GParamSpec * pspec);
81 static void gst_pitch_dispose (GObject * object);
83 static gboolean gst_pitch_set_caps (GstBaseTransform * trans, GstCaps * in,
85 static gboolean gst_pitch_start (GstBaseTransform * trans);
87 static GstFlowReturn gst_pitch_transform_ip (GstBaseTransform * trans,
90 #define DEFAULT_PROP_ALGORITHM GST_PITCH_ALGORITHM_FFT
92 #define GST_TYPE_PITCH_ALGORITHM (gst_pitch_algorithm_get_type())
94 gst_pitch_algorithm_get_type (void)
96 static GType pitch_algorithm_type = 0;
97 static const GEnumValue pitch_algorithm[] = {
98 {GST_PITCH_ALGORITHM_FFT, "fft", "fft"},
99 {GST_PITCH_ALGORITHM_HPS, "hps", "hps"},
103 if (!pitch_algorithm_type) {
104 pitch_algorithm_type =
105 g_enum_register_static ("GstPitchAlgorithm",
108 return pitch_algorithm_type;
111 /* GObject vmethod implementations */
114 gst_pitch_base_init (gpointer klass)
116 static GstElementDetails element_details = {
118 "Filter/Analyzer/Audio",
119 "Run an FFT on the audio signal, output fundamental frequency",
120 "Josep Torra <j.torra@telefonica.net>"
122 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
124 gst_element_class_add_pad_template (element_class,
125 gst_static_pad_template_get (&src_template));
126 gst_element_class_add_pad_template (element_class,
127 gst_static_pad_template_get (&sink_template));
128 gst_element_class_set_details (element_class, &element_details);
132 gst_pitch_class_init (GstPitchClass * klass)
134 GObjectClass *gobject_class;
135 GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS (klass);
137 gobject_class = (GObjectClass *) klass;
138 gobject_class->set_property = gst_pitch_set_property;
139 gobject_class->get_property = gst_pitch_get_property;
140 gobject_class->dispose = gst_pitch_dispose;
142 trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_pitch_set_caps);
143 trans_class->start = GST_DEBUG_FUNCPTR (gst_pitch_start);
144 trans_class->transform_ip = GST_DEBUG_FUNCPTR (gst_pitch_transform_ip);
145 trans_class->passthrough_on_same_caps = TRUE;
147 g_object_class_install_property (gobject_class, PROP_SIGNAL_FFREQ,
148 g_param_spec_boolean ("message", "Message",
149 "Post a fundamental frequency message for each passed interval",
150 TRUE, G_PARAM_READWRITE));
152 g_object_class_install_property (gobject_class, PROP_MINFREQ,
153 g_param_spec_int ("minfreq", "MinFreq",
154 "Initial scan frequency, default 30 Hz",
155 1, G_MAXINT, 30, G_PARAM_READWRITE));
157 g_object_class_install_property (gobject_class, PROP_MAXFREQ,
158 g_param_spec_int ("maxfreq", "MaxFreq",
159 "Final scan frequency, default 1500 Hz",
160 1, G_MAXINT, 1500, G_PARAM_READWRITE));
162 g_object_class_install_property (gobject_class, PROP_ALGORITHM,
163 g_param_spec_enum ("algorithm", "Algorithm",
164 "Pitch detection algorithm to use",
165 GST_TYPE_PITCH_ALGORITHM, DEFAULT_PROP_ALGORITHM,
169 GST_BASE_TRANSFORM_CLASS (klass)->transform_ip =
170 GST_DEBUG_FUNCPTR (gst_pitch_transform_ip);
174 gst_pitch_setup_algorithm (GstPitch * filter, GstPitchAlgorithm algorithm)
176 g_mutex_lock (filter->mutex);
177 if (algorithm == GST_PITCH_ALGORITHM_HPS) {
178 if (NULL == filter->module)
179 filter->module = (gint *) g_malloc (FFT_LEN * sizeof (gint));
183 g_free (filter->module);
185 filter->module = NULL;
187 filter->algorithm = algorithm;
188 g_mutex_unlock (filter->mutex);
192 gst_pitch_init (GstPitch * filter, GstPitchClass * klass)
194 filter->adapter = gst_adapter_new ();
196 filter->minfreq = 30;
197 filter->maxfreq = 1500;
198 filter->message = TRUE;
199 filter->mutex = g_mutex_new ();
201 gst_pitch_setup_algorithm (filter, DEFAULT_PROP_ALGORITHM);
205 gst_pitch_dispose (GObject * object)
207 GstPitch *filter = GST_PITCH (object);
209 if (filter->adapter) {
210 g_object_unref (filter->adapter);
211 filter->adapter = NULL;
214 g_free (filter->fft_cfg);
215 g_free (filter->signal);
216 g_free (filter->spectrum);
218 g_free (filter->module);
220 g_mutex_free (filter->mutex);
224 G_OBJECT_CLASS (parent_class)->dispose (object);
228 gst_pitch_set_property (GObject * object, guint prop_id,
229 const GValue * value, GParamSpec * pspec)
231 GstPitch *filter = GST_PITCH (object);
234 case PROP_SIGNAL_FFREQ:
235 filter->message = g_value_get_boolean (value);
238 filter->minfreq = g_value_get_int (value);
241 filter->maxfreq = g_value_get_int (value);
244 gst_pitch_setup_algorithm (filter, g_value_get_enum (value));
247 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
253 gst_pitch_get_property (GObject * object, guint prop_id,
254 GValue * value, GParamSpec * pspec)
256 GstPitch *filter = GST_PITCH (object);
259 case PROP_SIGNAL_FFREQ:
260 g_value_set_boolean (value, filter->message);
263 g_value_set_int (value, filter->minfreq);
266 g_value_set_int (value, filter->maxfreq);
269 g_value_set_enum (value, filter->algorithm);
272 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
278 gst_pitch_set_caps (GstBaseTransform * trans, GstCaps * in, GstCaps * out)
280 GstPitch *filter = GST_PITCH (trans);
282 filter->fft_cfg = kiss_fft_alloc (FFT_LEN, 0, NULL, NULL);
284 (kiss_fft_cpx *) g_malloc0 (FFT_LEN * sizeof (kiss_fft_cpx));
286 (kiss_fft_cpx *) g_malloc (FFT_LEN * sizeof (kiss_fft_cpx));
292 gst_pitch_start (GstBaseTransform * trans)
294 GstPitch *filter = GST_PITCH (trans);
296 gst_adapter_clear (filter->adapter);
302 gst_pitch_message_new (GstPitch * filter)
305 gint i, min_i, max_i;
306 gint freq_index, frequency_module;
309 /* Extract fundamental frequency */
311 frequency_module = 0;
313 min_i = filter->minfreq * ZERO_PADDING_FACTOR;
314 max_i = filter->maxfreq * ZERO_PADDING_FACTOR;
316 GST_DEBUG_OBJECT (filter, "min_freq = %d, max_freq = %d", filter->minfreq,
318 /*GST_DEBUG_OBJECT (filter, "min_i = %d, max_i = %d", min_i, max_i); */
320 g_mutex_lock (filter->mutex);
321 switch (filter->algorithm) {
323 case GST_PITCH_ALGORITHM_FFT:
327 for (i = min_i; i < max_i; i++) {
328 module = (filter->spectrum[i].r * filter->spectrum[i].r);
329 module += (filter->spectrum[i].i * filter->spectrum[i].i);
332 GST_LOG_OBJECT (filter, "module[%d] = %d", i, module);
334 /* find strongest peak */
335 if (module > frequency_module) {
336 frequency_module = module;
343 case GST_PITCH_ALGORITHM_HPS:
345 gint prev_frequency = 0;
348 for (i = min_i; i < FFT_LEN; i++) {
349 filter->module[i] = (filter->spectrum[i].r * filter->spectrum[i].r);
350 filter->module[i] += (filter->spectrum[i].i * filter->spectrum[i].i);
352 if (filter->module[i] > 0)
353 GST_LOG_OBJECT (filter, "module[%d] = %d", i, filter->module[i]);
356 /* Harmonic Product Spectrum algorithm */
357 #define MAX_DS_FACTOR (6)
358 for (i = min_i; (i <= max_i) && (i < FFT_LEN); i++) {
359 for (j = 2; j <= MAX_DS_FACTOR; j++) {
360 t = i * j * ZERO_PADDING_FACTOR;
364 /* this is not part of the HPS but it seems
365 * there are lots of zeroes in the spectrum ...
367 if (filter->module[t] != 0)
368 filter->module[i] *= filter->module[t];
371 /* find strongest peak */
372 if (filter->module[i] > frequency_module) {
373 prev_frequency = freq_index;
374 frequency_module = filter->module[i];
379 /* try to correct octave error */
381 if (freq_index != 0 && prev_frequency != 0) {
382 float ratio = (float) frequency / (float) prev_frequency;
383 if (ratio >= 1.9 && ratio < 2.1 && (float) filter->module[prev_frequency] >= 0.2 * (float) frequency_module ) {
384 g_debug("Chose freq %d[%d] over %d[%d]\n", prev_frequency, filter->module[prev_frequency], frequency, filter->module[frequency]);
385 frequency = prev_frequency;
386 frequency_module = filter->module[prev_frequency];
395 g_mutex_unlock (filter->mutex);
397 frequency = (gfloat) freq_index / ZERO_PADDING_FACTOR;
399 g_debug("freq %d[%d]\n", frequency, frequency_module);
401 GST_DEBUG_OBJECT (filter, "preparing message, frequency = %.2f", frequency);
403 s = gst_structure_new ("pitch", "frequency", G_TYPE_FLOAT, frequency, NULL);
405 return gst_message_new_element (GST_OBJECT (filter), s);
408 /* GstBaseTransform vmethod implementations */
410 /* this function does the actual processing
413 gst_pitch_transform_ip (GstBaseTransform * trans, GstBuffer * in)
415 GstPitch *filter = GST_PITCH (trans);
420 GST_DEBUG_OBJECT (filter, "transform : %ld bytes", GST_BUFFER_SIZE (in));
421 gst_adapter_push (filter->adapter, gst_buffer_ref (in));
423 /* required number of bytes */
424 avail = gst_adapter_available (filter->adapter);
425 GST_DEBUG_OBJECT (filter, "avail: %d wanted: %d", avail, WANTED);
427 if (avail > WANTED) {
429 /* copy sample data in the complex vector */
430 samples = (gint16 *) gst_adapter_peek (filter->adapter, WANTED);
432 for (i = 0; i < RATE; i++) {
433 filter->signal[i].r = (kiss_fft_scalar) (samples[i]);
436 /* flush half second of data to implement sliding window */
437 gst_adapter_flush (filter->adapter, WANTED >> 1);
439 GST_DEBUG ("perform fft");
440 kiss_fft (filter->fft_cfg, filter->signal, filter->spectrum);
442 if (filter->message) {
443 GstMessage *m = gst_pitch_message_new (filter);
444 gst_element_post_message (GST_ELEMENT (filter), m);
452 /* entry point to initialize the plug-in
453 * initialize the plug-in itself
454 * register the element factories and pad templates
455 * register the features
457 * exchange the string 'plugin' with your elemnt name
459 /* static */ gboolean
460 plugin_pitch_init (GstPlugin * plugin)
462 return gst_element_register (plugin, "pitch", GST_RANK_NONE, GST_TYPE_PITCH);
465 /* this is the structure that gstreamer looks for to register plugins
467 * exchange the strings 'plugin' and 'Template plugin' with you plugin name and
471 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
474 "Run an FFT on the audio signal, output fundamental frequency",
475 plugin_init, VERSION, "LGPL", "GStreamer", "http://gstreamer.net/")