3 * Copyright (C) <2005> Stefan Kost <ensonic at users dot sf dot net>
4 * Copyright (C) 2007-2010 Sebastian Dröge <sebastian.droege@collabora.co.uk>
6 * gstinterpolation.c: Interpolation methods for dynamic properties
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.
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.
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., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
28 #include "gstinterpolationcontrolsource.h"
29 #include "gstinterpolationcontrolsourceprivate.h"
31 #define GST_CAT_DEFAULT controller_debug
32 GST_DEBUG_CATEGORY_EXTERN (GST_CAT_DEFAULT);
39 gst_control_point_find (gconstpointer p1, gconstpointer p2)
41 GstClockTime ct1 = ((GstControlPoint *) p1)->timestamp;
42 GstClockTime ct2 = *(GstClockTime *) p2;
44 return ((ct1 < ct2) ? -1 : ((ct1 == ct2) ? 0 : 1));
48 * gst_interpolation_control_source_find_control_point_iter:
49 * @self: the interpolation control source to search in
50 * @timestamp: the search key
52 * Find last value before given timestamp in control point list.
53 * If all values in the control point list come after the given
54 * timestamp or no values exist, %NULL is returned.
56 * Returns: the found #GSequenceIter or %NULL
58 static GSequenceIter *gst_interpolation_control_source_find_control_point_iter
59 (GstInterpolationControlSource * self, GstClockTime timestamp)
63 if (!self->priv->values)
67 g_sequence_search (self->priv->values, ×tamp,
68 (GCompareDataFunc) gst_control_point_find, NULL);
70 /* g_sequence_search() returns the iter where timestamp
71 * would be inserted, i.e. the iter > timestamp, so
72 * we need to get the previous one. And of course, if
73 * there is no previous one, we return NULL. */
74 if (g_sequence_iter_is_begin (iter))
77 return g_sequence_iter_prev (iter);
80 /* steps-like (no-)interpolation, default */
81 /* just returns the value for the most recent key-frame */
82 static inline const GValue *
83 _interpolate_none_get (GstInterpolationControlSource * self,
89 GstControlPoint *cp = g_sequence_get (iter);
93 ret = &self->priv->default_value;
98 #define DEFINE_NONE_GET_FUNC_COMPARABLE(type) \
99 static inline const GValue * \
100 _interpolate_none_get_##type (GstInterpolationControlSource *self, GSequenceIter *iter) \
105 GstControlPoint *cp = g_sequence_get (iter); \
106 g##type ret_val = g_value_get_##type (&cp->value); \
108 if (g_value_get_##type (&self->priv->minimum_value) > ret_val) \
109 ret = &self->priv->minimum_value; \
110 else if (g_value_get_##type (&self->priv->maximum_value) < ret_val) \
111 ret = &self->priv->maximum_value; \
115 ret = &self->priv->default_value; \
120 #define DEFINE_NONE_GET(type,ctype,get_func) \
122 interpolate_none_get_##type (GstInterpolationControlSource *self, GstClockTime timestamp, GValue *value) \
125 GSequenceIter *iter; \
127 g_mutex_lock (self->lock); \
129 iter = gst_interpolation_control_source_find_control_point_iter (self, timestamp); \
130 ret = get_func (self, iter); \
131 g_value_copy (ret, value); \
132 g_mutex_unlock (self->lock); \
137 interpolate_none_get_##type##_value_array (GstInterpolationControlSource *self, \
138 GstClockTime timestamp, GstValueArray * value_array) \
141 GstClockTime ts = timestamp; \
142 GstClockTime next_ts = 0; \
143 ctype *values = (ctype *) value_array->values; \
144 const GValue *ret_val = NULL; \
146 GSequenceIter *iter1 = NULL, *iter2 = NULL; \
148 g_mutex_lock (self->lock); \
149 for(i = 0; i < value_array->nbsamples; i++) { \
150 if (!ret_val || ts >= next_ts) { \
151 iter1 = gst_interpolation_control_source_find_control_point_iter (self, ts); \
153 if (G_LIKELY (self->priv->values)) \
154 iter2 = g_sequence_get_begin_iter (self->priv->values); \
158 iter2 = g_sequence_iter_next (iter1); \
161 if (iter2 && !g_sequence_iter_is_end (iter2)) { \
162 GstControlPoint *cp; \
164 cp = g_sequence_get (iter2); \
165 next_ts = cp->timestamp; \
167 next_ts = GST_CLOCK_TIME_NONE; \
170 ret_val = get_func (self, iter1); \
171 ret = g_value_get_##type (ret_val); \
174 ts += value_array->sample_interval; \
177 g_mutex_unlock (self->lock); \
181 DEFINE_NONE_GET_FUNC_COMPARABLE (int);
182 DEFINE_NONE_GET (int, gint, _interpolate_none_get_int);
183 DEFINE_NONE_GET_FUNC_COMPARABLE (uint);
184 DEFINE_NONE_GET (uint, guint, _interpolate_none_get_uint);
185 DEFINE_NONE_GET_FUNC_COMPARABLE (long);
186 DEFINE_NONE_GET (long, glong, _interpolate_none_get_long);
187 DEFINE_NONE_GET_FUNC_COMPARABLE (ulong);
188 DEFINE_NONE_GET (ulong, gulong, _interpolate_none_get_ulong);
189 DEFINE_NONE_GET_FUNC_COMPARABLE (int64);
190 DEFINE_NONE_GET (int64, gint64, _interpolate_none_get_int64);
191 DEFINE_NONE_GET_FUNC_COMPARABLE (uint64);
192 DEFINE_NONE_GET (uint64, guint64, _interpolate_none_get_uint64);
193 DEFINE_NONE_GET_FUNC_COMPARABLE (float);
194 DEFINE_NONE_GET (float, gfloat, _interpolate_none_get_float);
195 DEFINE_NONE_GET_FUNC_COMPARABLE (double);
196 DEFINE_NONE_GET (double, gdouble, _interpolate_none_get_double);
198 DEFINE_NONE_GET (boolean, gboolean, _interpolate_none_get);
199 DEFINE_NONE_GET (enum, gint, _interpolate_none_get);
200 DEFINE_NONE_GET (string, const gchar *, _interpolate_none_get);
202 static GstInterpolateMethod interpolate_none = {
203 (GstControlSourceGetValue) interpolate_none_get_int,
204 (GstControlSourceGetValueArray) interpolate_none_get_int_value_array,
205 (GstControlSourceGetValue) interpolate_none_get_uint,
206 (GstControlSourceGetValueArray) interpolate_none_get_uint_value_array,
207 (GstControlSourceGetValue) interpolate_none_get_long,
208 (GstControlSourceGetValueArray) interpolate_none_get_long_value_array,
209 (GstControlSourceGetValue) interpolate_none_get_ulong,
210 (GstControlSourceGetValueArray) interpolate_none_get_ulong_value_array,
211 (GstControlSourceGetValue) interpolate_none_get_int64,
212 (GstControlSourceGetValueArray) interpolate_none_get_int64_value_array,
213 (GstControlSourceGetValue) interpolate_none_get_uint64,
214 (GstControlSourceGetValueArray) interpolate_none_get_uint64_value_array,
215 (GstControlSourceGetValue) interpolate_none_get_float,
216 (GstControlSourceGetValueArray) interpolate_none_get_float_value_array,
217 (GstControlSourceGetValue) interpolate_none_get_double,
218 (GstControlSourceGetValueArray) interpolate_none_get_double_value_array,
219 (GstControlSourceGetValue) interpolate_none_get_boolean,
220 (GstControlSourceGetValueArray) interpolate_none_get_boolean_value_array,
221 (GstControlSourceGetValue) interpolate_none_get_enum,
222 (GstControlSourceGetValueArray) interpolate_none_get_enum_value_array,
223 (GstControlSourceGetValue) interpolate_none_get_string,
224 (GstControlSourceGetValueArray) interpolate_none_get_string_value_array
227 /* returns the default value of the property, except for times with specific values */
228 /* needed for one-shot events, such as notes and triggers */
229 static inline const GValue *
230 _interpolate_trigger_get (GstInterpolationControlSource * self,
231 GSequenceIter * iter, GstClockTime timestamp)
235 /* check if there is a value at the registered timestamp */
237 cp = g_sequence_get (iter);
238 if (timestamp == cp->timestamp) {
242 if (self->priv->nvalues > 0)
243 return &self->priv->default_value;
248 #define DEFINE_TRIGGER_GET_FUNC_COMPARABLE(type) \
249 static inline const GValue * \
250 _interpolate_trigger_get_##type (GstInterpolationControlSource *self, GSequenceIter *iter, GstClockTime timestamp) \
252 GstControlPoint *cp; \
254 /* check if there is a value at the registered timestamp */ \
256 cp = g_sequence_get (iter); \
257 if (timestamp == cp->timestamp) { \
258 g##type ret = g_value_get_##type (&cp->value); \
259 if (g_value_get_##type (&self->priv->minimum_value) > ret) \
260 return &self->priv->minimum_value; \
261 else if (g_value_get_##type (&self->priv->maximum_value) < ret) \
262 return &self->priv->maximum_value; \
268 if (self->priv->nvalues > 0) \
269 return &self->priv->default_value; \
274 #define DEFINE_TRIGGER_GET(type, ctype, get_func) \
276 interpolate_trigger_get_##type (GstInterpolationControlSource *self, GstClockTime timestamp, GValue *value) \
279 GSequenceIter *iter; \
281 g_mutex_lock (self->lock); \
283 iter = gst_interpolation_control_source_find_control_point_iter (self, timestamp); \
284 ret = get_func (self, iter, timestamp); \
286 g_mutex_unlock (self->lock); \
290 g_value_copy (ret, value); \
291 g_mutex_unlock (self->lock); \
296 interpolate_trigger_get_##type##_value_array (GstInterpolationControlSource *self, \
297 GstClockTime timestamp, GstValueArray * value_array) \
300 GstClockTime ts = timestamp; \
301 GstClockTime next_ts = 0; \
302 ctype *values = (ctype *) value_array->values; \
303 const GValue *ret_val = NULL; \
305 GSequenceIter *iter1 = NULL, *iter2 = NULL; \
306 gboolean triggered = FALSE; \
308 g_mutex_lock (self->lock); \
309 for(i = 0; i < value_array->nbsamples; i++) { \
310 if (!ret_val || ts >= next_ts) { \
311 iter1 = gst_interpolation_control_source_find_control_point_iter (self, ts); \
313 if (G_LIKELY (self->priv->values)) \
314 iter2 = g_sequence_get_begin_iter (self->priv->values); \
318 iter2 = g_sequence_iter_next (iter1); \
321 if (iter2 && !g_sequence_iter_is_end (iter2)) { \
322 GstControlPoint *cp; \
324 cp = g_sequence_get (iter2); \
325 next_ts = cp->timestamp; \
327 next_ts = GST_CLOCK_TIME_NONE; \
330 ret_val = get_func (self, iter1, ts); \
332 g_mutex_unlock (self->lock); \
335 ret = g_value_get_##type (ret_val); \
337 } else if (triggered) { \
338 ret_val = get_func (self, iter1, ts); \
340 g_mutex_unlock (self->lock); \
343 ret = g_value_get_##type (ret_val); \
347 ts += value_array->sample_interval; \
350 g_mutex_unlock (self->lock); \
354 DEFINE_TRIGGER_GET_FUNC_COMPARABLE (int);
355 DEFINE_TRIGGER_GET (int, gint, _interpolate_trigger_get_int);
356 DEFINE_TRIGGER_GET_FUNC_COMPARABLE (uint);
357 DEFINE_TRIGGER_GET (uint, guint, _interpolate_trigger_get_uint);
358 DEFINE_TRIGGER_GET_FUNC_COMPARABLE (long);
359 DEFINE_TRIGGER_GET (long, glong, _interpolate_trigger_get_long);
360 DEFINE_TRIGGER_GET_FUNC_COMPARABLE (ulong);
361 DEFINE_TRIGGER_GET (ulong, gulong, _interpolate_trigger_get_ulong);
362 DEFINE_TRIGGER_GET_FUNC_COMPARABLE (int64);
363 DEFINE_TRIGGER_GET (int64, gint64, _interpolate_trigger_get_int64);
364 DEFINE_TRIGGER_GET_FUNC_COMPARABLE (uint64);
365 DEFINE_TRIGGER_GET (uint64, guint64, _interpolate_trigger_get_uint64);
366 DEFINE_TRIGGER_GET_FUNC_COMPARABLE (float);
367 DEFINE_TRIGGER_GET (float, gfloat, _interpolate_trigger_get_float);
368 DEFINE_TRIGGER_GET_FUNC_COMPARABLE (double);
369 DEFINE_TRIGGER_GET (double, gdouble, _interpolate_trigger_get_double);
371 DEFINE_TRIGGER_GET (boolean, gboolean, _interpolate_trigger_get);
372 DEFINE_TRIGGER_GET (enum, gint, _interpolate_trigger_get);
373 DEFINE_TRIGGER_GET (string, const gchar *, _interpolate_trigger_get);
375 static GstInterpolateMethod interpolate_trigger = {
376 (GstControlSourceGetValue) interpolate_trigger_get_int,
377 (GstControlSourceGetValueArray) interpolate_trigger_get_int_value_array,
378 (GstControlSourceGetValue) interpolate_trigger_get_uint,
379 (GstControlSourceGetValueArray) interpolate_trigger_get_uint_value_array,
380 (GstControlSourceGetValue) interpolate_trigger_get_long,
381 (GstControlSourceGetValueArray) interpolate_trigger_get_long_value_array,
382 (GstControlSourceGetValue) interpolate_trigger_get_ulong,
383 (GstControlSourceGetValueArray) interpolate_trigger_get_ulong_value_array,
384 (GstControlSourceGetValue) interpolate_trigger_get_int64,
385 (GstControlSourceGetValueArray) interpolate_trigger_get_int64_value_array,
386 (GstControlSourceGetValue) interpolate_trigger_get_uint64,
387 (GstControlSourceGetValueArray) interpolate_trigger_get_uint64_value_array,
388 (GstControlSourceGetValue) interpolate_trigger_get_float,
389 (GstControlSourceGetValueArray) interpolate_trigger_get_float_value_array,
390 (GstControlSourceGetValue) interpolate_trigger_get_double,
391 (GstControlSourceGetValueArray) interpolate_trigger_get_double_value_array,
392 (GstControlSourceGetValue) interpolate_trigger_get_boolean,
393 (GstControlSourceGetValueArray) interpolate_trigger_get_boolean_value_array,
394 (GstControlSourceGetValue) interpolate_trigger_get_enum,
395 (GstControlSourceGetValueArray) interpolate_trigger_get_enum_value_array,
396 (GstControlSourceGetValue) interpolate_trigger_get_string,
397 (GstControlSourceGetValueArray) interpolate_trigger_get_string_value_array
400 /* linear interpolation */
401 /* smoothes inbetween values */
402 #define DEFINE_LINEAR_GET(vtype, round, convert) \
404 _interpolate_linear_internal_##vtype (GstClockTime timestamp1, g##vtype value1, GstClockTime timestamp2, g##vtype value2, GstClockTime timestamp, g##vtype min, g##vtype max, g##vtype *ret) \
406 if (GST_CLOCK_TIME_IS_VALID (timestamp2)) { \
409 slope = ((gdouble) convert (value2) - (gdouble) convert (value1)) / gst_guint64_to_gdouble (timestamp2 - timestamp1); \
412 *ret = (g##vtype) (convert (value1) + gst_guint64_to_gdouble (timestamp - timestamp1) * slope + 0.5); \
414 *ret = (g##vtype) (convert (value1) + gst_guint64_to_gdouble (timestamp - timestamp1) * slope); \
418 *ret = CLAMP (*ret, min, max); \
422 interpolate_linear_get_##vtype (GstInterpolationControlSource *self, GstClockTime timestamp, GValue *value) \
424 g##vtype ret, min, max; \
425 GSequenceIter *iter; \
426 GstControlPoint *cp1, *cp2 = NULL, cp = {0, }; \
428 g_mutex_lock (self->lock); \
430 min = g_value_get_##vtype (&self->priv->minimum_value); \
431 max = g_value_get_##vtype (&self->priv->maximum_value); \
433 iter = gst_interpolation_control_source_find_control_point_iter (self, timestamp); \
435 cp1 = g_sequence_get (iter); \
436 iter = g_sequence_iter_next (iter); \
438 cp.timestamp = G_GUINT64_CONSTANT(0); \
439 g_value_init (&cp.value, self->priv->type); \
440 g_value_copy (&self->priv->default_value, &cp.value); \
442 if (G_LIKELY (self->priv->values)) \
443 iter = g_sequence_get_begin_iter (self->priv->values); \
445 if (iter && !g_sequence_iter_is_end (iter)) \
446 cp2 = g_sequence_get (iter); \
448 _interpolate_linear_internal_##vtype (cp1->timestamp, g_value_get_##vtype (&cp1->value), (cp2 ? cp2->timestamp : GST_CLOCK_TIME_NONE), (cp2 ? g_value_get_##vtype (&cp2->value) : 0), timestamp, min, max, &ret); \
449 g_value_set_##vtype (value, ret); \
450 g_mutex_unlock (self->lock); \
452 g_value_unset (&cp.value); \
457 interpolate_linear_get_##vtype##_value_array (GstInterpolationControlSource *self, \
458 GstClockTime timestamp, GstValueArray * value_array) \
461 GstClockTime ts = timestamp; \
462 GstClockTime next_ts = 0; \
463 g##vtype *values = (g##vtype *) value_array->values; \
464 GSequenceIter *iter1, *iter2 = NULL; \
465 GstControlPoint *cp1 = NULL, *cp2 = NULL, cp = {0, }; \
466 g##vtype val1 = 0, val2 = 0, min, max; \
468 g_mutex_lock (self->lock); \
470 cp.timestamp = G_GUINT64_CONSTANT(0); \
471 g_value_init (&cp.value, self->priv->type); \
472 g_value_copy (&self->priv->default_value, &cp.value); \
474 min = g_value_get_##vtype (&self->priv->minimum_value); \
475 max = g_value_get_##vtype (&self->priv->maximum_value); \
477 for(i = 0; i < value_array->nbsamples; i++) { \
478 if (timestamp >= next_ts) { \
479 iter1 = gst_interpolation_control_source_find_control_point_iter (self, ts); \
482 if (G_LIKELY (self->priv->values)) \
483 iter2 = g_sequence_get_begin_iter (self->priv->values); \
487 cp1 = g_sequence_get (iter1); \
488 iter2 = g_sequence_iter_next (iter1); \
491 if (iter2 && !g_sequence_iter_is_end (iter2)) { \
492 cp2 = g_sequence_get (iter2); \
493 next_ts = cp2->timestamp; \
495 next_ts = GST_CLOCK_TIME_NONE; \
497 val1 = g_value_get_##vtype (&cp1->value); \
499 val2 = g_value_get_##vtype (&cp2->value); \
501 _interpolate_linear_internal_##vtype (cp1->timestamp, val1, (cp2 ? cp2->timestamp : GST_CLOCK_TIME_NONE), (cp2 ? val2 : 0), ts, min, max, values); \
502 ts += value_array->sample_interval; \
505 g_mutex_unlock (self->lock); \
506 g_value_unset (&cp.value); \
510 DEFINE_LINEAR_GET (int, TRUE, EMPTY);
511 DEFINE_LINEAR_GET (uint, TRUE, EMPTY);
512 DEFINE_LINEAR_GET (long, TRUE, EMPTY);
513 DEFINE_LINEAR_GET (ulong, TRUE, EMPTY);
514 DEFINE_LINEAR_GET (int64, TRUE, EMPTY);
515 DEFINE_LINEAR_GET (uint64, TRUE, gst_guint64_to_gdouble);
516 DEFINE_LINEAR_GET (float, FALSE, EMPTY);
517 DEFINE_LINEAR_GET (double, FALSE, EMPTY);
519 static GstInterpolateMethod interpolate_linear = {
520 (GstControlSourceGetValue) interpolate_linear_get_int,
521 (GstControlSourceGetValueArray) interpolate_linear_get_int_value_array,
522 (GstControlSourceGetValue) interpolate_linear_get_uint,
523 (GstControlSourceGetValueArray) interpolate_linear_get_uint_value_array,
524 (GstControlSourceGetValue) interpolate_linear_get_long,
525 (GstControlSourceGetValueArray) interpolate_linear_get_long_value_array,
526 (GstControlSourceGetValue) interpolate_linear_get_ulong,
527 (GstControlSourceGetValueArray) interpolate_linear_get_ulong_value_array,
528 (GstControlSourceGetValue) interpolate_linear_get_int64,
529 (GstControlSourceGetValueArray) interpolate_linear_get_int64_value_array,
530 (GstControlSourceGetValue) interpolate_linear_get_uint64,
531 (GstControlSourceGetValueArray) interpolate_linear_get_uint64_value_array,
532 (GstControlSourceGetValue) interpolate_linear_get_float,
533 (GstControlSourceGetValueArray) interpolate_linear_get_float_value_array,
534 (GstControlSourceGetValue) interpolate_linear_get_double,
535 (GstControlSourceGetValueArray) interpolate_linear_get_double_value_array,
536 (GstControlSourceGetValue) NULL,
537 (GstControlSourceGetValueArray) NULL,
538 (GstControlSourceGetValue) NULL,
539 (GstControlSourceGetValueArray) NULL,
540 (GstControlSourceGetValue) NULL,
541 (GstControlSourceGetValueArray) NULL
544 /* square interpolation */
546 /* cubic interpolation */
548 /* The following functions implement a natural cubic spline interpolator.
549 * For details look at http://en.wikipedia.org/wiki/Spline_interpolation
551 * Instead of using a real matrix with n^2 elements for the linear system
552 * of equations we use three arrays o, p, q to hold the tridiagonal matrix
553 * as following to save memory:
561 #define DEFINE_CUBIC_GET(vtype,round, convert) \
563 _interpolate_cubic_update_cache_##vtype (GstInterpolationControlSource *self) \
565 gint i, n = self->priv->nvalues; \
566 gdouble *o = g_new0 (gdouble, n); \
567 gdouble *p = g_new0 (gdouble, n); \
568 gdouble *q = g_new0 (gdouble, n); \
570 gdouble *h = g_new0 (gdouble, n); \
571 gdouble *b = g_new0 (gdouble, n); \
572 gdouble *z = g_new0 (gdouble, n); \
574 GSequenceIter *iter; \
575 GstControlPoint *cp; \
576 GstClockTime x_prev, x, x_next; \
577 g##vtype y_prev, y, y_next; \
579 /* Fill linear system of equations */ \
580 iter = g_sequence_get_begin_iter (self->priv->values); \
581 cp = g_sequence_get (iter); \
583 y = g_value_get_##vtype (&cp->value); \
587 iter = g_sequence_iter_next (iter); \
588 cp = g_sequence_get (iter); \
589 x_next = cp->timestamp; \
590 y_next = g_value_get_##vtype (&cp->value); \
591 h[0] = gst_guint64_to_gdouble (x_next - x); \
593 for (i = 1; i < n-1; i++) { \
594 /* Shuffle x and y values */ \
599 iter = g_sequence_iter_next (iter); \
600 cp = g_sequence_get (iter); \
601 x_next = cp->timestamp; \
602 y_next = g_value_get_##vtype (&cp->value); \
604 h[i] = gst_guint64_to_gdouble (x_next - x); \
606 p[i] = 2.0 * (h[i-1] + h[i]); \
608 b[i] = convert (y_next - y) / h[i] - convert (y - y_prev) / h[i-1]; \
612 /* Use Gauss elimination to set everything below the \
613 * diagonal to zero */ \
614 for (i = 1; i < n-1; i++) { \
615 gdouble a = o[i] / p[i-1]; \
616 p[i] -= a * q[i-1]; \
617 b[i] -= a * b[i-1]; \
620 /* Solve everything else from bottom to top */ \
621 for (i = n-2; i > 0; i--) \
622 z[i] = (b[i] - q[i] * z[i+1]) / p[i]; \
624 /* Save cache next in the GstControlPoint */ \
626 iter = g_sequence_get_begin_iter (self->priv->values); \
627 for (i = 0; i < n; i++) { \
628 cp = g_sequence_get (iter); \
629 cp->cache.cubic.h = h[i]; \
630 cp->cache.cubic.z = z[i]; \
631 iter = g_sequence_iter_next (iter); \
634 /* Free our temporary arrays */ \
644 _interpolate_cubic_get_##vtype (GstInterpolationControlSource *self, GstControlPoint *cp1, g##vtype value1, GstControlPoint *cp2, g##vtype value2, GstClockTime timestamp, g##vtype min, g##vtype max, g##vtype *ret) \
646 if (!self->priv->valid_cache) { \
647 _interpolate_cubic_update_cache_##vtype (self); \
648 self->priv->valid_cache = TRUE; \
652 gdouble diff1, diff2; \
655 diff1 = gst_guint64_to_gdouble (timestamp - cp1->timestamp); \
656 diff2 = gst_guint64_to_gdouble (cp2->timestamp - timestamp); \
658 out = (cp2->cache.cubic.z * diff1 * diff1 * diff1 + cp1->cache.cubic.z * diff2 * diff2 * diff2) / cp1->cache.cubic.h; \
659 out += (convert (value2) / cp1->cache.cubic.h - cp1->cache.cubic.h * cp2->cache.cubic.z) * diff1; \
660 out += (convert (value1) / cp1->cache.cubic.h - cp1->cache.cubic.h * cp1->cache.cubic.z) * diff2; \
663 *ret = (g##vtype) (out + 0.5); \
665 *ret = (g##vtype) out; \
670 *ret = CLAMP (*ret, min, max); \
674 interpolate_cubic_get_##vtype (GstInterpolationControlSource *self, GstClockTime timestamp, GValue *value) \
676 g##vtype ret, min, max; \
677 GSequenceIter *iter; \
678 GstControlPoint *cp1, *cp2 = NULL, cp = {0, }; \
680 if (self->priv->nvalues <= 2) \
681 return interpolate_linear_get_##vtype (self, timestamp, value); \
683 g_mutex_lock (self->lock); \
685 min = g_value_get_##vtype (&self->priv->minimum_value); \
686 max = g_value_get_##vtype (&self->priv->maximum_value); \
688 iter = gst_interpolation_control_source_find_control_point_iter (self, timestamp); \
690 cp1 = g_sequence_get (iter); \
691 iter = g_sequence_iter_next (iter); \
693 cp.timestamp = G_GUINT64_CONSTANT(0); \
694 g_value_init (&cp.value, self->priv->type); \
695 g_value_copy (&self->priv->default_value, &cp.value); \
697 if (G_LIKELY (self->priv->values)) \
698 iter = g_sequence_get_begin_iter (self->priv->values); \
700 if (iter && !g_sequence_iter_is_end (iter)) \
701 cp2 = g_sequence_get (iter); \
703 _interpolate_cubic_get_##vtype (self, cp1, g_value_get_##vtype (&cp1->value), cp2, (cp2 ? g_value_get_##vtype (&cp2->value) : 0), timestamp, min, max, &ret); \
704 g_value_set_##vtype (value, ret); \
705 g_mutex_unlock (self->lock); \
707 g_value_unset (&cp.value); \
712 interpolate_cubic_get_##vtype##_value_array (GstInterpolationControlSource *self, \
713 GstClockTime timestamp, GstValueArray * value_array) \
716 GstClockTime ts = timestamp; \
717 GstClockTime next_ts = 0; \
718 g##vtype *values = (g##vtype *) value_array->values; \
719 GSequenceIter *iter1, *iter2 = NULL; \
720 GstControlPoint *cp1 = NULL, *cp2 = NULL, cp = {0, }; \
721 g##vtype val1 = 0, val2 = 0, min, max; \
723 if (self->priv->nvalues <= 2) \
724 return interpolate_linear_get_##vtype##_value_array (self, timestamp, value_array); \
726 g_mutex_lock (self->lock); \
728 cp.timestamp = G_GUINT64_CONSTANT(0); \
729 g_value_init (&cp.value, self->priv->type); \
730 g_value_copy (&self->priv->default_value, &cp.value); \
732 min = g_value_get_##vtype (&self->priv->minimum_value); \
733 max = g_value_get_##vtype (&self->priv->maximum_value); \
735 for(i = 0; i < value_array->nbsamples; i++) { \
736 if (timestamp >= next_ts) { \
737 iter1 = gst_interpolation_control_source_find_control_point_iter (self, ts); \
740 if (G_LIKELY (self->priv->values)) \
741 iter2 = g_sequence_get_begin_iter (self->priv->values); \
745 cp1 = g_sequence_get (iter1); \
746 iter2 = g_sequence_iter_next (iter1); \
749 if (iter2 && !g_sequence_iter_is_end (iter2)) { \
750 cp2 = g_sequence_get (iter2); \
751 next_ts = cp2->timestamp; \
753 next_ts = GST_CLOCK_TIME_NONE; \
755 val1 = g_value_get_##vtype (&cp1->value); \
757 val2 = g_value_get_##vtype (&cp2->value); \
759 _interpolate_cubic_get_##vtype (self, cp1, val1, cp2, val2, timestamp, min, max, values); \
760 ts += value_array->sample_interval; \
763 g_mutex_unlock (self->lock); \
764 g_value_unset (&cp.value); \
768 DEFINE_CUBIC_GET (int, TRUE, EMPTY);
769 DEFINE_CUBIC_GET (uint, TRUE, EMPTY);
770 DEFINE_CUBIC_GET (long, TRUE, EMPTY);
771 DEFINE_CUBIC_GET (ulong, TRUE, EMPTY);
772 DEFINE_CUBIC_GET (int64, TRUE, EMPTY);
773 DEFINE_CUBIC_GET (uint64, TRUE, gst_guint64_to_gdouble);
774 DEFINE_CUBIC_GET (float, FALSE, EMPTY);
775 DEFINE_CUBIC_GET (double, FALSE, EMPTY);
777 static GstInterpolateMethod interpolate_cubic = {
778 (GstControlSourceGetValue) interpolate_cubic_get_int,
779 (GstControlSourceGetValueArray) interpolate_cubic_get_int_value_array,
780 (GstControlSourceGetValue) interpolate_cubic_get_uint,
781 (GstControlSourceGetValueArray) interpolate_cubic_get_uint_value_array,
782 (GstControlSourceGetValue) interpolate_cubic_get_long,
783 (GstControlSourceGetValueArray) interpolate_cubic_get_long_value_array,
784 (GstControlSourceGetValue) interpolate_cubic_get_ulong,
785 (GstControlSourceGetValueArray) interpolate_cubic_get_ulong_value_array,
786 (GstControlSourceGetValue) interpolate_cubic_get_int64,
787 (GstControlSourceGetValueArray) interpolate_cubic_get_int64_value_array,
788 (GstControlSourceGetValue) interpolate_cubic_get_uint64,
789 (GstControlSourceGetValueArray) interpolate_cubic_get_uint64_value_array,
790 (GstControlSourceGetValue) interpolate_cubic_get_float,
791 (GstControlSourceGetValueArray) interpolate_cubic_get_float_value_array,
792 (GstControlSourceGetValue) interpolate_cubic_get_double,
793 (GstControlSourceGetValueArray) interpolate_cubic_get_double_value_array,
794 (GstControlSourceGetValue) NULL,
795 (GstControlSourceGetValueArray) NULL,
796 (GstControlSourceGetValue) NULL,
797 (GstControlSourceGetValueArray) NULL,
798 (GstControlSourceGetValue) NULL,
799 (GstControlSourceGetValueArray) NULL
802 /* register all interpolation methods */
803 GstInterpolateMethod *priv_gst_interpolation_methods[] = {
805 &interpolate_trigger,
811 guint priv_gst_num_interpolation_methods =
812 G_N_ELEMENTS (priv_gst_interpolation_methods);