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