Tempo ramps - switch MusicLocked tempos to beat-based dragging. fix various bugs...
[ardour.git] / libs / ardour / tempo.cc
1 /*
2     Copyright (C) 2000-2002 Paul Davis
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <algorithm>
21 #include <stdexcept>
22 #include <cmath>
23
24 #include <unistd.h>
25
26 #include <glibmm/threads.h>
27
28 #include "pbd/enumwriter.h"
29 #include "pbd/xml++.h"
30 #include "evoral/Beats.hpp"
31
32 #include "ardour/debug.h"
33 #include "ardour/lmath.h"
34 #include "ardour/tempo.h"
35
36 #include "i18n.h"
37 #include <locale.h>
38
39 using namespace std;
40 using namespace ARDOUR;
41 using namespace PBD;
42
43 using Timecode::BBT_Time;
44
45 /* _default tempo is 4/4 qtr=120 */
46
47 Meter    TempoMap::_default_meter (4.0, 4.0);
48 Tempo    TempoMap::_default_tempo (120.0);
49
50 /***********************************************************************/
51
52 double
53 Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
54 {
55         /* This is tempo- and meter-sensitive. The number it returns
56            is based on the interval between any two lines in the
57            grid that is constructed from tempo and meter sections.
58
59            The return value IS NOT interpretable in terms of "beats".
60         */
61
62         return (60.0 * sr) / (tempo.beats_per_minute() * (_note_type/tempo.note_type()));
63 }
64
65 double
66 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
67 {
68         return frames_per_grid (tempo, sr) * _divisions_per_bar;
69 }
70
71 /***********************************************************************/
72
73 const string TempoSection::xml_state_node_name = "Tempo";
74
75 TempoSection::TempoSection (const XMLNode& node)
76         : MetricSection (0.0), Tempo (TempoMap::default_tempo())
77 {
78         const XMLProperty *prop;
79         LocaleGuard lg;
80         BBT_Time bbt;
81         double pulse;
82         uint32_t frame;
83
84         if ((prop = node.property ("start")) != 0) {
85                 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
86                             &bbt.bars,
87                             &bbt.beats,
88                             &bbt.ticks) == 3) {
89                         /* legacy session - start used to be in bbt*/
90                         _legacy_bbt = bbt;
91                         pulse = -1.0;
92                         set_pulse (pulse);
93                 }
94         } else {
95                 warning << _("TempoSection XML node has no \"start\" property") << endmsg;
96         }
97
98
99         if ((prop = node.property ("pulse")) != 0) {
100                 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1 || pulse < 0.0) {
101                         error << _("TempoSection XML node has an illegal \"beat\" value") << endmsg;
102                 } else {
103                         set_pulse (pulse);
104                 }
105         }
106         if ((prop = node.property ("frame")) != 0) {
107                 if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) {
108                         error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg;
109                 } else {
110                         set_frame (frame);
111                 }
112         }
113
114         if ((prop = node.property ("beats-per-minute")) == 0) {
115                 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
116                 throw failed_constructor();
117         }
118
119         if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
120                 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
121                 throw failed_constructor();
122         }
123
124         if ((prop = node.property ("note-type")) == 0) {
125                 /* older session, make note type be quarter by default */
126                 _note_type = 4.0;
127         } else {
128                 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
129                         error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
130                         throw failed_constructor();
131                 }
132         }
133
134         if ((prop = node.property ("movable")) == 0) {
135                 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
136                 throw failed_constructor();
137         }
138
139         set_movable (string_is_affirmative (prop->value()));
140
141         if ((prop = node.property ("active")) == 0) {
142                 warning << _("TempoSection XML node has no \"active\" property") << endmsg;
143                 set_active(true);
144         } else {
145                 set_active (string_is_affirmative (prop->value()));
146         }
147
148         if ((prop = node.property ("bar-offset")) == 0) {
149                 _bar_offset = -1.0;
150         } else {
151                 if (sscanf (prop->value().c_str(), "%lf", &_bar_offset) != 1 || _bar_offset < 0.0) {
152                         error << _("TempoSection XML node has an illegal \"bar-offset\" value") << endmsg;
153                         throw failed_constructor();
154                 }
155         }
156
157         if ((prop = node.property ("tempo-type")) == 0) {
158                 _type = Constant;
159         } else {
160                 _type = Type (string_2_enum (prop->value(), _type));
161         }
162
163         if ((prop = node.property ("lock-style")) == 0) {
164                 set_position_lock_style (MusicTime);
165         } else {
166                 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
167         }
168 }
169
170 XMLNode&
171 TempoSection::get_state() const
172 {
173         XMLNode *root = new XMLNode (xml_state_node_name);
174         char buf[256];
175         LocaleGuard lg;
176
177         snprintf (buf, sizeof (buf), "%f", pulse());
178         root->add_property ("pulse", buf);
179         snprintf (buf, sizeof (buf), "%li", frame());
180         root->add_property ("frame", buf);
181         snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
182         root->add_property ("beats-per-minute", buf);
183         snprintf (buf, sizeof (buf), "%f", _note_type);
184         root->add_property ("note-type", buf);
185         // snprintf (buf, sizeof (buf), "%f", _bar_offset);
186         // root->add_property ("bar-offset", buf);
187         snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
188         root->add_property ("movable", buf);
189         snprintf (buf, sizeof (buf), "%s", active()?"yes":"no");
190         root->add_property ("active", buf);
191         root->add_property ("tempo-type", enum_2_string (_type));
192         root->add_property ("lock-style", enum_2_string (position_lock_style()));
193
194         return *root;
195 }
196
197 void
198
199 TempoSection::update_bar_offset_from_bbt (const Meter& m)
200 {
201         _bar_offset = (pulse() * BBT_Time::ticks_per_beat) /
202                 (m.divisions_per_bar() * BBT_Time::ticks_per_beat);
203
204         DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Tempo set bar offset to %1 from %2 w/%3\n", _bar_offset, pulse(), m.divisions_per_bar()));
205 }
206
207 void
208 TempoSection::set_type (Type type)
209 {
210         _type = type;
211 }
212
213 /** returns the tempo in whole pulses per minute at the zero-based (relative to session) frame.
214 */
215 double
216 TempoSection::tempo_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
217 {
218
219         if (_type == Constant) {
220                 return pulses_per_minute();
221         }
222
223         return pulse_tempo_at_time (frame_to_minute (f - frame(), frame_rate));
224 }
225
226 /** returns the zero-based frame (relative to session)
227    where the tempo in whole pulses per minute occurs in this section.
228    beat b is only used for constant tempos.
229    note that the tempo map may have multiple such values.
230 */
231 framepos_t
232 TempoSection::frame_at_tempo (const double& ppm, const double& b, const framecnt_t& frame_rate) const
233 {
234         if (_type == Constant) {
235                 return ((b - pulse())  * frames_per_pulse (frame_rate))  + frame();
236         }
237
238         return minute_to_frame (time_at_pulse_tempo (ppm), frame_rate) + frame();
239 }
240 /** returns the tempo in pulses per minute at the zero-based (relative to session) beat.
241 */
242 double
243 TempoSection::tempo_at_pulse (const double& p) const
244 {
245
246         if (_type == Constant) {
247                 return pulses_per_minute();
248         }
249         double const ppm = pulse_tempo_at_pulse (p - pulse());
250         return ppm;
251 }
252
253 /** returns the zero-based beat (relative to session)
254    where the tempo in whole pulses per minute occurs given frame f. frame f is only used for constant tempos.
255    note that the session tempo map may have multiple beats at a given tempo.
256 */
257 double
258 TempoSection::pulse_at_tempo (const double& ppm, const framepos_t& f, const framecnt_t& frame_rate) const
259 {
260         if (_type == Constant) {
261                 double const beats = ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
262                 return  beats;
263         }
264
265         return pulse_at_pulse_tempo (ppm) + pulse();
266 }
267
268 /** returns the zero-based pulse (relative to session origin)
269    where the zero-based frame (relative to session)
270    lies.
271 */
272 double
273 TempoSection::pulse_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
274 {
275         if (_type == Constant) {
276                 return ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
277         }
278
279         return pulse_at_time (frame_to_minute (f - frame(), frame_rate)) + pulse();
280 }
281
282 /** returns the zero-based frame (relative to session start frame)
283    where the zero-based pulse (relative to session start)
284    falls.
285 */
286
287 framepos_t
288 TempoSection::frame_at_pulse (const double& p, const framecnt_t& frame_rate) const
289 {
290         if (_type == Constant) {
291                 return (framepos_t) floor ((p - pulse()) * frames_per_pulse (frame_rate)) + frame();
292         }
293
294         return minute_to_frame (time_at_pulse (p - pulse()), frame_rate) + frame();
295 }
296
297 /*
298 Ramp Overview
299
300       |                     *
301 Tempo |                   *
302 Tt----|-----------------*|
303 Ta----|--------------|*  |
304       |            * |   |
305       |         *    |   |
306       |     *        |   |
307 T0----|*             |   |
308   *   |              |   |
309       _______________|___|____
310       time           a   t (next tempo)
311       [        c         ] defines c
312
313 Duration in beats at time a is the integral of some Tempo function.
314 In our case, the Tempo function (Tempo at time t) is
315 T(t) = T0(e^(ct))
316
317 with function constant
318 c = log(Ta/T0)/a
319 so
320 a = log(Ta/T0)/c
321
322 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
323 b(t) = T0(e^(ct) - 1) / c
324
325 To find the time t at beat duration b, we use the inverse function of the beat function (the time function) which can be shown to be:
326 t(b) = log((cb / T0) + 1) / c
327
328 The time t at which Tempo T occurs is a as above:
329 t(T) = log(T / T0) / c
330
331 The beat at which a Tempo T occurs is:
332 b(T) = (T - T0) / c
333
334 The Tempo at which beat b occurs is:
335 T(b) = b.c + T0
336
337 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
338 Our problem is that we usually don't know t.
339 We almost always know the duration in beats between this and the new section, so we need to find c in terms of the beat function.
340 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
341 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
342
343 By substituting our expanded t as a in the c function above, our problem is reduced to:
344 c = T0 (e^(log (Ta / T0)) - 1) / b
345
346 We can now store c for future time calculations.
347 If the following tempo section (the one that defines c in conjunction with this one)
348 is changed or moved, c is no longer valid.
349
350 The public methods are session-relative.
351
352 Most of this stuff is taken from this paper:
353
354 WHERE’S THE BEAT?
355 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
356 Jan C. Schacher
357 Martin Neukom
358 Zurich University of Arts
359 Institute for Computer Music and Sound Technology
360
361 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
362
363 */
364
365 /*
366   compute this ramp's function constant using the end tempo (in whole pulses per minute)
367   and duration (pulses into global start) of some later tempo section.
368 */
369 double
370 TempoSection::compute_c_func_pulse (const double& end_bpm, const double& end_pulse, const framecnt_t& frame_rate)
371 {
372         double const log_tempo_ratio = log (end_bpm / pulses_per_minute());
373         return pulses_per_minute() *  (exp (log_tempo_ratio) - 1) / (end_pulse - pulse());
374 }
375
376 /* compute the function constant from some later tempo section, given tempo (whole pulses/min.) and distance (in frames) from session origin */
377 double
378 TempoSection::compute_c_func_frame (const double& end_bpm, const framepos_t& end_frame, const framecnt_t& frame_rate) const
379 {
380         return c_func (end_bpm, frame_to_minute (end_frame - frame(), frame_rate));
381 }
382
383 framecnt_t
384 TempoSection::minute_to_frame (const double& time, const framecnt_t& frame_rate) const
385 {
386         return (framecnt_t) floor ((time * 60.0 * frame_rate) + 0.5);
387 }
388
389 double
390 TempoSection::frame_to_minute (const framecnt_t& frame, const framecnt_t& frame_rate) const
391 {
392         return (frame / (double) frame_rate) / 60.0;
393 }
394
395 /* position function */
396 double
397 TempoSection::a_func (double end_bpm, double c_func) const
398 {
399         return log (end_bpm / pulses_per_minute()) /  c_func;
400 }
401
402 /*function constant*/
403 double
404 TempoSection::c_func (double end_bpm, double end_time) const
405 {
406         return log (end_bpm / pulses_per_minute()) /  end_time;
407 }
408
409 /* tempo in ppm at time in minutes */
410 double
411 TempoSection::pulse_tempo_at_time (const double& time) const
412 {
413         return exp (_c_func * time) * pulses_per_minute();
414 }
415
416 /* time in minutes at tempo in ppm */
417 double
418 TempoSection::time_at_pulse_tempo (const double& pulse_tempo) const
419 {
420         return log (pulse_tempo / pulses_per_minute()) / _c_func;
421 }
422
423 /* tick at tempo in ppm */
424 double
425 TempoSection::pulse_at_pulse_tempo (const double& pulse_tempo) const
426 {
427         return (pulse_tempo - pulses_per_minute()) / _c_func;
428 }
429
430 /* tempo in ppm at tick */
431 double
432 TempoSection::pulse_tempo_at_pulse (const double& pulse) const
433 {
434         return (pulse * _c_func) + pulses_per_minute();
435 }
436
437 /* pulse at time in minutes */
438 double
439 TempoSection::pulse_at_time (const double& time) const
440 {
441         return ((exp (_c_func * time)) - 1) * (pulses_per_minute() / _c_func);
442 }
443
444 /* time in minutes at pulse */
445 double
446 TempoSection::time_at_pulse (const double& pulse) const
447 {
448         return log (((_c_func * pulse) / pulses_per_minute()) + 1) / _c_func;
449 }
450
451
452 void
453 TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
454 {
455         double new_beat;
456
457         if (_bar_offset < 0.0) {
458                 /* not set yet */
459                 return;
460         }
461
462         new_beat = pulse();
463
464         double ticks = BBT_Time::ticks_per_beat * meter.divisions_per_bar() * _bar_offset;
465         new_beat = ticks / BBT_Time::ticks_per_beat;
466
467         DEBUG_TRACE (DEBUG::TempoMath, string_compose ("from bar offset %1 and dpb %2, ticks = %3->%4 beats = %5\n",
468                                                        _bar_offset, meter.divisions_per_bar(), ticks, new_beat, new_beat));
469
470         set_pulse (new_beat);
471 }
472
473 /***********************************************************************/
474
475 const string MeterSection::xml_state_node_name = "Meter";
476
477 MeterSection::MeterSection (const XMLNode& node)
478         : MetricSection (0.0), Meter (TempoMap::default_meter())
479 {
480         XMLProperty const * prop;
481         BBT_Time start;
482         LocaleGuard lg;
483         const XMLProperty *prop;
484         BBT_Time bbt;
485         double pulse = 0.0;
486         double beat = 0.0;
487         framepos_t frame = 0;
488         pair<double, BBT_Time> start;
489
490         if ((prop = node.property ("start")) != 0) {
491                 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
492                     &bbt.bars,
493                     &bbt.beats,
494                     &bbt.ticks) < 3) {
495                         error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
496                 } else {
497                         /* legacy session - start used to be in bbt*/
498                         pulse = -1.0;
499                 }
500         } else {
501                 error << _("MeterSection XML node has no \"start\" property") << endmsg;
502         }
503
504         if ((prop = node.property ("pulse")) != 0) {
505                 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1 || pulse < 0.0) {
506                         error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
507                 }
508         }
509         set_pulse (pulse);
510
511         if ((prop = node.property ("beat")) != 0) {
512                 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
513                         error << _("MeterSection XML node has an illegal \"beat\" vlue") << endmsg;
514                 }
515         }
516
517         start.first = beat;
518
519         if ((prop = node.property ("bbt")) == 0) {
520                 error << _("MeterSection XML node has no \"bbt\" property") << endmsg;
521         } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
522                     &bbt.bars,
523                     &bbt.beats,
524                     &bbt.ticks) < 3) {
525                 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
526                 //throw failed_constructor();
527         }
528
529         start.second = bbt;
530         set_beat (start);
531
532         if ((prop = node.property ("frame")) != 0) {
533                 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
534                         error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
535                 } else {
536                         set_frame (frame);
537                 }
538         }
539
540         /* beats-per-bar is old; divisions-per-bar is new */
541
542         if ((prop = node.property ("divisions-per-bar")) == 0) {
543                 if ((prop = node.property ("beats-per-bar")) == 0) {
544                         error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
545                         throw failed_constructor();
546                 }
547         }
548         if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
549                 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
550                 throw failed_constructor();
551         }
552
553         if ((prop = node.property ("note-type")) == 0) {
554                 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
555                 throw failed_constructor();
556         }
557         if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
558                 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
559                 throw failed_constructor();
560         }
561
562         if ((prop = node.property ("lock-style")) == 0) {
563                 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
564                 set_position_lock_style (MusicTime);
565         } else {
566                 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
567         }
568
569         if ((prop = node.property ("movable")) == 0) {
570                 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
571                 throw failed_constructor();
572         }
573
574         set_movable (string_is_affirmative (prop->value()));
575 }
576
577 XMLNode&
578 MeterSection::get_state() const
579 {
580         XMLNode *root = new XMLNode (xml_state_node_name);
581         char buf[256];
582         LocaleGuard lg;
583
584         snprintf (buf, sizeof (buf), "%lf", pulse());
585         root->add_property ("pulse", buf);
586         snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
587                   bbt().bars,
588                   bbt().beats,
589                   bbt().ticks);
590         root->add_property ("bbt", buf);
591         snprintf (buf, sizeof (buf), "%lf", beat());
592         root->add_property ("beat", buf);
593         snprintf (buf, sizeof (buf), "%f", _note_type);
594         root->add_property ("note-type", buf);
595         snprintf (buf, sizeof (buf), "%li", frame());
596         root->add_property ("frame", buf);
597         root->add_property ("lock-style", enum_2_string (position_lock_style()));
598         snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
599         root->add_property ("divisions-per-bar", buf);
600         snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
601         root->add_property ("movable", buf);
602
603         return *root;
604 }
605
606 /***********************************************************************/
607
608 struct MetricSectionSorter {
609     bool operator() (const MetricSection* a, const MetricSection* b) {
610             return a->pulse() < b->pulse();
611     }
612 };
613
614 struct MetricSectionFrameSorter {
615     bool operator() (const MetricSection* a, const MetricSection* b) {
616             return a->frame() < b->frame();
617     }
618 };
619
620 TempoMap::TempoMap (framecnt_t fr)
621 {
622         _frame_rate = fr;
623         BBT_Time start (1, 1, 0);
624
625         TempoSection *t = new TempoSection (0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Constant);
626         MeterSection *m = new MeterSection (0.0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
627
628         t->set_movable (false);
629         m->set_movable (false);
630
631         /* note: frame time is correct (zero) for both of these */
632
633         _metrics.push_back (t);
634         _metrics.push_back (m);
635
636 }
637
638 TempoMap::~TempoMap ()
639 {
640 }
641
642 void
643 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
644 {
645         bool removed = false;
646
647         {
648                 Glib::Threads::RWLock::WriterLock lm (lock);
649                 if ((removed = remove_tempo_locked (tempo))) {
650                         if (complete_operation) {
651                                 recompute_map (_metrics);
652                         }
653                 }
654         }
655
656         if (removed && complete_operation) {
657                 PropertyChanged (PropertyChange ());
658         }
659 }
660
661 bool
662 TempoMap::remove_tempo_locked (const TempoSection& tempo)
663 {
664         Metrics::iterator i;
665
666         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
667                 if (dynamic_cast<TempoSection*> (*i) != 0) {
668                         if (tempo.frame() == (*i)->frame()) {
669                                 if ((*i)->movable()) {
670                                         _metrics.erase (i);
671                                         return true;
672                                 }
673                         }
674                 }
675         }
676
677         return false;
678 }
679
680 void
681 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
682 {
683         bool removed = false;
684
685         {
686                 Glib::Threads::RWLock::WriterLock lm (lock);
687                 if ((removed = remove_meter_locked (tempo))) {
688                         if (complete_operation) {
689                                 recompute_map (_metrics);
690                         }
691                 }
692         }
693
694         if (removed && complete_operation) {
695                 PropertyChanged (PropertyChange ());
696         }
697 }
698
699 bool
700 TempoMap::remove_meter_locked (const MeterSection& tempo)
701 {
702         Metrics::iterator i;
703
704         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
705                 if (dynamic_cast<MeterSection*> (*i) != 0) {
706                         if (tempo.frame() == (*i)->frame()) {
707                                 if ((*i)->movable()) {
708                                         _metrics.erase (i);
709                                         return true;
710                                 }
711                         }
712                 }
713         }
714
715         return false;
716 }
717
718 void
719 TempoMap::do_insert (MetricSection* section)
720 {
721         bool need_add = true;
722         /* we only allow new meters to be inserted on beat 1 of an existing
723          * measure.
724          */
725         MeterSection* m = 0;
726         if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
727                 //assert (m->bbt().ticks == 0);
728
729                 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
730
731                         pair<double, BBT_Time> corrected = make_pair (m->pulse(), m->bbt());
732                         corrected.second.beats = 1;
733                         corrected.second.ticks = 0;
734                         corrected.first = bbt_to_beats_locked (_metrics, corrected.second);
735                         warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
736                                                    m->bbt(), corrected.second) << endmsg;
737                         //m->set_pulse (corrected);
738                 }
739         }
740
741         /* Look for any existing MetricSection that is of the same type and
742            in the same bar as the new one, and remove it before adding
743            the new one. Note that this means that if we find a matching,
744            existing section, we can break out of the loop since we're
745            guaranteed that there is only one such match.
746         */
747
748         for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
749
750                 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
751                 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
752                 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
753                 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
754
755                 if (tempo && insert_tempo) {
756
757                         /* Tempo sections */
758                         bool const ipm = insert_tempo->position_lock_style() == MusicTime;
759                         if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
760
761                                 if (!tempo->movable()) {
762
763                                         /* can't (re)move this section, so overwrite
764                                          * its data content (but not its properties as
765                                          * a section).
766                                          */
767
768                                         *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
769                                         (*i)->set_position_lock_style (AudioTime);
770                                         need_add = false;
771                                 } else {
772                                         _metrics.erase (i);
773                                 }
774                                 break;
775                         }
776
777                 } else if (meter && insert_meter) {
778
779                         /* Meter Sections */
780
781                         bool const ipm = insert_meter->position_lock_style() == MusicTime;
782
783                         if ((ipm && meter->pulse() == insert_meter->pulse()) || (!ipm && meter->frame() == insert_meter->frame())) {
784
785                                 if (!meter->movable()) {
786
787                                         /* can't (re)move this section, so overwrite
788                                          * its data content (but not its properties as
789                                          * a section
790                                          */
791
792                                         *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
793                                         (*i)->set_position_lock_style (insert_meter->position_lock_style());
794                                         need_add = false;
795                                 } else {
796                                         _metrics.erase (i);
797
798                                 }
799
800                                 break;
801                         }
802                 } else {
803                         /* non-matching types, so we don't care */
804                 }
805         }
806
807         /* Add the given MetricSection, if we didn't just reset an existing
808          * one above
809          */
810
811         if (need_add) {
812                 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
813                 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
814                 Metrics::iterator i;
815                 if (insert_meter) {
816                         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
817                                 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
818
819                                 if (meter) {
820                                         bool const ipm = insert_meter->position_lock_style() == MusicTime;
821                                         if ((ipm && meter->pulse() > insert_meter->pulse()) || (!ipm && meter->frame() > insert_meter->frame())) {
822                                                 break;
823                                         }
824                                 }
825                         }
826                 } else if (insert_tempo) {
827                         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
828                                 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
829
830                                 if (tempo) {
831                                         bool const ipm = insert_tempo->position_lock_style() == MusicTime;
832                                         if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
833                                                 break;
834                                         }
835                                 }
836                         }
837                 }
838
839                 _metrics.insert (i, section);
840                 //dump (_metrics, std::cerr);
841         }
842 }
843
844 void
845 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, TempoSection::Type type)
846 {
847         {
848                 Glib::Threads::RWLock::WriterLock lm (lock);
849                 TempoSection& first (first_tempo());
850                 if (ts.pulse() != first.pulse()) {
851                         remove_tempo_locked (ts);
852                         add_tempo_locked (tempo, pulse, true, type);
853                 } else {
854                         first.set_type (type);
855                         {
856                                 /* cannot move the first tempo section */
857                                 *static_cast<Tempo*>(&first) = tempo;
858                                 recompute_map (_metrics);
859                         }
860                 }
861         }
862
863         PropertyChanged (PropertyChange ());
864 }
865
866 void
867 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const framepos_t& frame, TempoSection::Type type)
868 {
869         {
870                 Glib::Threads::RWLock::WriterLock lm (lock);
871                 TempoSection& first (first_tempo());
872                 if (ts.frame() != first.frame()) {
873                         remove_tempo_locked (ts);
874                         add_tempo_locked (tempo, frame, true, type);
875                 } else {
876                         first.set_type (type);
877                         first.set_pulse (0.0);
878                         first.set_position_lock_style (AudioTime);
879                         {
880                                 /* cannot move the first tempo section */
881                                 *static_cast<Tempo*>(&first) = tempo;
882                                 recompute_map (_metrics);
883                         }
884                 }
885         }
886
887         PropertyChanged (PropertyChange ());
888 }
889
890 void
891 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, ARDOUR::TempoSection::Type type)
892 {
893         {
894                 Glib::Threads::RWLock::WriterLock lm (lock);
895                 add_tempo_locked (tempo, pulse, true, type);
896         }
897
898         PropertyChanged (PropertyChange ());
899 }
900
901 void
902 TempoMap::add_tempo (const Tempo& tempo, const framepos_t& frame, ARDOUR::TempoSection::Type type)
903 {
904         {
905                 Glib::Threads::RWLock::WriterLock lm (lock);
906                 add_tempo_locked (tempo, frame, true, type);
907         }
908
909
910         PropertyChanged (PropertyChange ());
911 }
912
913 void
914 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, bool recompute, ARDOUR::TempoSection::Type type)
915 {
916         TempoSection* ts = new TempoSection (pulse, tempo.beats_per_minute(), tempo.note_type(), type);
917
918         do_insert (ts);
919
920         if (recompute) {
921                 solve_map (_metrics, ts, Tempo (ts->beats_per_minute(), ts->note_type()), ts->pulse());
922         }
923 }
924
925 void
926 TempoMap::add_tempo_locked (const Tempo& tempo, framepos_t frame, bool recompute, ARDOUR::TempoSection::Type type)
927 {
928         TempoSection* ts = new TempoSection (frame, tempo.beats_per_minute(), tempo.note_type(), type);
929
930         do_insert (ts);
931
932         if (recompute) {
933                 solve_map (_metrics, ts, Tempo (ts->beats_per_minute(), ts->note_type()), ts->frame());
934         }
935 }
936
937 void
938 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
939 {
940         {
941                 Glib::Threads::RWLock::WriterLock lm (lock);
942                 MeterSection& first (first_meter());
943
944                 const PositionLockStyle pl = ms.position_lock_style();
945                 if (ms.pulse() != first.pulse()) {
946                         remove_meter_locked (ms);
947                         add_meter_locked (meter, pulse_at_beat_locked (_metrics, bbt_to_beats_locked (_metrics, where)), where, true);
948                 } else {
949                         /* cannot move the first meter section */
950                         *static_cast<Meter*>(&first) = meter;
951                         first.set_position_lock_style (pl);
952                         recompute_map (_metrics);
953                 }
954         }
955
956         PropertyChanged (PropertyChange ());
957 }
958
959 void
960 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const framepos_t& frame)
961 {
962         {
963                 Glib::Threads::RWLock::WriterLock lm (lock);
964                 MeterSection& first (first_meter());
965                 TempoSection& first_t (first_tempo());
966
967                 if (ms.pulse() != first.pulse()) {
968                         remove_meter_locked (ms);
969                         add_meter_locked (meter, frame, true);
970                 } else {
971                         /* cannot move the first meter section */
972                         *static_cast<Meter*>(&first) = meter;
973                         first.set_position_lock_style (AudioTime);
974                         first.set_pulse (0.0);
975                         first.set_frame (frame);
976                         pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
977                         first.set_beat (beat);
978                         recompute_meters (_metrics);
979                         first_t.set_frame (first.frame());
980                         first_t.set_pulse (0.0);
981                         first_t.set_position_lock_style (AudioTime);
982
983                         recompute_map (_metrics);
984                 }
985         }
986         PropertyChanged (PropertyChange ());
987 }
988
989
990 void
991 TempoMap::add_meter (const Meter& meter, const double& beat, const BBT_Time& where)
992 {
993         {
994                 Glib::Threads::RWLock::WriterLock lm (lock);
995                 add_meter_locked (meter, beat, where, true);
996         }
997
998
999 #ifndef NDEBUG
1000         if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1001                 dump (_metrics, std::cerr);
1002         }
1003 #endif
1004
1005         PropertyChanged (PropertyChange ());
1006 }
1007
1008 void
1009 TempoMap::add_meter (const Meter& meter, const framepos_t& frame)
1010 {
1011         {
1012                 Glib::Threads::RWLock::WriterLock lm (lock);
1013                 add_meter_locked (meter, frame, true);
1014         }
1015
1016
1017 #ifndef NDEBUG
1018         if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1019                 dump (_metrics, std::cerr);
1020         }
1021 #endif
1022
1023         PropertyChanged (PropertyChange ());
1024 }
1025
1026 void
1027 TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, bool recompute)
1028 {
1029         /* a new meter always starts a new bar on the first beat. so
1030            round the start time appropriately. remember that
1031            `where' is based on the existing tempo map, not
1032            the result after we insert the new meter.
1033
1034         */
1035
1036         if (where.beats != 1) {
1037                 where.beats = 1;
1038                 where.bars++;
1039         }
1040         /* new meters *always* start on a beat. */
1041         where.ticks = 0;
1042         double pulse = pulse_at_beat_locked (_metrics, beat);
1043
1044         MeterSection* new_meter = new MeterSection (pulse, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1045         do_insert (new_meter);
1046
1047         if (recompute) {
1048                 solve_map (_metrics, new_meter, Meter (meter.divisions_per_bar(), meter.note_divisor()), pulse);
1049         }
1050
1051 }
1052
1053 void
1054 TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, bool recompute)
1055 {
1056
1057         MeterSection* new_meter = new MeterSection (frame, 0.0, meter.divisions_per_bar(), meter.note_divisor());
1058
1059         do_insert (new_meter);
1060
1061         if (recompute) {
1062                 solve_map (_metrics, new_meter, Meter (new_meter->divisions_per_bar(), new_meter->note_divisor()), frame);
1063         }
1064
1065 }
1066
1067 /**
1068 * This is for a gui that needs to know the frame of a tempo section if it were to be moved to some bbt time,
1069 * taking any possible reordering as a consequence of this into account.
1070 * @param section - the section to be altered
1071 * @param bpm - the new Tempo
1072 * @param bbt - the bbt where the altered tempo will fall
1073 * @return returns - the position in frames where the new tempo section will lie.
1074 */
1075 framepos_t
1076 TempoMap::predict_tempo_frame (TempoSection* section, const Tempo& bpm, const BBT_Time& bbt)
1077 {
1078         Glib::Threads::RWLock::ReaderLock lm (lock);
1079         Metrics future_map;
1080         framepos_t ret = 0;
1081         TempoSection* new_section = copy_metrics_and_point (future_map, section);
1082
1083         double const beat = bbt_to_beats_locked (future_map, bbt);
1084         if (solve_map (future_map, new_section, bpm, pulse_at_beat_locked (future_map, beat))) {
1085                 ret = new_section->frame();
1086         } else {
1087                 ret = frame_at_beat_locked (_metrics, beat);
1088         }
1089
1090         Metrics::const_iterator d = future_map.begin();
1091         while (d != future_map.end()) {
1092                 delete (*d);
1093                 ++d;
1094         }
1095         return ret;
1096 }
1097
1098 double
1099 TempoMap::predict_tempo_pulse (TempoSection* section, const Tempo& bpm, const framepos_t& frame)
1100 {
1101         Glib::Threads::RWLock::ReaderLock lm (lock);
1102         Metrics future_map;
1103         double ret = 0.0;
1104         TempoSection* new_section = copy_metrics_and_point (future_map, section);
1105
1106         if (solve_map (future_map, new_section, bpm, frame)) {
1107                 ret = new_section->pulse();
1108         } else {
1109                 ret = pulse_at_frame_locked (_metrics, frame);
1110         }
1111
1112         Metrics::const_iterator d = future_map.begin();
1113         while (d != future_map.end()) {
1114                 delete (*d);
1115                 ++d;
1116         }
1117         return ret;
1118 }
1119
1120 void
1121 TempoMap::gui_move_tempo_frame (TempoSection* ts,  const Tempo& bpm, const framepos_t& frame)
1122 {
1123         Metrics future_map;
1124         {
1125                 Glib::Threads::RWLock::WriterLock lm (lock);
1126                 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
1127                 if (solve_map (future_map, new_section, bpm, frame)) {
1128                         solve_map (_metrics, ts, bpm, frame);
1129                 }
1130         }
1131
1132         Metrics::const_iterator d = future_map.begin();
1133         while (d != future_map.end()) {
1134                 delete (*d);
1135                 ++d;
1136         }
1137
1138         MetricPositionChanged (); // Emit Signal
1139 }
1140
1141 void
1142 TempoMap::gui_move_tempo_beat (TempoSection* ts,  const Tempo& bpm, const double& beat)
1143 {
1144         Metrics future_map;
1145         {
1146                 Glib::Threads::RWLock::WriterLock lm (lock);
1147                 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
1148                 if (solve_map (future_map, new_section, bpm, pulse_at_beat_locked (future_map, beat))) {
1149                         solve_map (_metrics, ts, bpm,  pulse_at_beat_locked (_metrics, beat));
1150                 }
1151         }
1152
1153         Metrics::const_iterator d = future_map.begin();
1154         while (d != future_map.end()) {
1155                 delete (*d);
1156                 ++d;
1157         }
1158
1159         MetricPositionChanged (); // Emit Signal
1160 }
1161
1162 void
1163 TempoMap::gui_move_meter (MeterSection* ms, const Meter& mt, const framepos_t&  frame)
1164 {
1165         {
1166                 Glib::Threads::RWLock::WriterLock lm (lock);
1167                 solve_map (_metrics, ms, mt, frame);
1168         }
1169
1170         MetricPositionChanged (); // Emit Signal
1171 }
1172
1173 void
1174 TempoMap::gui_move_meter (MeterSection* ms, const Meter& mt, const double&  beat)
1175 {
1176         {
1177                 Glib::Threads::RWLock::WriterLock lm (lock);
1178                 solve_map (_metrics, ms, mt, pulse_at_beat_locked (_metrics, beat));
1179         }
1180
1181         MetricPositionChanged (); // Emit Signal
1182 }
1183
1184 bool
1185 TempoMap::gui_change_tempo (TempoSection* ts,  const Tempo& bpm)
1186 {
1187         Metrics future_map;
1188         bool can_solve = false;
1189         {
1190                 Glib::Threads::RWLock::WriterLock lm (lock);
1191                 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
1192                 new_section->set_beats_per_minute (bpm.beats_per_minute());
1193                 recompute_tempos (future_map);
1194
1195                 if (check_solved (future_map, true)) {
1196                         ts->set_beats_per_minute (bpm.beats_per_minute());
1197                         recompute_map (_metrics);
1198                         can_solve = true;
1199                 }
1200         }
1201
1202         Metrics::const_iterator d = future_map.begin();
1203         while (d != future_map.end()) {
1204                 delete (*d);
1205                 ++d;
1206         }
1207         if (can_solve) {
1208                 MetricPositionChanged (); // Emit Signal
1209         }
1210         return can_solve;
1211 }
1212
1213 TempoSection*
1214 TempoMap::copy_metrics_and_point (Metrics& copy, TempoSection* section)
1215 {
1216         TempoSection* t;
1217         TempoSection* ret = 0;
1218         MeterSection* m;
1219
1220         for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1221                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1222                         if (t == section) {
1223                                 if (t->position_lock_style() == MusicTime) {
1224                                         ret = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
1225                                 } else {
1226                                         ret = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
1227                                 }
1228                                 ret->set_active (t->active());
1229                                 ret->set_movable (t->movable());
1230                                 copy.push_back (ret);
1231                                 continue;
1232                         }
1233                         TempoSection* cp = 0;
1234                         if (t->position_lock_style() == MusicTime) {
1235                                 cp = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
1236                         } else {
1237                                 cp = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
1238                         }
1239                         cp->set_active (t->active());
1240                         cp->set_movable (t->movable());
1241                         copy.push_back (cp);
1242                 }
1243                 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1244                         if (m->position_lock_style() == MusicTime) {
1245                                 copy.push_back (new MeterSection (m->pulse(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor()));
1246                         } else {
1247                                 copy.push_back (new MeterSection (m->frame(), m->beat(), m->divisions_per_bar(), m->note_divisor()));
1248                         }
1249                 }
1250         }
1251         recompute_map (copy);
1252         return ret;
1253 }
1254
1255 bool
1256 TempoMap::can_solve_bbt (TempoSection* ts,  const Tempo& bpm, const BBT_Time& bbt)
1257 {
1258         Metrics copy;
1259         TempoSection* new_section = 0;
1260
1261         {
1262                 Glib::Threads::RWLock::ReaderLock lm (lock);
1263                 new_section = copy_metrics_and_point (copy, ts);
1264         }
1265
1266         double const beat = bbt_to_beats_locked (copy, bbt);
1267         bool ret = solve_map (copy, new_section, bpm, pulse_at_beat_locked (copy, beat));
1268
1269         Metrics::const_iterator d = copy.begin();
1270         while (d != copy.end()) {
1271                 delete (*d);
1272                 ++d;
1273         }
1274
1275         return ret;
1276 }
1277
1278 void
1279 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1280 {
1281         Tempo newtempo (beats_per_minute, note_type);
1282         TempoSection* t;
1283
1284         for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1285                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1286                         if (!t->active()) {
1287                                 continue;
1288                         }
1289                         {
1290                                 Glib::Threads::RWLock::WriterLock lm (lock);
1291                                 *((Tempo*) t) = newtempo;
1292                                 recompute_map (_metrics);
1293                         }
1294                         PropertyChanged (PropertyChange ());
1295                         break;
1296                 }
1297         }
1298 }
1299
1300 void
1301 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1302 {
1303         Tempo newtempo (beats_per_minute, note_type);
1304
1305         TempoSection* prev;
1306         TempoSection* first;
1307         Metrics::iterator i;
1308
1309         /* find the TempoSection immediately preceding "where"
1310          */
1311
1312         for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1313
1314                 if ((*i)->frame() > where) {
1315                         break;
1316                 }
1317
1318                 TempoSection* t;
1319
1320                 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1321                         if (!t->active()) {
1322                                 continue;
1323                         }
1324                         if (!first) {
1325                                 first = t;
1326                         }
1327                         prev = t;
1328                 }
1329         }
1330
1331         if (!prev) {
1332                 if (!first) {
1333                         error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1334                         return;
1335                 }
1336
1337                 prev = first;
1338         }
1339
1340         /* reset */
1341
1342         {
1343                 Glib::Threads::RWLock::WriterLock lm (lock);
1344                 /* cannot move the first tempo section */
1345                 *((Tempo*)prev) = newtempo;
1346                 recompute_map (_metrics);
1347         }
1348
1349         PropertyChanged (PropertyChange ());
1350 }
1351
1352 const MeterSection&
1353 TempoMap::first_meter () const
1354 {
1355         const MeterSection *m = 0;
1356
1357         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1358                 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1359                         return *m;
1360                 }
1361         }
1362
1363         fatal << _("programming error: no meter section in tempo map!") << endmsg;
1364         abort(); /*NOTREACHED*/
1365         return *m;
1366 }
1367
1368 MeterSection&
1369 TempoMap::first_meter ()
1370 {
1371         MeterSection *m = 0;
1372
1373         /* CALLER MUST HOLD LOCK */
1374
1375         for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1376                 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1377                         return *m;
1378                 }
1379         }
1380
1381         fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1382         abort(); /*NOTREACHED*/
1383         return *m;
1384 }
1385
1386 const TempoSection&
1387 TempoMap::first_tempo () const
1388 {
1389         const TempoSection *t = 0;
1390
1391         /* CALLER MUST HOLD LOCK */
1392
1393         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1394                 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1395                         if (!t->active()) {
1396                                 continue;
1397                         }
1398                         return *t;
1399                 }
1400         }
1401
1402         fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1403         abort(); /*NOTREACHED*/
1404         return *t;
1405 }
1406
1407 TempoSection&
1408 TempoMap::first_tempo ()
1409 {
1410         TempoSection *t = 0;
1411
1412         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1413                 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1414                         if (!t->active()) {
1415                                 continue;
1416                         }
1417                         return *t;
1418                 }
1419         }
1420
1421         fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1422         abort(); /*NOTREACHED*/
1423         return *t;
1424 }
1425 void
1426 TempoMap::recompute_tempos (Metrics& metrics)
1427 {
1428         TempoSection* prev_ts = 0;
1429
1430         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1431                 TempoSection* t;
1432
1433                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1434                         if (!t->active()) {
1435                                 continue;
1436                         }
1437                         if (prev_ts) {
1438                                 if (t->position_lock_style() == AudioTime) {
1439                                         prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1440                                         t->set_pulse (prev_ts->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
1441
1442                                 } else {
1443                                         prev_ts->set_c_func (prev_ts->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1444                                         t->set_frame (prev_ts->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
1445
1446                                 }
1447                         }
1448                         prev_ts = t;
1449                 }
1450         }
1451         prev_ts->set_c_func (0.0);
1452 }
1453
1454 /* tempos must be positioned correctly */
1455 void
1456 TempoMap::recompute_meters (Metrics& metrics)
1457 {
1458         MeterSection* meter = 0;
1459         MeterSection* prev_m = 0;
1460         double accumulated_beats = 0.0;
1461         uint32_t accumulated_bars = 0;
1462
1463         for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1464                 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1465                         if (prev_m) {
1466                                 const double beats_in_m = (meter->pulse() - prev_m->pulse()) * prev_m->note_divisor();
1467                                 accumulated_beats += beats_in_m;
1468                                 accumulated_bars += (beats_in_m + 1) / prev_m->divisions_per_bar();
1469                         }
1470                         if (meter->position_lock_style() == AudioTime) {
1471                                 double pulse = 0.0;
1472                                 pair<double, BBT_Time> bt = make_pair (accumulated_beats, BBT_Time (accumulated_bars + 1, 1, 0));
1473                                 meter->set_beat (bt);
1474                                 if (prev_m) {
1475                                         pulse = prev_m->pulse() + (meter->beat() - prev_m->beat()) / prev_m->note_divisor();
1476                                 } else {
1477                                         if (meter->movable()) {
1478                                                 pulse = pulse_at_frame_locked (metrics, meter->frame());
1479                                         } else {
1480                                                 pulse = 0.0;
1481                                         }
1482                                 }
1483                                 meter->set_pulse (pulse);
1484                         } else {
1485                                 double pulse = 0.0;
1486                                 if (prev_m) {
1487                                         pulse = prev_m->pulse() + (meter->beat() - prev_m->beat()) / prev_m->note_divisor();
1488                                 } else {
1489                                         pulse = pulse_at_beat_locked (metrics, meter->beat());
1490                                 }
1491                                 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1492                                 meter->set_pulse (pulse);
1493                         }
1494
1495                         prev_m = meter;
1496                 }
1497         }
1498 }
1499
1500 void
1501 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1502 {
1503         /* CALLER MUST HOLD WRITE LOCK */
1504
1505         if (end < 0) {
1506
1507                 /* we will actually stop once we hit
1508                    the last metric.
1509                 */
1510                 end = max_framepos;
1511
1512         }
1513
1514         DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1515
1516         if (end == 0) {
1517                 /* silly call from Session::process() during startup
1518                  */
1519                 return;
1520         }
1521
1522         recompute_tempos (metrics);
1523         recompute_meters (metrics);
1524 }
1525
1526 double
1527 TempoMap::pulse_at_beat (const double& beat) const
1528 {
1529         Glib::Threads::RWLock::ReaderLock lm (lock);
1530         return pulse_at_beat_locked (_metrics, beat);
1531 }
1532
1533 double
1534 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1535 {
1536         MeterSection* prev_ms = 0;
1537         double accumulated_beats = 0.0;
1538
1539         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1540                 MeterSection* m;
1541                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1542                         if (prev_ms && m->beat() > beat) {
1543                                 break;
1544                         }
1545                         accumulated_beats = m->beat();
1546                         prev_ms = m;
1547                 }
1548
1549         }
1550         double const ret = prev_ms->pulse() + ((beat - accumulated_beats) / prev_ms->note_divisor());
1551         return ret;
1552 }
1553
1554 double
1555 TempoMap::beat_at_pulse (const double& pulse) const
1556 {
1557         Glib::Threads::RWLock::ReaderLock lm (lock);
1558         return beat_at_pulse_locked (_metrics, pulse);
1559 }
1560
1561 double
1562 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1563 {
1564         MeterSection* prev_ms = 0;
1565         double accumulated_beats = 0.0;
1566
1567         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1568                 MeterSection* m;
1569                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1570                         if (prev_ms && m->pulse() > pulse) {
1571                                 break;
1572                         }
1573                         accumulated_beats = m->beat();
1574                         prev_ms = m;
1575                 }
1576         }
1577
1578         double const beats_in_section = (pulse - prev_ms->pulse()) * prev_ms->note_divisor();
1579
1580         return beats_in_section + accumulated_beats;
1581 }
1582
1583 TempoMetric
1584 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1585 {
1586         Glib::Threads::RWLock::ReaderLock lm (lock);
1587         TempoMetric m (first_meter(), first_tempo());
1588
1589         /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1590            at something, because we insert the default tempo and meter during
1591            TempoMap construction.
1592
1593            now see if we can find better candidates.
1594         */
1595
1596         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1597
1598                 if ((*i)->frame() > frame) {
1599                         break;
1600                 }
1601
1602                 m.set_metric(*i);
1603
1604                 if (last) {
1605                         *last = i;
1606                 }
1607         }
1608
1609         return m;
1610 }
1611
1612 /* XX meters only */
1613 TempoMetric
1614 TempoMap::metric_at (BBT_Time bbt) const
1615 {
1616         Glib::Threads::RWLock::ReaderLock lm (lock);
1617         TempoMetric m (first_meter(), first_tempo());
1618
1619         /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1620            at something, because we insert the default tempo and meter during
1621            TempoMap construction.
1622
1623            now see if we can find better candidates.
1624         */
1625
1626         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1627                 MeterSection* mw;
1628                 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1629                         BBT_Time section_start (mw->bbt());
1630
1631                         if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1632                                 break;
1633                         }
1634
1635                         m.set_metric (*i);
1636                 }
1637         }
1638
1639         return m;
1640 }
1641
1642 void
1643 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1644 {
1645
1646         if (frame < 0) {
1647                 bbt.bars = 1;
1648                 bbt.beats = 1;
1649                 bbt.ticks = 0;
1650                 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1651                 return;
1652         }
1653         Glib::Threads::RWLock::ReaderLock lm (lock);
1654         double const beat = beat_at_frame_locked (_metrics, frame);
1655
1656         bbt = beats_to_bbt_locked (_metrics, beat);
1657 }
1658
1659 double
1660 TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
1661 {
1662         Glib::Threads::RWLock::ReaderLock lm (lock);
1663
1664         return bbt_to_beats_locked (_metrics, bbt);
1665 }
1666
1667 double
1668 TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1669 {
1670         /* CALLER HOLDS READ LOCK */
1671
1672         double accumulated_beats = 0.0;
1673         double accumulated_bars = 0.0;
1674         MeterSection* prev_ms = 0;
1675         /* because audio-locked meters have 'fake' integral beats,
1676            there is no pulse offset here.
1677         */
1678         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1679                 MeterSection* m;
1680                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1681                         double bars_to_m = 0.0;
1682                         if (prev_ms) {
1683                                 bars_to_m = (m->beat() - prev_ms->beat()) / prev_ms->divisions_per_bar();
1684                                 if ((bars_to_m + accumulated_bars) > (bbt.bars - 1)) {
1685                                         break;
1686                                 }
1687                                 accumulated_beats = m->beat();
1688                                 accumulated_bars += bars_to_m;
1689                         }
1690                         prev_ms = m;
1691                 }
1692         }
1693
1694         double const remaining_bars = (bbt.bars - 1) - accumulated_bars;
1695         double const remaining_bars_in_beats = remaining_bars * prev_ms->divisions_per_bar();
1696         double const ret = remaining_bars_in_beats + accumulated_beats + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1697
1698         return ret;
1699 }
1700
1701 Timecode::BBT_Time
1702 TempoMap::beats_to_bbt (const double& beats)
1703 {
1704         Glib::Threads::RWLock::ReaderLock lm (lock);
1705
1706         return beats_to_bbt_locked (_metrics, beats);
1707 }
1708
1709 Timecode::BBT_Time
1710 TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
1711 {
1712         /* CALLER HOLDS READ LOCK */
1713         MeterSection* prev_ms = 0;
1714         const double beats = (b < 0.0) ? 0.0 : b;
1715         uint32_t accumulated_bars = 0;
1716         double accumulated_beats = 0.0;
1717
1718         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1719                 MeterSection* m = 0;
1720
1721                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1722
1723                         if (prev_ms) {
1724                                 double const beats_to_m = m->beat() - prev_ms->beat();
1725                                 if (accumulated_beats + beats_to_m > beats) {
1726                                         /* this is the meter after the one our beat is on*/
1727                                         break;
1728                                 }
1729
1730                                 /* we need a whole number of bars. */
1731                                 accumulated_bars += (beats_to_m + 1) / prev_ms->divisions_per_bar();
1732                                 accumulated_beats += beats_to_m;
1733                         }
1734
1735                         prev_ms = m;
1736                 }
1737         }
1738
1739         double const beats_in_ms = beats - accumulated_beats;
1740         uint32_t const bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
1741         uint32_t const total_bars = bars_in_ms + accumulated_bars;
1742         double const remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
1743         double const remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1744
1745         BBT_Time ret;
1746
1747         ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1748         ret.beats = (uint32_t) floor (remaining_beats);
1749         ret.bars = total_bars;
1750
1751         /* 0 0 0 to 1 1 0 - based mapping*/
1752         ++ret.bars;
1753         ++ret.beats;
1754
1755         if (ret.ticks >= BBT_Time::ticks_per_beat) {
1756                 ++ret.beats;
1757                 ret.ticks -= BBT_Time::ticks_per_beat;
1758         }
1759
1760         if (ret.beats >= prev_ms->divisions_per_bar() + 1) {
1761                 ++ret.bars;
1762                 ret.beats = 1;
1763         }
1764
1765         return ret;
1766 }
1767
1768 Timecode::BBT_Time
1769 TempoMap::pulse_to_bbt (const double& pulse)
1770 {
1771         Glib::Threads::RWLock::ReaderLock lm (lock);
1772         MeterSection* prev_ms = 0;
1773         uint32_t accumulated_bars = 0;
1774         double accumulated_pulses = 0.0;
1775
1776         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1777                 MeterSection* m = 0;
1778
1779                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1780
1781                         if (prev_ms) {
1782                                 double const pulses_to_m = m->pulse() - prev_ms->pulse();
1783                                 if (accumulated_pulses + pulses_to_m > pulse) {
1784                                         /* this is the meter after the one our beat is on*/
1785                                         break;
1786                                 }
1787
1788                                 /* we need a whole number of bars. */
1789                                 accumulated_pulses += pulses_to_m;
1790                                 accumulated_bars += ((pulses_to_m * prev_ms->note_divisor()) + 1) / prev_ms->divisions_per_bar();
1791                         }
1792
1793                         prev_ms = m;
1794                 }
1795         }
1796         double const beats_in_ms = (pulse - prev_ms->pulse()) * prev_ms->note_divisor();
1797         uint32_t const bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
1798         uint32_t const total_bars = bars_in_ms + accumulated_bars;
1799         double const remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
1800         double const remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1801
1802         BBT_Time ret;
1803
1804         ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1805         ret.beats = (uint32_t) floor (remaining_beats);
1806         ret.bars = total_bars;
1807
1808         /* 0 0 0 to 1 1 0 mapping*/
1809         ++ret.bars;
1810         ++ret.beats;
1811
1812         if (ret.ticks >= BBT_Time::ticks_per_beat) {
1813                 ++ret.beats;
1814                 ret.ticks -= BBT_Time::ticks_per_beat;
1815         }
1816
1817         if (ret.beats >= prev_ms->divisions_per_bar() + 1) {
1818                 ++ret.bars;
1819                 ret.beats = 1;
1820         }
1821
1822         return ret;
1823 }
1824
1825 double
1826 TempoMap::beat_at_frame (const framecnt_t& frame) const
1827 {
1828         Glib::Threads::RWLock::ReaderLock lm (lock);
1829         return beat_at_frame_locked (_metrics, frame);
1830 }
1831
1832 double
1833 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1834 {
1835         framecnt_t const offset_frame = frame + frame_offset_at (metrics, frame);
1836         double const pulse = pulse_at_frame_locked (metrics, offset_frame);
1837
1838         return beat_at_pulse_locked (metrics, pulse);
1839 }
1840
1841 double
1842 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1843 {
1844         /* HOLD (at least) THE READER LOCK */
1845         TempoSection* prev_ts = 0;
1846         double accumulated_pulses = 0.0;
1847
1848         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1849                 TempoSection* t;
1850                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1851                         if (!t->active()) {
1852                                 continue;
1853                         }
1854                         if (prev_ts && t->frame() > frame) {
1855                                 /*the previous ts is the one containing the frame */
1856                                 double const ret = prev_ts->pulse_at_frame (frame, _frame_rate);
1857                                 return ret;
1858                         }
1859                         accumulated_pulses = t->pulse();
1860                         prev_ts = t;
1861                 }
1862         }
1863
1864         /* treated as constant for this ts */
1865         double const pulses_in_section = (frame - prev_ts->frame()) / prev_ts->frames_per_pulse (_frame_rate);
1866
1867         return pulses_in_section + accumulated_pulses;
1868 }
1869
1870 framecnt_t
1871 TempoMap::frame_at_beat (const double& beat) const
1872 {
1873         Glib::Threads::RWLock::ReaderLock lm (lock);
1874         return frame_at_beat_locked (_metrics, beat);
1875 }
1876
1877 framecnt_t
1878 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1879 {
1880         framecnt_t const frame = frame_at_pulse_locked (metrics, pulse_at_beat_locked (metrics, beat));
1881         frameoffset_t const frame_off = frame_offset_at (metrics, frame);
1882         return frame - frame_off;
1883 }
1884
1885 framecnt_t
1886 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1887 {
1888         /* HOLD THE READER LOCK */
1889
1890         const TempoSection* prev_ts = 0;
1891         double accumulated_pulses = 0.0;
1892
1893         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1894                 TempoSection* t;
1895
1896                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1897                         if (!t->active()) {
1898                                 continue;
1899                         }
1900                         if (prev_ts && t->pulse() > pulse) {
1901                                 return prev_ts->frame_at_pulse (pulse, _frame_rate);
1902                         }
1903
1904                         accumulated_pulses = t->pulse();
1905                         prev_ts = t;
1906                 }
1907         }
1908         /* must be treated as constant, irrespective of _type */
1909         double const pulses_in_section = pulse - accumulated_pulses;
1910         double const dtime = pulses_in_section * prev_ts->frames_per_pulse (_frame_rate);
1911
1912         framecnt_t const ret = (framecnt_t) floor (dtime) + prev_ts->frame();
1913
1914         return ret;
1915 }
1916
1917 double
1918 TempoMap::beat_offset_at (const Metrics& metrics, const double& beat) const
1919 {
1920         MeterSection* prev_m = 0;
1921         double beat_off = first_meter().pulse();
1922
1923         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1924                 MeterSection* m = 0;
1925                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1926                         if (prev_m) {
1927                                 if (m->beat() > beat) {
1928                                         break;
1929                                 }
1930
1931                                 if (m->position_lock_style() == AudioTime) {
1932                                         beat_off += ((m->beat() - prev_m->beat()) / prev_m->note_divisor()) - floor (m->pulse() - prev_m->pulse());
1933                                 }
1934                         }
1935                         prev_m = m;
1936                 }
1937         }
1938
1939         return beat_off;
1940 }
1941
1942 frameoffset_t
1943 TempoMap::frame_offset_at (const Metrics& metrics, const framepos_t& frame) const
1944 {
1945         frameoffset_t frame_off = 0;
1946         MeterSection* prev_m = 0;
1947
1948         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1949                 MeterSection* m = 0;
1950                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1951                         if (m->frame() > frame) {
1952                                 break;
1953                         }
1954                         if (prev_m && m->position_lock_style() == AudioTime) {
1955                                 const double pulse = prev_m->pulse() + ((m->beat() - prev_m->beat()) / prev_m->note_divisor());
1956                                 frame_off += frame_at_pulse_locked (metrics, pulse) - m->frame();
1957                         }
1958                         prev_m = m;
1959                 }
1960         }
1961
1962         return frame_off;
1963 }
1964
1965 framepos_t
1966 TempoMap::frame_time (const BBT_Time& bbt)
1967 {
1968         if (bbt.bars < 1) {
1969                 warning << string_compose (_("tempo map asked for frame time at bar < 1  (%1)\n"), bbt) << endmsg;
1970                 return 0;
1971         }
1972
1973         if (bbt.beats < 1) {
1974                 throw std::logic_error ("beats are counted from one");
1975         }
1976         Glib::Threads::RWLock::ReaderLock lm (lock);
1977         double const beat = bbt_to_beats_locked (_metrics, bbt);
1978         framecnt_t const frame = frame_at_beat_locked (_metrics, beat);
1979         return frame;
1980 }
1981
1982 framepos_t
1983 TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
1984 {
1985         /* HOLD THE READER LOCK */
1986
1987         framepos_t const ret = frame_at_pulse_locked (metrics, pulse_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt)));
1988
1989         return ret;
1990 }
1991
1992 bool
1993 TempoMap::check_solved (Metrics& metrics, bool by_frame)
1994 {
1995         TempoSection* prev_ts = 0;
1996
1997         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1998                 TempoSection* t;
1999                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2000                         if (!t->active()) {
2001                                 continue;
2002                         }
2003                         if (prev_ts) {
2004                                 if ((by_frame && t->frame() < prev_ts->frame()) || (!by_frame && t->pulse() < prev_ts->pulse())) {
2005                                         return false;
2006                                 }
2007                                 if (by_frame && t->frame() != prev_ts->frame_at_pulse (t->pulse(), _frame_rate)) {
2008                                         return false;
2009                                 }
2010                                 /*
2011                                 if (!by_frame && fabs (t->pulse() - prev_ts->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate)) > 0.00001) {
2012                                         std::cerr << "beat precision too low for bpm: " << t->beats_per_minute() << std::endl <<
2013                                                 " |error          :" << t->pulse() - prev_ts->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate) << std::endl <<
2014                                                 "|frame at beat   :" << prev_ts->frame_at_pulse (t->pulse(), _frame_rate) << std::endl <<
2015                                                 " |frame at tempo : " << prev_ts->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate) << std::endl;
2016                                         return false;
2017                                 }
2018                                 */
2019                         }
2020                         prev_ts = t;
2021                 }
2022         }
2023
2024         return true;
2025 }
2026
2027 bool
2028 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm, const framepos_t& frame)
2029 {
2030
2031         TempoSection* prev_ts = 0;
2032         TempoSection* section_prev = 0;
2033         MetricSectionFrameSorter fcmp;
2034         MetricSectionSorter cmp;
2035         framepos_t first_m_frame = 0;
2036         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2037                 MeterSection* m;
2038                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2039                         if (!m->movable()) {
2040                                 first_m_frame = m->frame();
2041                         }
2042                 }
2043         }
2044         if (section->movable() && frame <= first_m_frame) {
2045                 return false;
2046         } else {
2047                 section->set_active (true);
2048         }
2049         section->set_frame (frame);
2050
2051         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2052                 TempoSection* t;
2053                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2054
2055                         if (!t->active()) {
2056                                 continue;
2057                         }
2058                         if (prev_ts) {
2059                                 if (t == section) {
2060                                         section_prev = prev_ts;
2061                                         continue;
2062                                 }
2063                                 if (t->position_lock_style() == MusicTime) {
2064                                         prev_ts->set_c_func (prev_ts->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
2065                                         t->set_frame (prev_ts->frame_at_pulse (t->pulse(), _frame_rate));
2066                                 } else {
2067                                         prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
2068                                         t->set_pulse (prev_ts->pulse_at_frame (t->frame(), _frame_rate));
2069                                 }
2070                         }
2071                         prev_ts = t;
2072                 }
2073         }
2074
2075         if (section_prev) {
2076                 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
2077                 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
2078         }
2079
2080         if (section->position_lock_style() == MusicTime) {
2081                 /* we're setting the frame */
2082                 section->set_position_lock_style (AudioTime);
2083                 recompute_tempos (imaginary);
2084                 section->set_position_lock_style (MusicTime);
2085         } else {
2086                 recompute_tempos (imaginary);
2087         }
2088
2089         if (check_solved (imaginary, true)) {
2090                 recompute_meters (imaginary);
2091                 return true;
2092         }
2093
2094         imaginary.sort (fcmp);
2095         if (section->position_lock_style() == MusicTime) {
2096                 /* we're setting the frame */
2097                 section->set_position_lock_style (AudioTime);
2098                 recompute_tempos (imaginary);
2099                 section->set_position_lock_style (MusicTime);
2100         } else {
2101                 recompute_tempos (imaginary);
2102         }
2103         if (check_solved (imaginary, true)) {
2104                 recompute_meters (imaginary);
2105                 return true;
2106         }
2107
2108         imaginary.sort (cmp);
2109         if (section->position_lock_style() == MusicTime) {
2110                 /* we're setting the frame */
2111                 section->set_position_lock_style (AudioTime);
2112                 recompute_tempos (imaginary);
2113                 section->set_position_lock_style (MusicTime);
2114         } else {
2115                 recompute_tempos (imaginary);
2116         }
2117         if (check_solved (imaginary, true)) {
2118                 recompute_meters (imaginary);
2119                 return true;
2120         }
2121         //dump (imaginary, std::cerr);
2122
2123         return false;
2124 }
2125
2126 bool
2127 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const Tempo& bpm, const double& pulse)
2128 {
2129         MetricSectionSorter cmp;
2130         MetricSectionFrameSorter fcmp;
2131         TempoSection* prev_ts = 0;
2132         TempoSection* section_prev = 0;
2133
2134         section->set_pulse (pulse);
2135
2136         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2137                 TempoSection* t;
2138                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2139                         if (!t->active()) {
2140                                 continue;
2141                         }
2142                         if (prev_ts) {
2143                                 if (t == section) {
2144                                         section_prev = prev_ts;
2145                                         continue;
2146                                 }
2147                                 if (t->position_lock_style() == MusicTime) {
2148                                         prev_ts->set_c_func (prev_ts->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
2149                                         t->set_frame (prev_ts->frame_at_pulse (t->pulse(), _frame_rate));
2150                                 } else {
2151                                         prev_ts->set_c_func (prev_ts->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
2152                                         t->set_pulse (prev_ts->pulse_at_frame (t->frame(), _frame_rate));
2153                                 }
2154                         }
2155                         prev_ts = t;
2156                 }
2157         }
2158         if (section_prev) {
2159                 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
2160                 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
2161         }
2162
2163         if (section->position_lock_style() == AudioTime) {
2164                 /* we're setting the pulse */
2165                 section->set_position_lock_style (MusicTime);
2166                 recompute_tempos (imaginary);
2167                 section->set_position_lock_style (AudioTime);
2168         } else {
2169                 recompute_tempos (imaginary);
2170         }
2171         if (check_solved (imaginary, false)) {
2172                 recompute_meters (imaginary);
2173                 return true;
2174         }
2175
2176         imaginary.sort (cmp);
2177         if (section->position_lock_style() == AudioTime) {
2178                 /* we're setting the pulse */
2179                 section->set_position_lock_style (MusicTime);
2180                 recompute_tempos (imaginary);
2181                 section->set_position_lock_style (AudioTime);
2182         } else {
2183                 recompute_tempos (imaginary);
2184         }
2185
2186         if (check_solved (imaginary, false)) {
2187                 recompute_meters (imaginary);
2188                 return true;
2189         }
2190
2191         imaginary.sort (fcmp);
2192         if (section->position_lock_style() == AudioTime) {
2193                 /* we're setting the pulse */
2194                 section->set_position_lock_style (MusicTime);
2195                 recompute_tempos (imaginary);
2196                 section->set_position_lock_style (AudioTime);
2197         } else {
2198                 recompute_tempos (imaginary);
2199         }
2200
2201         if (check_solved (imaginary, false)) {
2202                 recompute_meters (imaginary);
2203                 return true;
2204         }
2205
2206         //dump (imaginary, std::cerr);
2207
2208         return false;
2209 }
2210
2211 void
2212 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const Meter& mt, const double& pulse)
2213 {
2214         MeterSection* prev_ms = 0;
2215         double accumulated_beats = 0.0;
2216         uint32_t accumulated_bars = 0;
2217
2218         section->set_pulse (pulse);
2219
2220         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2221                 MeterSection* m;
2222                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2223                         if (prev_ms) {
2224                                 double const beats_in_m = (m->pulse() - prev_ms->pulse()) * prev_ms->note_divisor();
2225                                 accumulated_beats += beats_in_m;
2226                                 accumulated_bars += (beats_in_m + 1) / prev_ms->divisions_per_bar();
2227                         }
2228                         if (m == section){
2229                                 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2230                                 pair<double, BBT_Time> b_bbt = make_pair (accumulated_beats, BBT_Time (accumulated_bars + 1, 1, 0));
2231                                 section->set_beat (b_bbt);
2232                                 prev_ms = section;
2233                                 continue;
2234                         }
2235                         if (prev_ms) {
2236                                 if (m->position_lock_style() == MusicTime) {
2237                                         const double pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
2238                                         m->set_frame (frame_at_pulse_locked (imaginary, pulse));
2239                                         m->set_pulse (pulse);
2240                                 } else {
2241                                         pair<double, BBT_Time> b_bbt = make_pair (accumulated_beats, BBT_Time (accumulated_bars + 1, 1, 0));
2242                                         m->set_beat (b_bbt);
2243                                         const double pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
2244                                         m->set_pulse (pulse);
2245                                 }
2246                         }
2247                         prev_ms = m;
2248                 }
2249         }
2250
2251         if (section->position_lock_style() == AudioTime) {
2252                 /* we're setting the pulse */
2253                 section->set_position_lock_style (MusicTime);
2254                 recompute_meters (imaginary);
2255                 section->set_position_lock_style (AudioTime);
2256         } else {
2257                 recompute_meters (imaginary);
2258         }
2259 }
2260
2261 void
2262 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const Meter& mt, const framepos_t& frame)
2263 {
2264         MeterSection* prev_ms = 0;
2265
2266         if (!section->movable()) {
2267                 TempoSection* first_t;
2268                 for (Metrics::const_iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2269                         TempoSection* t;
2270                         if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2271                                 if (!t->movable()) {
2272                                         t->set_active (true);
2273                                         first_t = t;
2274                                 }
2275                                 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
2276                                         t->set_active (false);
2277                                         t->set_pulse (0.0);
2278                                 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
2279                                         t->set_active (true);
2280                                 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
2281                                         return;
2282                                 }
2283                         }
2284                 }
2285
2286                 Metrics future_map;
2287                 TempoSection* new_section = copy_metrics_and_point (future_map, first_t);
2288
2289                 new_section->set_frame (frame);
2290                 new_section->set_pulse (0.0);
2291                 new_section->set_active (true);
2292
2293                 if (solve_map (future_map, new_section, Tempo (new_section->beats_per_minute(), new_section->note_type()), frame)) {
2294                         first_t->set_frame (frame);
2295                         first_t->set_pulse (0.0);
2296                         first_t->set_active (true);
2297                         solve_map (imaginary, first_t, Tempo (first_t->beats_per_minute(), first_t->note_type()), frame);
2298                 } else {
2299                         return;
2300                 }
2301         }
2302
2303         double accumulated_beats = 0.0;
2304         uint32_t accumulated_bars = 0;
2305
2306         section->set_frame (frame);
2307
2308         for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2309                 MeterSection* m;
2310                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2311                         if (prev_ms) {
2312                                 const double beats_in_m = (m->pulse() - prev_ms->pulse()) * prev_ms->note_divisor();
2313                                 accumulated_beats += beats_in_m;
2314                                 accumulated_bars += (beats_in_m + 1) / prev_ms->divisions_per_bar();
2315                         }
2316                         if (m == section){
2317                                 /*
2318                                   here we define the pulse for this frame.
2319                                   we're going to set it 'incorrectly' to the next integer and use this 'error'
2320                                   as an offset to the map as far as users of the public methods are concerned.
2321                                   (meters should go on absolute pulses to keep us sane)
2322                                 */
2323                                 pair<double, BBT_Time> b_bbt = make_pair (accumulated_beats, BBT_Time (accumulated_bars + 1, 1, 0));
2324                                 if (m->movable()) {
2325                                         m->set_pulse (pulse_at_frame_locked (imaginary, frame));
2326                                 } else {
2327                                         m->set_pulse (0.0);
2328                                 }
2329                                 m->set_beat (b_bbt);
2330                                 prev_ms = m;
2331                                 continue;
2332                         }
2333                         if (prev_ms) {
2334                                 if (m->position_lock_style() == MusicTime) {
2335                                         const double pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
2336                                         m->set_frame (frame_at_pulse_locked (imaginary, pulse));
2337                                         m->set_pulse (pulse);
2338                                 } else {
2339                                         pair<double, BBT_Time> b_bbt = make_pair (accumulated_beats, BBT_Time (accumulated_bars + 1, 1, 0));
2340                                         m->set_beat (b_bbt);
2341                                         const double pulse = prev_ms->pulse() + (m->beat() - prev_ms->beat()) / prev_ms->note_divisor();
2342                                         m->set_pulse (pulse);
2343                                 }
2344                         }
2345                         prev_ms = m;
2346                 }
2347         }
2348
2349         if (section->position_lock_style() == MusicTime) {
2350                 /* we're setting the frame */
2351                 section->set_position_lock_style (AudioTime);
2352                 recompute_meters (imaginary);
2353                 section->set_position_lock_style (MusicTime);
2354         } else {
2355                 recompute_meters (imaginary);
2356         }
2357         //dump (imaginary, std::cerr);
2358 }
2359
2360 framecnt_t
2361 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2362 {
2363         Glib::Threads::RWLock::ReaderLock lm (lock);
2364
2365         double const tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2366         double const bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2367         double const total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2368         framecnt_t const time_at_bbt = frame_at_beat_locked (_metrics, total_beats);
2369         framecnt_t const ret = time_at_bbt;
2370
2371         return ret;
2372 }
2373
2374 framepos_t
2375 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2376 {
2377         return round_to_type (fr, dir, Bar);
2378 }
2379
2380 framepos_t
2381 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2382 {
2383         return round_to_type (fr, dir, Beat);
2384 }
2385
2386 framepos_t
2387 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2388 {
2389         Glib::Threads::RWLock::ReaderLock lm (lock);
2390         uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2391         uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2392         uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2393
2394         ticks -= beats * BBT_Time::ticks_per_beat;
2395
2396         if (dir > 0) {
2397                 /* round to next (or same iff dir == RoundUpMaybe) */
2398
2399                 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2400
2401                 if (mod == 0 && dir == RoundUpMaybe) {
2402                         /* right on the subdivision, which is fine, so do nothing */
2403
2404                 } else if (mod == 0) {
2405                         /* right on the subdivision, so the difference is just the subdivision ticks */
2406                         ticks += ticks_one_subdivisions_worth;
2407
2408                 } else {
2409                         /* not on subdivision, compute distance to next subdivision */
2410
2411                         ticks += ticks_one_subdivisions_worth - mod;
2412                 }
2413
2414                 if (ticks >= BBT_Time::ticks_per_beat) {
2415                         ticks -= BBT_Time::ticks_per_beat;
2416                 }
2417         } else if (dir < 0) {
2418
2419                 /* round to previous (or same iff dir == RoundDownMaybe) */
2420
2421                 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2422
2423                 if (difference == 0 && dir == RoundDownAlways) {
2424                         /* right on the subdivision, but force-rounding down,
2425                            so the difference is just the subdivision ticks */
2426                         difference = ticks_one_subdivisions_worth;
2427                 }
2428
2429                 if (ticks < difference) {
2430                         ticks = BBT_Time::ticks_per_beat - ticks;
2431                 } else {
2432                         ticks -= difference;
2433                 }
2434
2435         } else {
2436                 /* round to nearest */
2437                 double rem;
2438
2439                 /* compute the distance to the previous and next subdivision */
2440
2441                 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2442
2443                         /* closer to the next subdivision, so shift forward */
2444
2445                         ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2446
2447                         DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2448
2449                         if (ticks > BBT_Time::ticks_per_beat) {
2450                                 ++beats;
2451                                 ticks -= BBT_Time::ticks_per_beat;
2452                                 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2453                         }
2454
2455                 } else if (rem > 0) {
2456
2457                         /* closer to previous subdivision, so shift backward */
2458
2459                         if (rem > ticks) {
2460                                 if (beats == 0) {
2461                                         /* can't go backwards past zero, so ... */
2462                                         return 0;
2463                                 }
2464                                 /* step back to previous beat */
2465                                 --beats;
2466                                 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2467                                 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2468                         } else {
2469                                 ticks = lrint (ticks - rem);
2470                                 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2471                         }
2472                 } else {
2473                         /* on the subdivision, do nothing */
2474                 }
2475         }
2476
2477         framepos_t const ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2478
2479         return ret_frame;
2480 }
2481
2482 void
2483 TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num)
2484 {
2485         if (sub_num == -1) {
2486                 const double bpb = meter_section_at (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2487                 if ((double) when.beats > bpb / 2.0) {
2488                         ++when.bars;
2489                 }
2490                 when.beats = 1;
2491                 when.ticks = 0;
2492                 return;
2493         } else if (sub_num == 0) {
2494                 const double bpb = meter_section_at (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2495                 if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
2496                         ++when.beats;
2497                         while ((double) when.beats > bpb) {
2498                                 ++when.bars;
2499                                 when.beats -= (uint32_t) floor (bpb);
2500                         }
2501                 }
2502                 when.ticks = 0;
2503                 return;
2504         }
2505         const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
2506         double rem;
2507         if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
2508                 /* closer to the next subdivision, so shift forward */
2509
2510                 when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
2511
2512                 if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
2513                         ++when.beats;
2514                         when.ticks -= Timecode::BBT_Time::ticks_per_beat;
2515                 }
2516
2517         } else if (rem > 0) {
2518                 /* closer to previous subdivision, so shift backward */
2519
2520                 if (rem > when.ticks) {
2521                         if (when.beats == 0) {
2522                                 /* can't go backwards past zero, so ... */
2523                         }
2524                         /* step back to previous beat */
2525                         --when.beats;
2526                         when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
2527                 } else {
2528                         when.ticks = when.ticks - rem;
2529                 }
2530         }
2531 }
2532
2533 framepos_t
2534 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
2535 {
2536         Glib::Threads::RWLock::ReaderLock lm (lock);
2537
2538         double const beat_at_framepos = beat_at_frame_locked (_metrics, frame);
2539         BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
2540
2541         switch (type) {
2542         case Bar:
2543                 if (dir < 0) {
2544                         /* find bar previous to 'frame' */
2545                         bbt.beats = 1;
2546                         bbt.ticks = 0;
2547                         return frame_time (bbt);
2548
2549                 } else if (dir > 0) {
2550                         /* find bar following 'frame' */
2551                         ++bbt.bars;
2552                         bbt.beats = 1;
2553                         bbt.ticks = 0;
2554                         return frame_time (bbt);
2555                 } else {
2556                         /* true rounding: find nearest bar */
2557                         framepos_t raw_ft = frame_time (bbt);
2558                         bbt.beats = 1;
2559                         bbt.ticks = 0;
2560                         framepos_t prev_ft = frame_time (bbt);
2561                         ++bbt.bars;
2562                         framepos_t next_ft = frame_time (bbt);
2563
2564                         if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) { 
2565                                 return next_ft;
2566                         } else {
2567                                 return prev_ft;
2568                         }
2569                 }
2570
2571                 break;
2572
2573         case Beat:
2574                 if (dir < 0) {
2575                         return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
2576                 } else if (dir > 0) {
2577                         return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
2578                 } else {
2579                         return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
2580                 }
2581                 break;
2582         }
2583
2584         return 0;
2585 }
2586
2587 void
2588 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2589                     framepos_t lower, framepos_t upper)
2590 {
2591         Glib::Threads::RWLock::ReaderLock lm (lock);
2592         int32_t const upper_beat = (int32_t) ceil (beat_at_frame_locked (_metrics, upper));
2593         int32_t cnt = floor (beat_at_frame_locked (_metrics, lower));
2594         /* although the map handles negative beats, bbt doesn't. */
2595         if (cnt < 0.0) {
2596                 cnt = 0.0;
2597         }
2598         while (cnt <= upper_beat) {
2599                 framecnt_t pos = frame_at_beat_locked (_metrics, cnt);
2600                 TempoSection const tempo = tempo_section_at_locked (pos);
2601                 MeterSection const meter = meter_section_at_locked (pos);
2602                 BBT_Time const bbt = beats_to_bbt (cnt);
2603
2604                 points.push_back (BBTPoint (meter, tempo_at_locked (pos), pos, bbt.bars, bbt.beats, tempo.get_c_func()));
2605                 ++cnt;
2606         }
2607 }
2608
2609 const TempoSection&
2610 TempoMap::tempo_section_at (framepos_t frame) const
2611 {
2612         Glib::Threads::RWLock::ReaderLock lm (lock);
2613         return tempo_section_at_locked (frame);
2614 }
2615
2616 const TempoSection&
2617 TempoMap::tempo_section_at_locked (framepos_t frame) const
2618 {
2619         Metrics::const_iterator i;
2620         TempoSection* prev = 0;
2621
2622         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2623                 TempoSection* t;
2624
2625                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2626                         if (!t->active()) {
2627                                 continue;
2628                         }
2629                         if (t->frame() > frame) {
2630                                 break;
2631                         }
2632
2633                         prev = t;
2634                 }
2635         }
2636
2637         if (prev == 0) {
2638                 fatal << endmsg;
2639                 abort(); /*NOTREACHED*/
2640         }
2641
2642         return *prev;
2643 }
2644
2645
2646 /* don't use this to calculate length (the tempo is only correct for this frame).
2647    do that stuff based on the beat_at_frame and frame_at_beat api
2648 */
2649 double
2650 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
2651 {
2652         Glib::Threads::RWLock::ReaderLock lm (lock);
2653
2654         const TempoSection* ts_at = &tempo_section_at_locked (frame);
2655         const TempoSection* ts_after = 0;
2656         Metrics::const_iterator i;
2657
2658         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2659                 TempoSection* t;
2660
2661                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2662                         if (!t->active()) {
2663                                 continue;
2664                         }
2665                         if ((*i)->frame() > frame) {
2666                                 ts_after = t;
2667                                 break;
2668                         }
2669                 }
2670         }
2671
2672         if (ts_after) {
2673                 return  (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
2674         }
2675         /* must be treated as constant tempo */
2676         return ts_at->frames_per_beat (_frame_rate);
2677 }
2678
2679 const Tempo
2680 TempoMap::tempo_at (const framepos_t& frame) const
2681 {
2682         Glib::Threads::RWLock::ReaderLock lm (lock);
2683         return tempo_at_locked (frame);
2684 }
2685
2686 const Tempo
2687 TempoMap::tempo_at_locked (const framepos_t& frame) const
2688 {
2689         TempoSection* prev_ts = 0;
2690
2691         Metrics::const_iterator i;
2692
2693         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2694                 TempoSection* t;
2695                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2696                         if (!t->active()) {
2697                                 continue;
2698                         }
2699                         if ((prev_ts) && t->frame() > frame) {
2700                                 /* t is the section past frame */
2701                                 double const ret = prev_ts->tempo_at_frame (frame, _frame_rate) * prev_ts->note_type();
2702                                 Tempo const ret_tempo (ret, prev_ts->note_type());
2703                                 return ret_tempo;
2704                         }
2705                         prev_ts = t;
2706                 }
2707         }
2708
2709         double const ret = prev_ts->beats_per_minute();
2710         Tempo const ret_tempo (ret, prev_ts->note_type ());
2711
2712         return ret_tempo;
2713 }
2714
2715 const MeterSection&
2716 TempoMap::meter_section_at (framepos_t frame) const
2717 {
2718         Glib::Threads::RWLock::ReaderLock lm (lock);
2719         return meter_section_at_locked (frame);
2720 }
2721
2722 const MeterSection&
2723 TempoMap::meter_section_at_locked (framepos_t frame) const
2724 {
2725         //framepos_t const frame_off = frame + frame_offset_at (_metrics, frame);
2726         Metrics::const_iterator i;
2727         MeterSection* prev = 0;
2728
2729         for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2730                 MeterSection* m;
2731
2732                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2733
2734                         if (prev && (*i)->frame() > frame) {
2735                                 break;
2736                         }
2737
2738                         prev = m;
2739                 }
2740         }
2741
2742         if (prev == 0) {
2743                 fatal << endmsg;
2744                 abort(); /*NOTREACHED*/
2745         }
2746
2747         return *prev;
2748 }
2749
2750 const Meter&
2751 TempoMap::meter_at (framepos_t frame) const
2752 {
2753         TempoMetric m (metric_at (frame));
2754         return m.meter();
2755 }
2756
2757 const MeterSection&
2758 TempoMap::meter_section_at (const double& beat) const
2759 {
2760         MeterSection* prev_ms = 0;
2761         Glib::Threads::RWLock::ReaderLock lm (lock);
2762
2763         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2764                 MeterSection* m;
2765                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2766                         if (prev_ms && m->beat() > beat) {
2767                                 break;
2768                         }
2769                         prev_ms = m;
2770                 }
2771
2772         }
2773         return *prev_ms;
2774 }
2775
2776 XMLNode&
2777 TempoMap::get_state ()
2778 {
2779         Metrics::const_iterator i;
2780         XMLNode *root = new XMLNode ("TempoMap");
2781
2782         {
2783                 Glib::Threads::RWLock::ReaderLock lm (lock);
2784                 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2785                         root->add_child_nocopy ((*i)->get_state());
2786                 }
2787         }
2788
2789         return *root;
2790 }
2791
2792 int
2793 TempoMap::set_state (const XMLNode& node, int /*version*/)
2794 {
2795         {
2796                 Glib::Threads::RWLock::WriterLock lm (lock);
2797
2798                 XMLNodeList nlist;
2799                 XMLNodeConstIterator niter;
2800                 Metrics old_metrics (_metrics);
2801                 MeterSection* last_meter = 0;
2802                 _metrics.clear();
2803
2804                 nlist = node.children();
2805
2806                 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2807                         XMLNode* child = *niter;
2808
2809                         if (child->name() == TempoSection::xml_state_node_name) {
2810
2811                                 try {
2812                                         TempoSection* ts = new TempoSection (*child);
2813                                         _metrics.push_back (ts);
2814
2815                                         if (ts->bar_offset() < 0.0) {
2816                                                 if (last_meter) {
2817                                                         //ts->update_bar_offset_from_bbt (*last_meter);
2818                                                 }
2819                                         }
2820                                 }
2821
2822                                 catch (failed_constructor& err){
2823                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2824                                         _metrics = old_metrics;
2825                                         break;
2826                                 }
2827
2828                         } else if (child->name() == MeterSection::xml_state_node_name) {
2829
2830                                 try {
2831                                         MeterSection* ms = new MeterSection (*child);
2832                                         _metrics.push_back (ms);
2833                                         last_meter = ms;
2834                                 }
2835
2836                                 catch (failed_constructor& err) {
2837                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2838                                         _metrics = old_metrics;
2839                                         break;
2840                                 }
2841                         }
2842                 }
2843
2844                 if (niter == nlist.end()) {
2845                         MetricSectionSorter cmp;
2846                         _metrics.sort (cmp);
2847                 }
2848                 /* check for legacy sessions where bbt was the base musical unit for tempo */
2849                 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2850                         MeterSection* m;
2851                         TempoSection* t;
2852                         MeterSection* prev_ms = 0;
2853                         TempoSection* prev_ts = 0;
2854                         if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
2855                                 if (prev_ms && prev_ms->pulse() < 0.0) {
2856                                         /*XX we cannot possibly make this work??. */
2857                                         pair<double, BBT_Time> start = make_pair (((prev_ms->bbt().bars - 1) * prev_ms->note_divisor()) + (prev_ms->bbt().beats - 1) + (prev_ms->bbt().ticks / BBT_Time::ticks_per_beat), prev_ms->bbt());
2858                                         prev_ms->set_beat (start);
2859                                         const double start_pulse = ((prev_ms->bbt().bars - 1) * prev_ms->note_divisor()) + (prev_ms->bbt().beats - 1) + (prev_ms->bbt().ticks / BBT_Time::ticks_per_beat);
2860                                         prev_ms->set_pulse (start_pulse);
2861                                 }
2862                                 prev_ms = m;
2863                         } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
2864                                 if (!t->active()) {
2865                                         continue;
2866                                 }
2867                                 if (prev_ts && prev_ts->pulse() < 0.0) {
2868                                         double const start = ((prev_ts->legacy_bbt().bars - 1) * prev_ms->note_divisor()) + (prev_ts->legacy_bbt().beats - 1) + (prev_ts->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
2869                                         prev_ts->set_pulse (start);
2870                                 }
2871                                 prev_ts = t;
2872                         }
2873                 }
2874                 /* check for multiple tempo/meters at the same location, which
2875                    ardour2 somehow allowed.
2876                 */
2877
2878                 Metrics::iterator prev = _metrics.end();
2879                 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2880                         if (prev != _metrics.end()) {
2881                                 MeterSection* ms;
2882                                 MeterSection* prev_ms;
2883                                 TempoSection* ts;
2884                                 TempoSection* prev_ts;
2885                                 if ((prev_ms = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2886                                         if (prev_ms->pulse() == ms->pulse()) {
2887                                                 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_ms->pulse()) << endmsg;
2888                                                 error << string_compose (_("Multiple meter definitions found at %1"), prev_ms->pulse()) << endmsg;
2889                                                 return -1;
2890                                         }
2891                                 } else if ((prev_ts = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2892                                         if (prev_ts->pulse() == ts->pulse()) {
2893                                                 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->pulse()) << endmsg;
2894                                                 error << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->pulse()) << endmsg;
2895                                                 return -1;
2896                                         }
2897                                 }
2898                         }
2899                         prev = i;
2900                 }
2901
2902                 recompute_map (_metrics);
2903         }
2904
2905         PropertyChanged (PropertyChange ());
2906
2907         return 0;
2908 }
2909
2910 void
2911 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
2912 {
2913         Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2914         const MeterSection* m;
2915         const TempoSection* t;
2916         const TempoSection* prev_ts = 0;
2917
2918         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2919
2920                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2921                         o << "Tempo @ " << *i << " (Bar-offset: " << t->bar_offset() << ") " << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
2922                           << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
2923                         o << "current      : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
2924                         if (prev_ts) {
2925                                 o << "previous     : " << prev_ts->beats_per_minute() << " | " << prev_ts->pulse() << " | " << prev_ts->frame() << std::endl;
2926                                 o << "calculated   : " << prev_ts->tempo_at_pulse (t->pulse()) *  prev_ts->note_type() << " | " << prev_ts->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate) <<  " | " << prev_ts->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate) << std::endl;
2927                         }
2928                         prev_ts = t;
2929                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2930                         o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
2931                           << " pulse: " << m->pulse() <<  " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
2932                 }
2933         }
2934         o << "------" << std::endl;
2935 }
2936
2937 int
2938 TempoMap::n_tempos() const
2939 {
2940         Glib::Threads::RWLock::ReaderLock lm (lock);
2941         int cnt = 0;
2942
2943         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2944                 if (dynamic_cast<const TempoSection*>(*i) != 0) {
2945                         cnt++;
2946                 }
2947         }
2948
2949         return cnt;
2950 }
2951
2952 int
2953 TempoMap::n_meters() const
2954 {
2955         Glib::Threads::RWLock::ReaderLock lm (lock);
2956         int cnt = 0;
2957
2958         for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2959                 if (dynamic_cast<const MeterSection*>(*i) != 0) {
2960                         cnt++;
2961                 }
2962         }
2963
2964         return cnt;
2965 }
2966
2967 void
2968 TempoMap::insert_time (framepos_t where, framecnt_t amount)
2969 {
2970         {
2971                 Glib::Threads::RWLock::WriterLock lm (lock);
2972                 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2973                         if ((*i)->frame() >= where && (*i)->movable ()) {
2974                                 (*i)->set_frame ((*i)->frame() + amount);
2975                         }
2976                 }
2977
2978                 /* now reset the BBT time of all metrics, based on their new
2979                  * audio time. This is the only place where we do this reverse
2980                  * timestamp.
2981                  */
2982
2983                 Metrics::iterator i;
2984                 const MeterSection* meter;
2985                 const TempoSection* tempo;
2986                 MeterSection *m;
2987                 TempoSection *t;
2988
2989                 meter = &first_meter ();
2990                 tempo = &first_tempo ();
2991
2992                 BBT_Time start;
2993                 BBT_Time end;
2994
2995                 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
2996
2997                 bool first = true;
2998                 MetricSection* prev = 0;
2999
3000                 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3001
3002                         BBT_Time bbt;
3003                         //TempoMetric metric (*meter, *tempo);
3004                         MeterSection* ms = const_cast<MeterSection*>(meter);
3005                         TempoSection* ts = const_cast<TempoSection*>(tempo);
3006                         if (prev) {
3007                                 if (ts){
3008                                         if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3009                                                 if (!t->active()) {
3010                                                         continue;
3011                                                 }
3012                                                 ts->set_pulse (t->pulse());
3013                                         }
3014                                         if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3015                                                 ts->set_pulse (m->pulse());
3016                                         }
3017                                         ts->set_frame (prev->frame());
3018
3019                                 }
3020                                 if (ms) {
3021                                         if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3022                                                 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3023                                                 ms->set_beat (start);
3024                                                 ms->set_pulse (m->pulse());
3025                                         }
3026                                         if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3027                                                 if (!t->active()) {
3028                                                         continue;
3029                                                 }
3030                                                 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3031                                                 pair<double, BBT_Time> start = make_pair (beat, beats_to_bbt_locked (_metrics, beat));
3032                                                 ms->set_beat (start);
3033                                                 ms->set_pulse (t->pulse());
3034                                         }
3035                                         ms->set_frame (prev->frame());
3036                                 }
3037
3038                         } else {
3039                                 // metric will be at frames=0 bbt=1|1|0 by default
3040                                 // which is correct for our purpose
3041                         }
3042
3043                         // cerr << bbt << endl;
3044
3045                         if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3046                                 if (!t->active()) {
3047                                         continue;
3048                                 }
3049                                 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3050                                 tempo = t;
3051                                 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3052                         } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3053                                 bbt_time (m->frame(), bbt);
3054
3055                                 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3056
3057                                 if (first) {
3058                                         first = false;
3059                                 } else {
3060
3061                                         if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3062                                                 /* round up to next beat */
3063                                                 bbt.beats += 1;
3064                                         }
3065
3066                                         bbt.ticks = 0;
3067
3068                                         if (bbt.beats != 1) {
3069                                                 /* round up to next bar */
3070                                                 bbt.bars += 1;
3071                                                 bbt.beats = 1;
3072                                         }
3073                                 }
3074                                 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3075                                 m->set_beat (start);
3076                                 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3077                                 meter = m;
3078                                 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3079                         } else {
3080                                 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3081                                 abort(); /*NOTREACHED*/
3082                         }
3083
3084                         prev = (*i);
3085                 }
3086
3087                 recompute_map (_metrics);
3088         }
3089
3090
3091         PropertyChanged (PropertyChange ());
3092 }
3093 bool
3094 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3095 {
3096         bool moved = false;
3097
3098         std::list<MetricSection*> metric_kill_list;
3099
3100         TempoSection* last_tempo = NULL;
3101         MeterSection* last_meter = NULL;
3102         bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3103         bool meter_after = false; // is there a meter marker likewise?
3104         {
3105                 Glib::Threads::RWLock::WriterLock lm (lock);
3106                 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3107                         if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3108                                 metric_kill_list.push_back(*i);
3109                                 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3110                                 if (lt)
3111                                         last_tempo = lt;
3112                                 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3113                                 if (lm)
3114                                         last_meter = lm;
3115                         }
3116                         else if ((*i)->frame() >= where) {
3117                                 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3118                                 (*i)->set_frame ((*i)->frame() - amount);
3119                                 if ((*i)->frame() == where) {
3120                                         // marker was immediately after end of range
3121                                         tempo_after = dynamic_cast<TempoSection*> (*i);
3122                                         meter_after = dynamic_cast<MeterSection*> (*i);
3123                                 }
3124                                 moved = true;
3125                         }
3126                 }
3127
3128                 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3129                 if (last_tempo && !tempo_after) {
3130                         metric_kill_list.remove(last_tempo);
3131                         last_tempo->set_frame(where);
3132                         moved = true;
3133                 }
3134                 if (last_meter && !meter_after) {
3135                         metric_kill_list.remove(last_meter);
3136                         last_meter->set_frame(where);
3137                         moved = true;
3138                 }
3139
3140                 //remove all the remaining metrics
3141                 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3142                         _metrics.remove(*i);
3143                         moved = true;
3144                 }
3145
3146                 if (moved) {
3147                         recompute_map (_metrics);
3148                 }
3149         }
3150         PropertyChanged (PropertyChange ());
3151         return moved;
3152 }
3153
3154 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3155  *  pos can be -ve, if required.
3156  */
3157 framepos_t
3158 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3159 {
3160         return frame_at_beat (beat_at_frame (pos) + beats.to_double());
3161 }
3162
3163 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3164 framepos_t
3165 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3166 {
3167         return frame_at_beat (beat_at_frame (pos) - beats.to_double());
3168 }
3169
3170 /** Add the BBT interval op to pos and return the result */
3171 framepos_t
3172 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3173 {
3174         Glib::Threads::RWLock::ReaderLock lm (lock);
3175
3176         BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3177         pos_bbt.ticks += op.ticks;
3178         if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3179                 ++pos_bbt.beats;
3180                 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3181         }
3182         pos_bbt.beats += op.beats;
3183         /* the meter in effect will start on the bar */
3184         double divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3185         while (pos_bbt.beats >= divisions_per_bar + 1) {
3186                 ++pos_bbt.bars;
3187                 divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3188                 pos_bbt.beats -= divisions_per_bar;
3189         }
3190         pos_bbt.bars += op.bars;
3191
3192         return frame_time_locked (_metrics, pos_bbt);
3193 }
3194
3195 /** Count the number of beats that are equivalent to distance when going forward,
3196     starting at pos.
3197 */
3198 Evoral::Beats
3199 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3200 {
3201         return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
3202 }
3203
3204 struct bbtcmp {
3205     bool operator() (const BBT_Time& a, const BBT_Time& b) {
3206             return a < b;
3207     }
3208 };
3209
3210 std::ostream&
3211 operator<< (std::ostream& o, const Meter& m) {
3212         return o << m.divisions_per_bar() << '/' << m.note_divisor();
3213 }
3214
3215 std::ostream&
3216 operator<< (std::ostream& o, const Tempo& t) {
3217         return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3218 }
3219
3220 std::ostream&
3221 operator<< (std::ostream& o, const MetricSection& section) {
3222
3223         o << "MetricSection @ " << section.frame() << ' ';
3224
3225         const TempoSection* ts;
3226         const MeterSection* ms;
3227
3228         if ((ts = dynamic_cast<const TempoSection*> (&section)) != 0) {
3229                 o << *((const Tempo*) ts);
3230         } else if ((ms = dynamic_cast<const MeterSection*> (&section)) != 0) {
3231                 //o << *((const Meter*) ms);
3232         }
3233
3234         return o;
3235 }