1 /* GStreamer
2 *
3 * Copyright (C) 2007,2010 Sebastian Dröge <sebastian.droege@collabora.co.uk>
4 *
5 * gstlfocontrolsource.c: Control source that provides some periodic waveforms
6 * as control values.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the
20 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24 /**
25 * SECTION:gstlfocontrolsource
26 * @title: GstLFOControlSource
27 * @short_description: LFO control source
28 *
29 * #GstLFOControlSource is a #GstControlSource, that provides several periodic
30 * waveforms as control values.
31 *
32 * To use #GstLFOControlSource get a new instance by calling
33 * gst_lfo_control_source_new(), bind it to a #GParamSpec and set the relevant
34 * properties.
35 *
36 * All functions are MT-safe.
37 */
38 #ifdef HAVE_CONFIG_H
39 #include "config.h"
40 #endif
41
42 #include <float.h>
43
44 #include <glib-object.h>
45 #include <gst/gst.h>
46 #include <gst/gstcontrolsource.h>
47
48 #include "gstlfocontrolsource.h"
49
50 #include "gst/glib-compat-private.h"
51
52 #include <gst/math-compat.h>
53
54 #define GST_CAT_DEFAULT controller_debug
55 GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
56
57 struct _GstLFOControlSourcePrivate
58 {
59 GstLFOWaveform waveform;
60 gdouble frequency;
61 GstClockTime period;
62 GstClockTime timeshift;
63 gdouble amplitude;
64 gdouble offset;
65 };
66
67 /* FIXME: as % in C is not the modulo operator we need here for
68 * negative numbers implement our own. Are there better ways? */
69 static inline GstClockTime
_calculate_pos(GstClockTime timestamp,GstClockTime timeshift,GstClockTime period)70 _calculate_pos (GstClockTime timestamp, GstClockTime timeshift,
71 GstClockTime period)
72 {
73 while (timestamp < timeshift)
74 timestamp += period;
75
76 timestamp -= timeshift;
77
78 return timestamp % period;
79 }
80
81 static inline gdouble
_sine_get(GstLFOControlSource * self,gdouble amp,gdouble off,GstClockTime timeshift,GstClockTime period,gdouble frequency,GstClockTime timestamp)82 _sine_get (GstLFOControlSource * self, gdouble amp, gdouble off,
83 GstClockTime timeshift, GstClockTime period, gdouble frequency,
84 GstClockTime timestamp)
85 {
86 gdouble pos =
87 gst_guint64_to_gdouble (_calculate_pos (timestamp, timeshift, period));
88 gdouble ret;
89
90 ret = sin (2.0 * M_PI * (frequency / GST_SECOND) * pos);
91 ret *= amp;
92 ret += off;
93
94 return ret;
95 }
96
97 static gboolean
waveform_sine_get(GstLFOControlSource * self,GstClockTime timestamp,gdouble * value)98 waveform_sine_get (GstLFOControlSource * self, GstClockTime timestamp,
99 gdouble * value)
100 {
101 GstLFOControlSourcePrivate *priv = self->priv;
102
103 gst_object_sync_values (GST_OBJECT (self), timestamp);
104 g_mutex_lock (&self->lock);
105 *value = _sine_get (self, priv->amplitude, priv->offset, priv->timeshift,
106 priv->period, priv->frequency, timestamp);
107 g_mutex_unlock (&self->lock);
108 return TRUE;
109 }
110
111 static gboolean
waveform_sine_get_value_array(GstLFOControlSource * self,GstClockTime timestamp,GstClockTime interval,guint n_values,gdouble * values)112 waveform_sine_get_value_array (GstLFOControlSource * self,
113 GstClockTime timestamp, GstClockTime interval, guint n_values,
114 gdouble * values)
115 {
116 GstLFOControlSourcePrivate *priv = self->priv;
117 guint i;
118 GstClockTime ts = timestamp;
119
120 for (i = 0; i < n_values; i++) {
121 gst_object_sync_values (GST_OBJECT (self), ts);
122 g_mutex_lock (&self->lock);
123 *values = _sine_get (self, priv->amplitude, priv->offset, priv->timeshift,
124 priv->period, priv->frequency, ts);
125 g_mutex_unlock (&self->lock);
126 ts += interval;
127 values++;
128 }
129 return TRUE;
130 }
131
132
133 static inline gdouble
_square_get(GstLFOControlSource * self,gdouble amp,gdouble off,GstClockTime timeshift,GstClockTime period,gdouble frequency,GstClockTime timestamp)134 _square_get (GstLFOControlSource * self, gdouble amp, gdouble off,
135 GstClockTime timeshift, GstClockTime period, gdouble frequency,
136 GstClockTime timestamp)
137 {
138 GstClockTime pos = _calculate_pos (timestamp, timeshift, period);
139 gdouble ret;
140
141 if (pos >= period / 2)
142 ret = amp;
143 else
144 ret = -amp;
145 ret += off;
146
147 return ret;
148 }
149
150 static gboolean
waveform_square_get(GstLFOControlSource * self,GstClockTime timestamp,gdouble * value)151 waveform_square_get (GstLFOControlSource * self, GstClockTime timestamp,
152 gdouble * value)
153 {
154 GstLFOControlSourcePrivate *priv = self->priv;
155
156 gst_object_sync_values (GST_OBJECT (self), timestamp);
157 g_mutex_lock (&self->lock);
158 *value = _square_get (self, priv->amplitude, priv->offset, priv->timeshift,
159 priv->period, priv->frequency, timestamp);
160 g_mutex_unlock (&self->lock);
161 return TRUE;
162 }
163
164 static gboolean
waveform_square_get_value_array(GstLFOControlSource * self,GstClockTime timestamp,GstClockTime interval,guint n_values,gdouble * values)165 waveform_square_get_value_array (GstLFOControlSource * self,
166 GstClockTime timestamp, GstClockTime interval, guint n_values,
167 gdouble * values)
168 {
169 GstLFOControlSourcePrivate *priv = self->priv;
170 guint i;
171 GstClockTime ts = timestamp;
172
173 for (i = 0; i < n_values; i++) {
174 gst_object_sync_values (GST_OBJECT (self), ts);
175 g_mutex_lock (&self->lock);
176 *values = _square_get (self, priv->amplitude, priv->offset, priv->timeshift,
177 priv->period, priv->frequency, ts);
178 g_mutex_unlock (&self->lock);
179 ts += interval;
180 values++;
181 }
182 return TRUE;
183 }
184
185 static inline gdouble
_saw_get(GstLFOControlSource * self,gdouble amp,gdouble off,GstClockTime timeshift,GstClockTime period,gdouble frequency,GstClockTime timestamp)186 _saw_get (GstLFOControlSource * self, gdouble amp, gdouble off,
187 GstClockTime timeshift, GstClockTime period, gdouble frequency,
188 GstClockTime timestamp)
189 {
190 gdouble pos =
191 gst_guint64_to_gdouble (_calculate_pos (timestamp, timeshift, period));
192 gdouble per = gst_guint64_to_gdouble (period);
193 gdouble ret;
194
195 ret = -((pos - per / 2.0) * ((2.0 * amp) / per));
196 ret += off;
197
198 return ret;
199 }
200
201 static gboolean
waveform_saw_get(GstLFOControlSource * self,GstClockTime timestamp,gdouble * value)202 waveform_saw_get (GstLFOControlSource * self, GstClockTime timestamp,
203 gdouble * value)
204 {
205 GstLFOControlSourcePrivate *priv = self->priv;
206
207 gst_object_sync_values (GST_OBJECT (self), timestamp);
208 g_mutex_lock (&self->lock);
209 *value = _saw_get (self, priv->amplitude, priv->offset, priv->timeshift,
210 priv->period, priv->frequency, timestamp);
211 g_mutex_unlock (&self->lock);
212 return TRUE;
213 }
214
215 static gboolean
waveform_saw_get_value_array(GstLFOControlSource * self,GstClockTime timestamp,GstClockTime interval,guint n_values,gdouble * values)216 waveform_saw_get_value_array (GstLFOControlSource * self,
217 GstClockTime timestamp, GstClockTime interval, guint n_values,
218 gdouble * values)
219 {
220 GstLFOControlSourcePrivate *priv = self->priv;
221 guint i;
222 GstClockTime ts = timestamp;
223
224 for (i = 0; i < n_values; i++) {
225 gst_object_sync_values (GST_OBJECT (self), ts);
226 g_mutex_lock (&self->lock);
227 *values = _saw_get (self, priv->amplitude, priv->offset, priv->timeshift,
228 priv->period, priv->frequency, ts);
229 g_mutex_unlock (&self->lock);
230 ts += interval;
231 values++;
232 }
233 return TRUE;
234 }
235
236 static inline gdouble
_rsaw_get(GstLFOControlSource * self,gdouble amp,gdouble off,GstClockTime timeshift,GstClockTime period,gdouble frequency,GstClockTime timestamp)237 _rsaw_get (GstLFOControlSource * self, gdouble amp, gdouble off,
238 GstClockTime timeshift, GstClockTime period, gdouble frequency,
239 GstClockTime timestamp)
240 {
241 gdouble pos =
242 gst_guint64_to_gdouble (_calculate_pos (timestamp, timeshift, period));
243 gdouble per = gst_guint64_to_gdouble (period);
244 gdouble ret;
245
246 ret = (pos - per / 2.0) * ((2.0 * amp) / per);
247 ret += off;
248
249 return ret;
250 }
251
252 static gboolean
waveform_rsaw_get(GstLFOControlSource * self,GstClockTime timestamp,gdouble * value)253 waveform_rsaw_get (GstLFOControlSource * self, GstClockTime timestamp,
254 gdouble * value)
255 {
256 GstLFOControlSourcePrivate *priv = self->priv;
257
258 gst_object_sync_values (GST_OBJECT (self), timestamp);
259 g_mutex_lock (&self->lock);
260 *value = _rsaw_get (self, priv->amplitude, priv->offset, priv->timeshift,
261 priv->period, priv->frequency, timestamp);
262 g_mutex_unlock (&self->lock);
263 return TRUE;
264 }
265
266 static gboolean
waveform_rsaw_get_value_array(GstLFOControlSource * self,GstClockTime timestamp,GstClockTime interval,guint n_values,gdouble * values)267 waveform_rsaw_get_value_array (GstLFOControlSource * self,
268 GstClockTime timestamp, GstClockTime interval, guint n_values,
269 gdouble * values)
270 {
271 GstLFOControlSourcePrivate *priv = self->priv;
272 guint i;
273 GstClockTime ts = timestamp;
274
275 for (i = 0; i < n_values; i++) {
276 gst_object_sync_values (GST_OBJECT (self), ts);
277 g_mutex_lock (&self->lock);
278 *values = _rsaw_get (self, priv->amplitude, priv->offset, priv->timeshift,
279 priv->period, priv->frequency, ts);
280 g_mutex_unlock (&self->lock);
281 ts += interval;
282 values++;
283 }
284 return TRUE;
285 }
286
287
288 static inline gdouble
_triangle_get(GstLFOControlSource * self,gdouble amp,gdouble off,GstClockTime timeshift,GstClockTime period,gdouble frequency,GstClockTime timestamp)289 _triangle_get (GstLFOControlSource * self, gdouble amp, gdouble off,
290 GstClockTime timeshift, GstClockTime period, gdouble frequency,
291 GstClockTime timestamp)
292 {
293 gdouble pos =
294 gst_guint64_to_gdouble (_calculate_pos (timestamp, timeshift, period));
295 gdouble per = gst_guint64_to_gdouble (period);
296 gdouble ret;
297
298 if (pos <= 0.25 * per)
299 /* 1st quarter */
300 ret = pos * ((4.0 * amp) / per);
301 else if (pos <= 0.75 * per)
302 /* 2nd & 3rd quarter */
303 ret = -(pos - per / 2.0) * ((4.0 * amp) / per);
304 else
305 /* 4th quarter */
306 ret = -(per - pos) * ((4.0 * amp) / per);
307
308 ret += off;
309
310 return ret;
311 }
312
313 static gboolean
waveform_triangle_get(GstLFOControlSource * self,GstClockTime timestamp,gdouble * value)314 waveform_triangle_get (GstLFOControlSource * self, GstClockTime timestamp,
315 gdouble * value)
316 {
317 GstLFOControlSourcePrivate *priv = self->priv;
318
319 gst_object_sync_values (GST_OBJECT (self), timestamp);
320 g_mutex_lock (&self->lock);
321 *value = _triangle_get (self, priv->amplitude, priv->offset, priv->timeshift,
322 priv->period, priv->frequency, timestamp);
323 g_mutex_unlock (&self->lock);
324 return TRUE;
325 }
326
327 static gboolean
waveform_triangle_get_value_array(GstLFOControlSource * self,GstClockTime timestamp,GstClockTime interval,guint n_values,gdouble * values)328 waveform_triangle_get_value_array (GstLFOControlSource * self,
329 GstClockTime timestamp, GstClockTime interval, guint n_values,
330 gdouble * values)
331 {
332 GstLFOControlSourcePrivate *priv = self->priv;
333 guint i;
334 GstClockTime ts = timestamp;
335
336 for (i = 0; i < n_values; i++) {
337 gst_object_sync_values (GST_OBJECT (self), ts);
338 g_mutex_lock (&self->lock);
339 *values =
340 _triangle_get (self, priv->amplitude, priv->offset, priv->timeshift,
341 priv->period, priv->frequency, ts);
342 g_mutex_unlock (&self->lock);
343 ts += interval;
344 values++;
345 }
346 return TRUE;
347 }
348
349 static struct
350 {
351 GstControlSourceGetValue get;
352 GstControlSourceGetValueArray get_value_array;
353 } waveforms[] = {
354 {
355 (GstControlSourceGetValue) waveform_sine_get,
356 (GstControlSourceGetValueArray) waveform_sine_get_value_array}, {
357 (GstControlSourceGetValue) waveform_square_get,
358 (GstControlSourceGetValueArray) waveform_square_get_value_array}, {
359 (GstControlSourceGetValue) waveform_saw_get,
360 (GstControlSourceGetValueArray) waveform_saw_get_value_array}, {
361 (GstControlSourceGetValue) waveform_rsaw_get,
362 (GstControlSourceGetValueArray) waveform_rsaw_get_value_array}, {
363 (GstControlSourceGetValue) waveform_triangle_get,
364 (GstControlSourceGetValueArray) waveform_triangle_get_value_array}
365 };
366
367 static const guint num_waveforms = G_N_ELEMENTS (waveforms);
368
369 enum
370 {
371 PROP_WAVEFORM = 1,
372 PROP_FREQUENCY,
373 PROP_TIMESHIFT,
374 PROP_AMPLITUDE,
375 PROP_OFFSET
376 };
377
378 #define _do_init \
379 GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "lfo control source", 0, "low frequency oscillator control source")
380
381 #define gst_lfo_control_source_parent_class parent_class
382 G_DEFINE_TYPE_WITH_CODE (GstLFOControlSource, gst_lfo_control_source,
383 GST_TYPE_CONTROL_SOURCE, G_ADD_PRIVATE (GstLFOControlSource) _do_init);
384
385 static void
gst_lfo_control_source_reset(GstLFOControlSource * self)386 gst_lfo_control_source_reset (GstLFOControlSource * self)
387 {
388 GstControlSource *csource = GST_CONTROL_SOURCE (self);
389
390 csource->get_value = NULL;
391 csource->get_value_array = NULL;
392 }
393
394 /**
395 * gst_lfo_control_source_new:
396 *
397 * This returns a new, unbound #GstLFOControlSource.
398 *
399 * Returns: (transfer full): a new, unbound #GstLFOControlSource.
400 */
401 GstControlSource *
gst_lfo_control_source_new(void)402 gst_lfo_control_source_new (void)
403 {
404 GstControlSource *csource = g_object_new (GST_TYPE_LFO_CONTROL_SOURCE, NULL);
405
406 /* Clear floating flag */
407 gst_object_ref_sink (csource);
408
409 return csource;
410 }
411
412 static gboolean
gst_lfo_control_source_set_waveform(GstLFOControlSource * self,GstLFOWaveform waveform)413 gst_lfo_control_source_set_waveform (GstLFOControlSource * self,
414 GstLFOWaveform waveform)
415 {
416 GstControlSource *csource = GST_CONTROL_SOURCE (self);
417
418 if (waveform >= num_waveforms || (int) waveform < 0) {
419 GST_WARNING ("waveform %d invalid or not implemented yet", waveform);
420 return FALSE;
421 }
422
423 csource->get_value = waveforms[waveform].get;
424 csource->get_value_array = waveforms[waveform].get_value_array;
425
426 self->priv->waveform = waveform;
427
428 return TRUE;
429 }
430
431 static void
gst_lfo_control_source_init(GstLFOControlSource * self)432 gst_lfo_control_source_init (GstLFOControlSource * self)
433 {
434 self->priv = gst_lfo_control_source_get_instance_private (self);
435 self->priv->waveform = gst_lfo_control_source_set_waveform (self,
436 GST_LFO_WAVEFORM_SINE);
437 self->priv->frequency = 1.0;
438 self->priv->amplitude = 1.0;
439 self->priv->period = GST_SECOND / self->priv->frequency;
440 self->priv->timeshift = 0;
441
442 g_mutex_init (&self->lock);
443 }
444
445 static void
gst_lfo_control_source_finalize(GObject * obj)446 gst_lfo_control_source_finalize (GObject * obj)
447 {
448 GstLFOControlSource *self = GST_LFO_CONTROL_SOURCE (obj);
449
450 gst_lfo_control_source_reset (self);
451 g_mutex_clear (&self->lock);
452
453 G_OBJECT_CLASS (parent_class)->finalize (obj);
454 }
455
456 static void
gst_lfo_control_source_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)457 gst_lfo_control_source_set_property (GObject * object, guint prop_id,
458 const GValue * value, GParamSpec * pspec)
459 {
460 GstLFOControlSource *self = GST_LFO_CONTROL_SOURCE (object);
461
462 switch (prop_id) {
463 case PROP_WAVEFORM:
464 g_mutex_lock (&self->lock);
465 gst_lfo_control_source_set_waveform (self,
466 (GstLFOWaveform) g_value_get_enum (value));
467 g_mutex_unlock (&self->lock);
468 break;
469 case PROP_FREQUENCY:{
470 gdouble frequency = g_value_get_double (value);
471
472 g_return_if_fail (((GstClockTime) (GST_SECOND / frequency)) != 0);
473
474 g_mutex_lock (&self->lock);
475 self->priv->frequency = frequency;
476 self->priv->period = GST_SECOND / frequency;
477 g_mutex_unlock (&self->lock);
478 break;
479 }
480 case PROP_TIMESHIFT:
481 g_mutex_lock (&self->lock);
482 self->priv->timeshift = g_value_get_uint64 (value);
483 g_mutex_unlock (&self->lock);
484 break;
485 case PROP_AMPLITUDE:
486 g_mutex_lock (&self->lock);
487 self->priv->amplitude = g_value_get_double (value);
488 g_mutex_unlock (&self->lock);
489 break;
490 case PROP_OFFSET:
491 g_mutex_lock (&self->lock);
492 self->priv->offset = g_value_get_double (value);
493 g_mutex_unlock (&self->lock);
494 break;
495 default:
496 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
497 break;
498 }
499 }
500
501 static void
gst_lfo_control_source_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)502 gst_lfo_control_source_get_property (GObject * object, guint prop_id,
503 GValue * value, GParamSpec * pspec)
504 {
505 GstLFOControlSource *self = GST_LFO_CONTROL_SOURCE (object);
506
507 switch (prop_id) {
508 case PROP_WAVEFORM:
509 g_value_set_enum (value, self->priv->waveform);
510 break;
511 case PROP_FREQUENCY:
512 g_value_set_double (value, self->priv->frequency);
513 break;
514 case PROP_TIMESHIFT:
515 g_value_set_uint64 (value, self->priv->timeshift);
516 break;
517 case PROP_AMPLITUDE:
518 g_value_set_double (value, self->priv->amplitude);
519 break;
520 case PROP_OFFSET:
521 g_value_set_double (value, self->priv->offset);
522 break;
523 default:
524 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
525 break;
526 }
527 }
528
529 static void
gst_lfo_control_source_class_init(GstLFOControlSourceClass * klass)530 gst_lfo_control_source_class_init (GstLFOControlSourceClass * klass)
531 {
532 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
533
534 gobject_class->finalize = gst_lfo_control_source_finalize;
535 gobject_class->set_property = gst_lfo_control_source_set_property;
536 gobject_class->get_property = gst_lfo_control_source_get_property;
537
538 /**
539 * GstLFOControlSource:waveform:
540 *
541 * Specifies the waveform that should be used for this #GstLFOControlSource.
542 */
543 g_object_class_install_property (gobject_class, PROP_WAVEFORM,
544 g_param_spec_enum ("waveform", "Waveform", "Waveform",
545 GST_TYPE_LFO_WAVEFORM, GST_LFO_WAVEFORM_SINE,
546 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
547
548 /**
549 * GstLFOControlSource:frequency:
550 *
551 * Specifies the frequency that should be used for the waveform
552 * of this #GstLFOControlSource. It should be large enough
553 * so that the period is longer than one nanosecond.
554 */
555 g_object_class_install_property (gobject_class, PROP_FREQUENCY,
556 g_param_spec_double ("frequency", "Frequency",
557 "Frequency of the waveform", DBL_MIN, G_MAXDOUBLE, 1.0,
558 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
559
560 /**
561 * GstLFOControlSource:timeshift:
562 *
563 * Specifies the timeshift to the right that should be used for the waveform
564 * of this #GstLFOControlSource in nanoseconds.
565 *
566 * To get a n nanosecond shift to the left use
567 * "(GST_SECOND / frequency) - n".
568 *
569 */
570 g_object_class_install_property (gobject_class, PROP_TIMESHIFT,
571 g_param_spec_uint64 ("timeshift", "Timeshift",
572 "Timeshift of the waveform to the right", 0, G_MAXUINT64, 0,
573 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
574
575 /**
576 * GstLFOControlSource:amplitude:
577 *
578 * Specifies the amplitude for the waveform of this #GstLFOControlSource.
579 */
580 g_object_class_install_property (gobject_class, PROP_AMPLITUDE,
581 g_param_spec_double ("amplitude", "Amplitude",
582 "Amplitude of the waveform", 0.0, 1.0, 1.0,
583 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
584
585 /**
586 * GstLFOControlSource:offset:
587 *
588 * Specifies the value offset for the waveform of this #GstLFOControlSource.
589 */
590 g_object_class_install_property (gobject_class, PROP_OFFSET,
591 g_param_spec_double ("offset", "Offset", "Offset of the waveform",
592 0.0, 1.0, 1.0,
593 G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS));
594 }
595