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