a515c3a39a8e341efe04950e0edc5ca4df29bb1e
[ardour.git] / libs / ardour / curve.cc
1 /*
2     Copyright (C) 2001-2003 Paul Davis 
3
4     Contains ideas derived from "Constrained Cubic Spline Interpolation" 
5     by CJC Kruger (www.korf.co.uk/spline.pdf).
6
7     This program is free software; you can redistribute it and/or modify
8     it under the terms of the GNU General Public License as published by
9     the Free Software Foundation; either version 2 of the License, or
10     (at your option) any later version.
11
12     This program 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
15     GNU General Public License for more details.
16
17     You should have received a copy of the GNU General Public License
18     along with this program; if not, write to the Free Software
19     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20
21     $Id$
22 */
23
24 #include <iostream>
25 #include <float.h>
26 #include <cmath>
27 #include <climits>
28 #include <cfloat>
29 #include <cmath>
30
31 #include <glibmm/thread.h>
32 #include <sigc++/bind.h>
33
34 #include "ardour/curve.h"
35
36 #include "i18n.h"
37
38 using namespace std;
39 using namespace ARDOUR;
40 using namespace sigc;
41
42 Curve::Curve (double minv, double maxv, double canv, bool nostate)
43         : AutomationList (canv, nostate)
44 {
45         min_yval = minv;
46         max_yval = maxv;
47 }
48
49 Curve::Curve (const Curve& other)
50         : AutomationList (other)
51 {
52         min_yval = other.min_yval;
53         max_yval = other.max_yval;
54 }
55
56 Curve::Curve (const Curve& other, double start, double end)
57         : AutomationList (other, start, end)
58 {
59         min_yval = other.min_yval;
60         max_yval = other.max_yval;
61 }
62
63 Curve::~Curve ()
64 {
65 }
66
67 void
68 Curve::solve ()
69 {
70         uint32_t npoints;
71
72         if (!_dirty) {
73                 return;
74         }
75         
76         if ((npoints = events.size()) > 2) {
77                 
78                 /* Compute coefficients needed to efficiently compute a constrained spline
79                    curve. See "Constrained Cubic Spline Interpolation" by CJC Kruger
80                    (www.korf.co.uk/spline.pdf) for more details.
81                 */
82
83                 double x[npoints];
84                 double y[npoints];
85                 uint32_t i;
86                 AutomationEventList::iterator xx;
87
88                 for (i = 0, xx = events.begin(); xx != events.end(); ++xx, ++i) {
89                         x[i] = (double) (*xx)->when;
90                         y[i] = (double) (*xx)->value;
91                 }
92
93                 double lp0, lp1, fpone;
94
95                 lp0 =(x[1] - x[0])/(y[1] - y[0]);
96                 lp1 = (x[2] - x[1])/(y[2] - y[1]);
97
98                 if (lp0*lp1 < 0) {
99                         fpone = 0;
100                 } else {
101                         fpone = 2 / (lp1 + lp0);
102                 }
103
104                 double fplast = 0;
105
106                 for (i = 0, xx = events.begin(); xx != events.end(); ++xx, ++i) {
107                         
108                         CurvePoint* cp = dynamic_cast<CurvePoint*>(*xx);
109
110                         if (cp == 0) {
111                                 fatal  << _("programming error: ")
112                                        << X_("non-CurvePoint event found in event list for a Curve")
113                                        << endmsg;
114                                 /*NOTREACHED*/
115                         }
116                         
117                         double xdelta;   /* gcc is wrong about possible uninitialized use */
118                         double xdelta2;  /* ditto */
119                         double ydelta;   /* ditto */
120                         double fppL, fppR;
121                         double fpi;
122
123                         if (i > 0) {
124                                 xdelta = x[i] - x[i-1];
125                                 xdelta2 = xdelta * xdelta;
126                                 ydelta = y[i] - y[i-1];
127                         }
128
129                         /* compute (constrained) first derivatives */
130                         
131                         if (i == 0) {
132
133                                 /* first segment */
134                                 
135                                 fplast = ((3 * (y[1] - y[0]) / (2 * (x[1] - x[0]))) - (fpone * 0.5));
136
137                                 /* we don't store coefficients for i = 0 */
138
139                                 continue;
140
141                         } else if (i == npoints - 1) {
142
143                                 /* last segment */
144
145                                 fpi = ((3 * ydelta) / (2 * xdelta)) - (fplast * 0.5);
146                                 
147                         } else {
148
149                                 /* all other segments */
150
151                                 double slope_before = ((x[i+1] - x[i]) / (y[i+1] - y[i]));
152                                 double slope_after = (xdelta / ydelta);
153
154                                 if (slope_after * slope_before < 0.0) {
155                                         /* slope changed sign */
156                                         fpi = 0.0;
157                                 } else {
158                                         fpi = 2 / (slope_before + slope_after);
159                                 }
160                                 
161                         }
162
163                         /* compute second derivative for either side of control point `i' */
164                         
165                         fppL = (((-2 * (fpi + (2 * fplast))) / (xdelta))) +
166                                 ((6 * ydelta) / xdelta2);
167                         
168                         fppR = (2 * ((2 * fpi) + fplast) / xdelta) -
169                                 ((6 * ydelta) / xdelta2);
170                         
171                         /* compute polynomial coefficients */
172
173                         double b, c, d;
174
175                         d = (fppR - fppL) / (6 * xdelta);   
176                         c = ((x[i] * fppL) - (x[i-1] * fppR))/(2 * xdelta);
177                         
178                         double xim12, xim13;
179                         double xi2, xi3;
180                         
181                         xim12 = x[i-1] * x[i-1];  /* "x[i-1] squared" */
182                         xim13 = xim12 * x[i-1];   /* "x[i-1] cubed" */
183                         xi2 = x[i] * x[i];        /* "x[i] squared" */
184                         xi3 = xi2 * x[i];         /* "x[i] cubed" */
185                         
186                         b = (ydelta - (c * (xi2 - xim12)) - (d * (xi3 - xim13))) / xdelta;
187
188                         /* store */
189
190                         cp->coeff[0] = y[i-1] - (b * x[i-1]) - (c * xim12) - (d * xim13);
191                         cp->coeff[1] = b;
192                         cp->coeff[2] = c;
193                         cp->coeff[3] = d;
194
195                         fplast = fpi;
196                 }
197                 
198         }
199
200         _dirty = false;
201 }
202
203 bool
204 Curve::rt_safe_get_vector (double x0, double x1, float *vec, int32_t veclen)
205 {
206         Glib::Mutex::Lock lm (lock, Glib::TRY_LOCK);
207
208         if (!lm.locked()) {
209                 return false;
210         } else {
211                 _get_vector (x0, x1, vec, veclen);
212                 return true;
213         }
214 }
215
216 void
217 Curve::get_vector (double x0, double x1, float *vec, int32_t veclen)
218 {
219         Glib::Mutex::Lock lm (lock);
220         _get_vector (x0, x1, vec, veclen);
221 }
222
223 void
224 Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen)
225 {
226         double rx, dx, lx, hx, max_x, min_x;
227         int32_t i;
228         int32_t original_veclen;
229         int32_t npoints;
230
231         if ((npoints = events.size()) == 0) {
232                  for (i = 0; i < veclen; ++i) {
233                          vec[i] = default_value;
234                  }
235                  return;
236         }
237
238         /* events is now known not to be empty */
239
240         max_x = events.back()->when;
241         min_x = events.front()->when;
242
243         lx = max (min_x, x0);
244
245         if (x1 < 0) {
246                 x1 = events.back()->when;
247         }
248
249         hx = min (max_x, x1);
250
251         original_veclen = veclen;
252
253         if (x0 < min_x) {
254
255                 /* fill some beginning section of the array with the 
256                    initial (used to be default) value 
257                 */
258
259                 double frac = (min_x - x0) / (x1 - x0);
260                 int32_t subveclen = (int32_t) floor (veclen * frac);
261                 
262                 subveclen = min (subveclen, veclen);
263
264                 for (i = 0; i < subveclen; ++i) {
265                         vec[i] = events.front()->value;
266                 }
267
268                 veclen -= subveclen;
269                 vec += subveclen;
270         }
271
272         if (veclen && x1 > max_x) {
273
274                 /* fill some end section of the array with the default or final value */
275
276                 double frac = (x1 - max_x) / (x1 - x0);
277
278                 int32_t subveclen = (int32_t) floor (original_veclen * frac);
279
280                 float val;
281                 
282                 subveclen = min (subveclen, veclen);
283
284                 val = events.back()->value;
285
286                 i = veclen - subveclen;
287
288                 for (i = veclen - subveclen; i < veclen; ++i) {
289                         vec[i] = val;
290                 }
291
292                 veclen -= subveclen;
293         }
294
295         if (veclen == 0) {
296                 return;
297         }
298
299         if (npoints == 1 ) {
300         
301                 for (i = 0; i < veclen; ++i) {
302                         vec[i] = events.front()->value;
303                 }
304                 return;
305         }
306  
307  
308         if (npoints == 2) {
309  
310                 /* linear interpolation between 2 points */
311  
312                 /* XXX I'm not sure that this is the right thing to
313                    do here. but its not a common case for the envisaged
314                    uses.
315                 */
316         
317                 if (veclen > 1) {
318                         dx = (hx - lx) / (veclen - 1) ;
319                 } else {
320                         dx = 0; // not used
321                 }
322         
323                 double slope = (events.back()->value - events.front()->value)/  
324                         (events.back()->when - events.front()->when);
325                 double yfrac = dx*slope;
326  
327                 vec[0] = events.front()->value + slope * (lx - events.front()->when);
328  
329                 for (i = 1; i < veclen; ++i) {
330                         vec[i] = vec[i-1] + yfrac;
331                 }
332  
333                 return;
334         }
335  
336         if (_dirty) {
337                 solve ();
338         }
339
340         rx = lx;
341
342         if (veclen > 1) {
343
344                 dx = (hx - lx) / veclen;
345
346                 for (i = 0; i < veclen; ++i, rx += dx) {
347                         vec[i] = multipoint_eval (rx);
348                 }
349         }
350 }
351
352 double
353 Curve::unlocked_eval (double x)
354 {
355         if (_dirty) {
356                 solve ();
357         }
358
359         return shared_eval (x);
360 }
361
362 double
363 Curve::multipoint_eval (double x)
364 {       
365         pair<AutomationEventList::iterator,AutomationEventList::iterator> range;
366
367         if ((lookup_cache.left < 0) ||
368             ((lookup_cache.left > x) || 
369              (lookup_cache.range.first == events.end()) || 
370              ((*lookup_cache.range.second)->when < x))) {
371                 
372                 TimeComparator cmp;
373                 ControlEvent cp (x, 0.0);
374
375                 lookup_cache.range = equal_range (events.begin(), events.end(), &cp, cmp);
376         }
377
378         range = lookup_cache.range;
379
380         /* EITHER 
381            
382            a) x is an existing control point, so first == existing point, second == next point
383
384            OR
385
386            b) x is between control points, so range is empty (first == second, points to where
387                to insert x)
388            
389         */
390
391         if (range.first == range.second) {
392
393                 /* x does not exist within the list as a control point */
394                 
395                 lookup_cache.left = x;
396
397                 if (range.first == events.begin()) {
398                         /* we're before the first point */
399                         // return default_value;
400                         events.front()->value;
401                 }
402                 
403                 if (range.second == events.end()) {
404                         /* we're after the last point */
405                         return events.back()->value;
406                 }
407
408                 double x2 = x * x;
409                 CurvePoint* cp = dynamic_cast<CurvePoint*> (*range.second);
410
411                 return cp->coeff[0] + (cp->coeff[1] * x) + (cp->coeff[2] * x2) + (cp->coeff[3] * x2 * x);
412         } 
413
414         /* x is a control point in the data */
415         /* invalidate the cached range because its not usable */
416         lookup_cache.left = -1;
417         return (*range.first)->value;
418 }
419
420 ControlEvent*
421 Curve::point_factory (double when, double val) const
422 {
423         return new CurvePoint (when, val);
424 }
425
426 ControlEvent*
427 Curve::point_factory (const ControlEvent& other) const
428 {
429         return new CurvePoint (other.when, other.value);
430 }
431
432 Change
433 Curve::restore_state (StateManager::State& state)
434 {
435         mark_dirty ();
436         return AutomationList::restore_state (state);
437 }
438
439
440 extern "C" {
441
442 void 
443 curve_get_vector_from_c (void *arg, double x0, double x1, float* vec, int32_t vecsize)
444 {
445         static_cast<Curve*>(arg)->get_vector (x0, x1, vec, vecsize);
446 }
447
448 }