* src/gstpitch.c: (gst_pitch_class_init), (gst_pitch_init),
[tunertool] / src / gstpitch.c
1 /*
2  * GStreamer
3  * Copyright (C) 2006 Josep Torra <j.torra@telefonica.net>
4  *
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.
9  *
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.
14  *
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.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <gst/audio/audio.h>
26
27 #include "gstpitch.h"
28
29 GST_DEBUG_CATEGORY_STATIC (gst_pitch_debug);
30 #define GST_CAT_DEFAULT gst_pitch_debug
31
32 #define RATE    8000
33 #define WANTED  RATE * 2
34
35 /* Filter signals and args */
36 enum
37 {
38   PROP_0,
39   PROP_SIGNAL_FFREQ,
40   PROP_SIGNAL_INTERVAL,
41   PROP_SIGNAL_MINFREQ,
42   PROP_SIGNAL_MAXFREQ,
43   PROP_NFFT
44 };
45
46 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
47     GST_PAD_SINK,
48     GST_PAD_ALWAYS,
49     GST_STATIC_CAPS ("audio/x-raw-int, "
50         "rate = (int) 8000, "
51         "channels = (int) 1, "
52         "endianness = (int) BYTE_ORDER, "
53         "width = (int) 16, " "depth = (int) 16, " "signed = (boolean) true")
54     );
55
56 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
57     GST_PAD_SRC,
58     GST_PAD_ALWAYS,
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")
64     );
65
66 #define DEBUG_INIT(bla) \
67   GST_DEBUG_CATEGORY_INIT (gst_pitch_debug, "Pitch", 0, "fundamental frequency plugin");
68
69 GST_BOILERPLATE_FULL (GstPitch, gst_pitch, GstBaseTransform,
70     GST_TYPE_BASE_TRANSFORM, DEBUG_INIT);
71
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);
77
78 static gboolean gst_pitch_set_caps (GstBaseTransform * trans, GstCaps * in,
79     GstCaps * out);
80 static gboolean gst_pitch_start (GstBaseTransform * trans);
81
82 static GstFlowReturn gst_pitch_transform_ip (GstBaseTransform * trans,
83     GstBuffer * in);
84
85 /* GObject vmethod implementations */
86
87 static void
88 gst_pitch_base_init (gpointer klass)
89 {
90   static GstElementDetails element_details = {
91     "Pitch analyzer",
92     "Filter/Analyzer/Audio",
93     "Run an FFT on the audio signal, output fundamental frequency",
94     "Josep Torra <j.torra@telefonica.net>"
95   };
96   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
97
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);
103 }
104
105 static void
106 gst_pitch_class_init (GstPitchClass * klass)
107 {
108   GObjectClass *gobject_class;
109   GstBaseTransformClass *trans_class = GST_BASE_TRANSFORM_CLASS (klass);
110
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;
115
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;
120
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));
125
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));
130
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));
135
136
137   GST_BASE_TRANSFORM_CLASS (klass)->transform_ip =
138       GST_DEBUG_FUNCPTR (gst_pitch_transform_ip);
139 }
140
141 static void
142 gst_pitch_init (GstPitch * filter, GstPitchClass * klass)
143 {
144   filter->adapter = gst_adapter_new ();
145
146   filter->minfreq = 30;
147   filter->maxfreq = 1500;
148   filter->message = TRUE;
149 }
150
151 static void
152 gst_pitch_dispose (GObject * object)
153 {
154   GstPitch *filter = GST_PITCH (object);
155
156   if (filter->adapter) {
157     g_object_unref (filter->adapter);
158     filter->adapter = NULL;
159   }
160
161   g_free (filter->fft_cfg);
162   g_free (filter->signal);
163   g_free (filter->spectrum);
164
165   kiss_fft_cleanup ();
166
167   G_OBJECT_CLASS (parent_class)->dispose (object);
168 }
169
170 static void
171 gst_pitch_set_property (GObject * object, guint prop_id,
172     const GValue * value, GParamSpec * pspec)
173 {
174   GstPitch *filter = GST_PITCH (object);
175
176   switch (prop_id) {
177     case PROP_SIGNAL_FFREQ:
178       filter->message = g_value_get_boolean (value);
179       break;
180     case PROP_SIGNAL_MINFREQ:
181       filter->minfreq = g_value_get_int (value);
182       break;
183     case PROP_SIGNAL_MAXFREQ:
184       filter->maxfreq = g_value_get_int (value);
185       break;
186     default:
187       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
188       break;
189   }
190 }
191
192 static void
193 gst_pitch_get_property (GObject * object, guint prop_id,
194     GValue * value, GParamSpec * pspec)
195 {
196   GstPitch *filter = GST_PITCH (object);
197
198   switch (prop_id) {
199     case PROP_SIGNAL_FFREQ:
200       g_value_set_boolean (value, filter->message);
201       break;
202     case PROP_SIGNAL_MINFREQ:
203       g_value_set_int (value, filter->minfreq);
204       break;
205     case PROP_SIGNAL_MAXFREQ:
206       g_value_set_int (value, filter->maxfreq);
207       break;
208     default:
209       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
210       break;
211   }
212 }
213
214 static gboolean
215 gst_pitch_set_caps (GstBaseTransform * trans, GstCaps * in, GstCaps * out)
216 {
217   GstPitch *filter = GST_PITCH (trans);
218
219   filter->fft_cfg = kiss_fft_alloc (RATE, 0, NULL, NULL);
220   filter->signal =
221       (kiss_fft_cpx *) g_malloc (RATE * sizeof (kiss_fft_cpx));
222   filter->spectrum =
223       (kiss_fft_cpx *) g_malloc (RATE * sizeof (kiss_fft_cpx));
224
225   return TRUE;
226 }
227
228 static gboolean
229 gst_pitch_start (GstBaseTransform * trans)
230 {
231   GstPitch *filter = GST_PITCH (trans);
232
233   gst_adapter_clear (filter->adapter);
234
235   return TRUE;
236 }
237
238 static GstMessage *
239 gst_pitch_message_new (GstPitch * filter)
240 {
241   GstStructure *s;
242   gint i, min_i, max_i;
243   gint frequency, frequency_module, module;
244
245   /* Extract fundamental frequency */
246   frequency = 0;
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,
251       filter->maxfreq);
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);
256
257     if (module > 0)
258       GST_LOG_OBJECT (filter, "module[%d] = %d", i, module);
259
260     if (module > frequency_module) {
261       frequency_module = module;
262       frequency = i;
263     }
264   }
265
266   GST_DEBUG_OBJECT (filter, "preparing message, frequency = %d ", frequency);
267
268   s = gst_structure_new ("pitch", "frequency", G_TYPE_INT, frequency, NULL);
269
270   return gst_message_new_element (GST_OBJECT (filter), s);
271 }
272
273 /* GstBaseTransform vmethod implementations */
274
275 /* this function does the actual processing
276  */
277 static GstFlowReturn
278 gst_pitch_transform_ip (GstBaseTransform * trans, GstBuffer * in)
279 {
280   GstPitch *filter = GST_PITCH (trans);
281   gint16 *samples;
282   gint i;
283   guint avail;
284
285   GST_DEBUG_OBJECT (filter, "transform : %ld bytes", GST_BUFFER_SIZE (in));
286   gst_adapter_push (filter->adapter, gst_buffer_ref (in));
287
288   /* required number of bytes */
289   avail = gst_adapter_available (filter->adapter);
290   GST_DEBUG_OBJECT (filter, "avail: %d wanted: %d", avail, WANTED);
291
292   if (avail > WANTED) {
293
294     /* copy sample data in the complex vector */
295     samples = (gint16 *) gst_adapter_peek (filter->adapter, WANTED);
296
297     for (i = 0; i < RATE; i++) {
298       filter->signal[i].r = (kiss_fft_scalar) (samples[i]);
299     }
300
301     /* flush half second of data to implement sliding window */
302     gst_adapter_flush (filter->adapter, WANTED >> 1);
303
304     GST_DEBUG ("perform fft");
305     kiss_fft (filter->fft_cfg, filter->signal, filter->spectrum);
306
307     if (filter->message) {
308       GstMessage *m = gst_pitch_message_new (filter);
309       gst_element_post_message (GST_ELEMENT (filter), m);
310     }
311   }
312
313   return GST_FLOW_OK;
314 }
315
316
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
321  *
322  * exchange the string 'plugin' with your elemnt name
323  */
324 /* static */ gboolean
325 plugin_pitch_init (GstPlugin * plugin)
326 {
327   return gst_element_register (plugin, "pitch", GST_RANK_NONE, GST_TYPE_PITCH);
328 }
329
330 /* this is the structure that gstreamer looks for to register plugins
331  *
332  * exchange the strings 'plugin' and 'Template plugin' with you plugin name and
333  * description
334  */
335 #if 0
336 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
337     GST_VERSION_MINOR,
338     "pitch",
339     "Run an FFT on the audio signal, output fundamental frequency",
340     plugin_init, VERSION, "LGPL", "GStreamer", "http://gstreamer.net/")
341 #endif