Tempo ramps - demystify comments a bit.
[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
74 const string TempoSection::xml_state_node_name = "Tempo";
75
76 TempoSection::TempoSection (const XMLNode& node)
77         : MetricSection (0.0), Tempo (TempoMap::default_tempo())
78 {
79         const XMLProperty *prop;
80         LocaleGuard lg;
81         BBT_Time bbt;
82         double beat;
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         } else {
106                 error << _("TempoSection XML node has no \"beat\" property") << endmsg;
107         }
108
109         if ((prop = node.property ("beats-per-minute")) == 0) {
110                 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
111                 throw failed_constructor();
112         }
113
114         if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
115                 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
116                 throw failed_constructor();
117         }
118
119         if ((prop = node.property ("note-type")) == 0) {
120                 /* older session, make note type be quarter by default */
121                 _note_type = 4.0;
122         } else {
123                 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
124                         error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
125                         throw failed_constructor();
126                 }
127         }
128
129         if ((prop = node.property ("movable")) == 0) {
130                 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
131                 throw failed_constructor();
132         }
133
134         set_movable (string_is_affirmative (prop->value()));
135
136         if ((prop = node.property ("bar-offset")) == 0) {
137                 _bar_offset = -1.0;
138         } else {
139                 if (sscanf (prop->value().c_str(), "%lf", &_bar_offset) != 1 || _bar_offset < 0.0) {
140                         error << _("TempoSection XML node has an illegal \"bar-offset\" value") << endmsg;
141                         throw failed_constructor();
142                 }
143         }
144
145         if ((prop = node.property ("tempo-type")) == 0) {
146                 _type = Constant;
147         } else {
148                 _type = Type (string_2_enum (prop->value(), _type));
149         }
150 }
151
152 XMLNode&
153 TempoSection::get_state() const
154 {
155         XMLNode *root = new XMLNode (xml_state_node_name);
156         char buf[256];
157         LocaleGuard lg;
158
159         snprintf (buf, sizeof (buf), "%f", beat());
160         root->add_property ("beat", buf);
161         snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
162         root->add_property ("beats-per-minute", buf);
163         snprintf (buf, sizeof (buf), "%f", _note_type);
164         root->add_property ("note-type", buf);
165         // snprintf (buf, sizeof (buf), "%f", _bar_offset);
166         // root->add_property ("bar-offset", buf);
167         snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
168         root->add_property ("movable", buf);
169         root->add_property ("tempo-type", enum_2_string (_type));
170
171         return *root;
172 }
173
174 void
175
176 TempoSection::update_bar_offset_from_bbt (const Meter& m)
177 {
178         _bar_offset = (beat() * BBT_Time::ticks_per_beat) /
179                 (m.divisions_per_bar() * BBT_Time::ticks_per_beat);
180
181         DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Tempo set bar offset to %1 from %2 w/%3\n", _bar_offset, beat(), m.divisions_per_bar()));
182 }
183
184 void
185 TempoSection::set_type (Type type)
186 {
187         _type = type;
188 }
189
190 /** returns the tempo at the zero-based (relative to tempo section) frame.
191 */
192 double
193 TempoSection::tempo_at_frame (framepos_t frame, framecnt_t frame_rate) const
194 {
195
196         if (_type == Constant) {
197                 return beats_per_minute();
198         }
199
200         return tick_tempo_at_time (frame_to_minute (frame, frame_rate)) / BBT_Time::ticks_per_beat;
201 }
202
203 /** returns the zero-based frame (relative to tempo section)
204    where the tempo occurs.
205 */
206 framepos_t
207 TempoSection::frame_at_tempo (double bpm, framecnt_t frame_rate) const
208 {
209         if (_type == Constant) {
210                 return 0;
211         }
212
213         return minute_to_frame (time_at_tick_tempo (bpm *  BBT_Time::ticks_per_beat), frame_rate);
214 }
215
216 /** returns the zero-based tick (relative to tempo section)
217    where the zero-based frame (relative to tempo section)
218    lies.
219 */
220 double
221 TempoSection::tick_at_frame (framepos_t frame, framecnt_t frame_rate) const
222 {
223         if (_type == Constant) {
224                 return (frame / frames_per_beat (frame_rate)) * BBT_Time::ticks_per_beat;
225         }
226
227         return tick_at_time (frame_to_minute (frame, frame_rate));
228 }
229
230 /** returns the zero-based frame (relative to tempo section)
231    where the zero-based tick (relative to tempo section)
232    falls.
233 */
234 framepos_t
235 TempoSection::frame_at_tick (double tick, framecnt_t frame_rate) const
236 {
237         if (_type == Constant) {
238                 return (framepos_t) floor ((tick  / BBT_Time::ticks_per_beat) * frames_per_beat (frame_rate));
239         }
240
241         return minute_to_frame (time_at_tick (tick), frame_rate);
242 }
243
244 /** returns the zero-based beat (relative to tempo section)
245    where the zero-based frame (relative to tempo section)
246    lies.
247 */
248 double
249 TempoSection::beat_at_frame (framepos_t frame, framecnt_t frame_rate) const
250 {
251         return tick_at_frame (frame, frame_rate) / BBT_Time::ticks_per_beat;
252 }
253
254 /** returns the zero-based frame (relative to tempo section start frame)
255    where the zero-based beat (relative to tempo section start)
256    falls.
257 */
258
259 framepos_t
260 TempoSection::frame_at_beat (double beat, framecnt_t frame_rate) const
261 {
262         return frame_at_tick (beat * BBT_Time::ticks_per_beat, frame_rate);
263 }
264
265 /*
266 Ramp Overview
267
268       |                     *
269 Tempo |                   *
270 Tt----|-----------------*|
271 Ta----|--------------|*  |
272       |            * |   |
273       |         *    |   |
274       |     *        |   |
275 T0----|*             |   |
276   *   |              |   |
277       _______________|___|____
278       time           a   t (next tempo)
279       [        c         ] defines c
280
281 Duration in beats at time a is the integral of some Tempo function.
282 In our case, the Tempo function (Tempo at time t) is
283 T(t) = T0(e^(ct))
284
285 where c is the function constant
286 c = log(Ta/T0)/a
287 so
288 a = log(Ta/T0)/c
289
290 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
291 b(t) = T0(e^(ct) - 1) / c
292
293 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:
294 t(b) = log((cb / T0) + 1) / c
295
296 The time t at which Tempo T occurs is a as above:
297 t(T) = log(T / T0) / c
298
299 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
300 Our problem is that we usually don't know t.
301 We usually do know the duration in beats between this and the next tempo section.
302 Where t = a (i.e. when a is equal to the time of the next tempo section), we can solve t in terms of
303 beat duration and our two tempos.
304 A bit of scribbling with the beat function gives us:
305 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
306
307 By substituting our expanded t as a in the c function above, we see that our problem is reduced to:
308 c = T0 (e^(log (Ta / T0)) - 1) / b
309
310 We can now evaluate and store c for use in beat, time and tempo calculations until the following tempo section
311 (the one that defines c in conjunction with this one) is changed or moved.
312
313 Most of this stuff is taken from this paper:
314
315 WHERE’S THE BEAT?
316 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
317 Jan C. Schacher
318 Martin Neukom
319 Zurich University of Arts
320 Institute for Computer Music and Sound Technology
321
322 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
323
324 */
325
326 /* set this ramp's function constant using the end tempo and duration in beats of some later tempo section*/
327 void
328 TempoSection::set_c_func_from_tempo_and_beat (double end_bpm, double end_beat, framecnt_t frame_rate)
329 {
330         double const log_tempo_ratio = log ((end_bpm * BBT_Time::ticks_per_beat) / ticks_per_minute());
331         _c_func = ticks_per_minute() *  (exp (log_tempo_ratio) - 1) / (end_beat * BBT_Time::ticks_per_beat);
332 }
333
334 /* compute the function constant from some later tempo section, given tempo (beats/min.) and distance (in frames) from this tempo section */
335 double
336 TempoSection::compute_c_func (double end_bpm, framepos_t end_frame, framecnt_t frame_rate) const
337 {
338         return c_func (end_bpm * BBT_Time::ticks_per_beat, frame_to_minute (end_frame, frame_rate));
339 }
340
341 framecnt_t
342 TempoSection::minute_to_frame (double time, framecnt_t frame_rate) const
343 {
344         return (framecnt_t) floor ((time * 60.0 * frame_rate) + 0.5);
345 }
346
347 double
348 TempoSection::frame_to_minute (framecnt_t frame, framecnt_t frame_rate) const
349 {
350         return (frame / (double) frame_rate) / 60.0;
351 }
352
353 /* position function */
354 double
355 TempoSection::a_func (double end_tpm, double c_func) const
356 {
357         return log (end_tpm / ticks_per_minute()) /  c_func;
358 }
359
360 /*function constant*/
361 double
362 TempoSection::c_func (double end_tpm, double end_time) const
363 {
364         return log (end_tpm / ticks_per_minute()) /  end_time;
365 }
366
367 /* tempo in tpm at time in minutes */
368 double
369 TempoSection::tick_tempo_at_time (double time) const
370 {
371         return exp (_c_func * time) * ticks_per_minute();
372 }
373
374 /* time in minutes at tempo in tpm */
375 double
376 TempoSection::time_at_tick_tempo (double tick_tempo) const
377 {
378         return log (tick_tempo / ticks_per_minute()) / _c_func;
379 }
380
381 /* tick at time in minutes */
382 double
383 TempoSection::tick_at_time (double time) const
384 {
385         return ((exp (_c_func * time)) - 1) * ticks_per_minute() / _c_func;
386 }
387
388 /* time in minutes at tick */
389 double
390 TempoSection::time_at_tick (double tick) const
391 {
392         return log (((_c_func * tick) / ticks_per_minute()) + 1) / _c_func;
393 }
394
395 /* beat at time in minutes */
396 double
397 TempoSection::beat_at_time (double time) const
398 {
399         return tick_at_time (time) / BBT_Time::ticks_per_beat;
400 }
401
402 /* time in munutes at beat */
403 double
404 TempoSection::time_at_beat (double beat) const
405 {
406         return time_at_tick (beat * BBT_Time::ticks_per_beat);
407 }
408
409 void
410 TempoSection::update_bbt_time_from_bar_offset (const Meter& meter)
411 {
412         double new_beat;
413
414         if (_bar_offset < 0.0) {
415                 /* not set yet */
416                 return;
417         }
418
419         new_beat = beat();
420
421         double ticks = BBT_Time::ticks_per_beat * meter.divisions_per_bar() * _bar_offset;
422         new_beat = ticks / BBT_Time::ticks_per_beat;
423
424         DEBUG_TRACE (DEBUG::TempoMath, string_compose ("from bar offset %1 and dpb %2, ticks = %3->%4 beats = %5\n",
425                                                        _bar_offset, meter.divisions_per_bar(), ticks, new_beat, new_beat));
426
427         set_beat (new_beat);
428 }
429
430 /***********************************************************************/
431
432 const string MeterSection::xml_state_node_name = "Meter";
433
434 MeterSection::MeterSection (const XMLNode& node)
435         : MetricSection (0.0), Meter (TempoMap::default_meter())
436 {
437         XMLProperty const * prop;
438         BBT_Time start;
439         LocaleGuard lg;
440         const XMLProperty *prop;
441         BBT_Time bbt;
442         double beat = 0.0;
443         pair<double, BBT_Time> start;
444
445         if ((prop = node.property ("start")) != 0) {
446                 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
447                     &bbt.bars,
448                     &bbt.beats,
449                     &bbt.ticks) < 3) {
450                         error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
451                 } else {
452                         /* legacy session - start used to be in bbt*/
453                         beat = -1.0;
454                 }
455         } else {
456                 error << _("MeterSection XML node has no \"start\" property") << endmsg;
457         }
458
459         if ((prop = node.property ("beat")) != 0) {
460                 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1 || beat < 0.0) {
461                         error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
462                 }
463         } else {
464                 error << _("MeterSection XML node has no \"beat\" property") << endmsg;
465         }
466
467         start.first = beat;
468
469         if ((prop = node.property ("bbt")) == 0) {
470                 error << _("MeterSection XML node has no \"bbt\" property") << endmsg;
471         } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
472                     &bbt.bars,
473                     &bbt.beats,
474                     &bbt.ticks) < 3) {
475                 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
476                 throw failed_constructor();
477         }
478
479         start.second = bbt;
480
481         set_beat (start);
482
483         /* beats-per-bar is old; divisions-per-bar is new */
484
485         if ((prop = node.property ("divisions-per-bar")) == 0) {
486                 if ((prop = node.property ("beats-per-bar")) == 0) {
487                         error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
488                         throw failed_constructor();
489                 }
490         }
491
492         if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
493                 error << _("MeterSection XML node has an illegal \"beats-per-bar\" or \"divisions-per-bar\" value") << endmsg;
494                 throw failed_constructor();
495         }
496
497         if ((prop = node.property ("note-type")) == 0) {
498                 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
499                 throw failed_constructor();
500         }
501
502         if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
503                 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
504                 throw failed_constructor();
505         }
506
507         if ((prop = node.property ("movable")) == 0) {
508                 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
509                 throw failed_constructor();
510         }
511
512         set_movable (string_is_affirmative (prop->value()));
513 }
514
515 XMLNode&
516 MeterSection::get_state() const
517 {
518         XMLNode *root = new XMLNode (xml_state_node_name);
519         char buf[256];
520         LocaleGuard lg;
521
522         snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
523                   bbt().bars,
524                   bbt().beats,
525                   bbt().ticks);
526         root->add_property ("bbt", buf);
527         snprintf (buf, sizeof (buf), "%lf", beat());
528         root->add_property ("beat", buf);
529         snprintf (buf, sizeof (buf), "%f", _note_type);
530         root->add_property ("note-type", buf);
531         snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
532         root->add_property ("divisions-per-bar", buf);
533         snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
534         root->add_property ("movable", buf);
535
536         return *root;
537 }
538
539 /***********************************************************************/
540
541 struct MetricSectionSorter {
542     bool operator() (const MetricSection* a, const MetricSection* b) {
543             return a->beat() < b->beat();
544     }
545 };
546
547 struct MetricSectionFrameSorter {
548     bool operator() (const MetricSection* a, const MetricSection* b) {
549             return a->frame() < b->frame();
550     }
551 };
552
553 TempoMap::TempoMap (framecnt_t fr)
554 {
555         _frame_rate = fr;
556         BBT_Time start;
557
558         start.bars = 1;
559         start.beats = 1;
560         start.ticks = 0;
561
562         TempoSection *t = new TempoSection (0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Constant);
563         MeterSection *m = new MeterSection (0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
564
565         t->set_movable (false);
566         m->set_movable (false);
567
568         /* note: frame time is correct (zero) for both of these */
569
570         metrics.push_back (t);
571         metrics.push_back (m);
572
573 }
574
575 TempoMap::~TempoMap ()
576 {
577 }
578
579 void
580 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
581 {
582         bool removed = false;
583
584         {
585                 Glib::Threads::RWLock::WriterLock lm (lock);
586                 if ((removed = remove_tempo_locked (tempo))) {
587                         if (complete_operation) {
588                                 recompute_map (true);
589                         }
590                 }
591         }
592
593         if (removed && complete_operation) {
594                 PropertyChanged (PropertyChange ());
595         }
596 }
597
598 bool
599 TempoMap::remove_tempo_locked (const TempoSection& tempo)
600 {
601         Metrics::iterator i;
602
603         for (i = metrics.begin(); i != metrics.end(); ++i) {
604                 if (dynamic_cast<TempoSection*> (*i) != 0) {
605                         if (tempo.frame() == (*i)->frame()) {
606                                 if ((*i)->movable()) {
607                                         metrics.erase (i);
608                                         return true;
609                                 }
610                         }
611                 }
612         }
613
614         return false;
615 }
616
617 void
618 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
619 {
620         bool removed = false;
621
622         {
623                 Glib::Threads::RWLock::WriterLock lm (lock);
624                 if ((removed = remove_meter_locked (tempo))) {
625                         if (complete_operation) {
626                                 recompute_map (true);
627                         }
628                 }
629         }
630
631         if (removed && complete_operation) {
632                 PropertyChanged (PropertyChange ());
633         }
634 }
635
636 bool
637 TempoMap::remove_meter_locked (const MeterSection& tempo)
638 {
639         Metrics::iterator i;
640
641         for (i = metrics.begin(); i != metrics.end(); ++i) {
642                 if (dynamic_cast<MeterSection*> (*i) != 0) {
643                         if (tempo.frame() == (*i)->frame()) {
644                                 if ((*i)->movable()) {
645                                         metrics.erase (i);
646                                         return true;
647                                 }
648                         }
649                 }
650         }
651
652         return false;
653 }
654
655 void
656 TempoMap::do_insert (MetricSection* section)
657 {
658         bool need_add = true;
659
660         /* we only allow new meters to be inserted on beat 1 of an existing
661          * measure.
662          */
663         MeterSection* m = 0;
664         if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
665                 assert (m->bbt().ticks == 0);
666
667                 /* we need to (potentially) update the BBT times of tempo
668                    sections based on this new meter.
669                 */
670
671                 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
672
673                         pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
674                         corrected.second.beats = 1;
675                         corrected.second.ticks = 0;
676                         corrected.first = bbt_to_beats_locked (corrected.second);
677                         warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
678                                                    m->bbt(), corrected.second) << endmsg;
679                         m->set_beat (corrected);
680                 }
681         }
682
683
684
685         /* Look for any existing MetricSection that is of the same type and
686            in the same bar as the new one, and remove it before adding
687            the new one. Note that this means that if we find a matching,
688            existing section, we can break out of the loop since we're
689            guaranteed that there is only one such match.
690         */
691
692         for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
693
694                 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
695                 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
696
697                 if (tempo && insert_tempo) {
698
699                         /* Tempo sections */
700
701                         if (tempo->beat() == insert_tempo->beat()) {
702
703                                 if (!tempo->movable()) {
704
705                                         /* can't (re)move this section, so overwrite
706                                          * its data content (but not its properties as
707                                          * a section).
708                                          */
709
710                                         *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
711                                         need_add = false;
712                                 } else {
713                                         metrics.erase (i);
714                                 }
715                                 break;
716                         }
717
718                 } else if (!tempo && !insert_tempo) {
719
720                         /* Meter Sections */
721                         MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
722                         MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
723                         if (meter->beat() == insert_meter->beat()) {
724
725                                 if (!meter->movable()) {
726
727                                         /* can't (re)move this section, so overwrite
728                                          * its data content (but not its properties as
729                                          * a section
730                                          */
731
732                                         *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
733                                         need_add = false;
734                                 } else {
735                                         metrics.erase (i);
736
737                                 }
738
739                                 break;
740                         }
741                 } else {
742                         /* non-matching types, so we don't care */
743                 }
744         }
745
746         /* Add the given MetricSection, if we didn't just reset an existing
747          * one above
748          */
749
750         if (need_add) {
751                 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
752                 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
753
754                 Metrics::iterator i;
755                 if (insert_meter) {
756                         for (i = metrics.begin(); i != metrics.end(); ++i) {
757                                 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
758
759                                 if (meter && meter->beat() > insert_meter->beat()) {
760                                         break;
761                                 }
762                         }
763                 } else if (insert_tempo) {
764                         for (i = metrics.begin(); i != metrics.end(); ++i) {
765                                 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
766
767                                 if (tempo) {
768                                         if (tempo->beat() > insert_tempo->beat()) {
769                                                 break;
770                                         }
771                                 }
772                         }
773                 }
774
775                 metrics.insert (i, section);
776         }
777 }
778
779 void
780 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& where, TempoSection::Type type)
781 {
782         {
783                 Glib::Threads::RWLock::WriterLock lm (lock);
784                 TempoSection& first (first_tempo());
785                 if (ts.beat() != first.beat()) {
786                         remove_tempo_locked (ts);
787                         add_tempo_locked (tempo, where, true, type);
788                 } else {
789                         first.set_type (type);
790                         {
791                                 /* cannot move the first tempo section */
792                                 *static_cast<Tempo*>(&first) = tempo;
793                                 recompute_map (false);
794                         }
795                 }
796         }
797
798         PropertyChanged (PropertyChange ());
799 }
800
801 void
802 TempoMap::gui_set_tempo_frame (TempoSection& ts, framepos_t frame, double  beat_where)
803 {
804         {
805                 Glib::Threads::RWLock::WriterLock lm (lock);
806
807                 /* currently this is always done in audio time */
808                 //if (ts.position_lock_style() == MusicTime) {
809                 if (0) {
810                         /* MusicTime */
811                         ts.set_beat (beat_where);
812                         MetricSectionSorter cmp;
813                         metrics.sort (cmp);
814                 } else {
815                         /*AudioTime*/
816                         ts.set_frame (frame);
817                         MetricSectionFrameSorter fcmp;
818                         metrics.sort (fcmp);
819
820                         Metrics::const_iterator i;
821                         TempoSection* prev_ts = 0;
822                         TempoSection* next_ts = 0;
823
824                         for (i = metrics.begin(); i != metrics.end(); ++i) {
825                                 TempoSection* t;
826                                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
827
828                                         if (t->frame() >= frame) {
829                                                 break;
830                                         }
831
832                                         prev_ts = t;
833                                 }
834                         }
835
836                         for (i = metrics.begin(); i != metrics.end(); ++i) {
837                                 TempoSection* t;
838                                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
839
840                                         if (t->frame() > frame) {
841                                                 next_ts = t;
842                                                 break;
843                                         }
844                                 }
845                         }
846
847                         if (prev_ts) {
848                                 /* set the start beat - we need to reset the function constant before beat calculations make sense*/
849                                 prev_ts->set_c_func (prev_ts->compute_c_func (ts.beats_per_minute(), frame - prev_ts->frame(), _frame_rate));
850
851                                 double beats_to_ts = prev_ts->beat_at_frame (frame - prev_ts->frame(), _frame_rate);
852                                 double beats = beats_to_ts + prev_ts->beat();
853
854                                 if (next_ts) {
855                                         if (next_ts->beat() < beats) {
856                                                 /* with frame-based editing, it is possible to get in a
857                                                    situation where if the tempo was placed at the mouse pointer frame,
858                                                    the following music-based tempo would jump to an earlier frame,
859                                                    changing the beat beat of the moved tempo.
860                                                    in this case, we have to do some beat-based comparison TODO
861                                                 */
862                                         } else if (prev_ts->beat() > beats) {
863                                                 ts.set_beat (prev_ts->beat());
864                                         } else {
865                                                 ts.set_beat (beats);
866                                         }
867                                 } else {
868                                         ts.set_beat (beats);
869                                         ts.set_c_func (0.0);
870
871                                 }
872                                 MetricSectionSorter cmp;
873                                 metrics.sort (cmp);
874                         }
875                 }
876
877                 recompute_map (false);
878         }
879
880         MetricPositionChanged (); // Emit Signal
881 }
882
883 void
884 TempoMap::add_tempo (const Tempo& tempo, double where, ARDOUR::TempoSection::Type type)
885 {
886         {
887                 Glib::Threads::RWLock::WriterLock lm (lock);
888                 add_tempo_locked (tempo, where, true, type);
889         }
890
891
892         PropertyChanged (PropertyChange ());
893 }
894
895 void
896 TempoMap::add_tempo_locked (const Tempo& tempo, double where, bool recompute, ARDOUR::TempoSection::Type type)
897 {
898         TempoSection* ts = new TempoSection (where, tempo.beats_per_minute(), tempo.note_type(), type);
899
900         do_insert (ts);
901
902         if (recompute) {
903                 recompute_map (false);
904         }
905 }
906
907 void
908 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
909 {
910         {
911                 Glib::Threads::RWLock::WriterLock lm (lock);
912                 MeterSection& first (first_meter());
913                 if (ms.beat() != first.beat()) {
914                         remove_meter_locked (ms);
915                         add_meter_locked (meter, bbt_to_beats_locked (where), where, true);
916                 } else {
917                         /* cannot move the first meter section */
918                         *static_cast<Meter*>(&first) = meter;
919                         recompute_map (true);
920                 }
921         }
922
923         PropertyChanged (PropertyChange ());
924 }
925
926 void
927 TempoMap::add_meter (const Meter& meter, double beat, BBT_Time where)
928 {
929         {
930                 Glib::Threads::RWLock::WriterLock lm (lock);
931                 add_meter_locked (meter, beat, where, true);
932         }
933
934
935 #ifndef NDEBUG
936         if (DEBUG_ENABLED(DEBUG::TempoMap)) {
937                 dump (std::cerr);
938         }
939 #endif
940
941         PropertyChanged (PropertyChange ());
942 }
943
944 void
945 TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, bool recompute)
946 {
947         /* a new meter always starts a new bar on the first beat. so
948            round the start time appropriately. remember that
949            `where' is based on the existing tempo map, not
950            the result after we insert the new meter.
951
952         */
953
954         if (where.beats != 1) {
955                 where.beats = 1;
956                 where.bars++;
957         }
958
959         /* new meters *always* start on a beat. */
960         where.ticks = 0;
961
962         do_insert (new MeterSection (beat, where, meter.divisions_per_bar(), meter.note_divisor()));
963
964         if (recompute) {
965                 recompute_map (true);
966         }
967
968 }
969
970 void
971 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
972 {
973         Tempo newtempo (beats_per_minute, note_type);
974         TempoSection* t;
975
976         for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
977                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
978                         {
979                                 Glib::Threads::RWLock::WriterLock lm (lock);
980                                 *((Tempo*) t) = newtempo;
981                                 recompute_map (false);
982                         }
983                         PropertyChanged (PropertyChange ());
984                         break;
985                 }
986         }
987 }
988
989 void
990 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
991 {
992         Tempo newtempo (beats_per_minute, note_type);
993
994         TempoSection* prev;
995         TempoSection* first;
996         Metrics::iterator i;
997
998         /* find the TempoSection immediately preceding "where"
999          */
1000
1001         for (first = 0, i = metrics.begin(), prev = 0; i != metrics.end(); ++i) {
1002
1003                 if ((*i)->frame() > where) {
1004                         break;
1005                 }
1006
1007                 TempoSection* t;
1008
1009                 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1010                         if (!first) {
1011                                 first = t;
1012                         }
1013                         prev = t;
1014                 }
1015         }
1016
1017         if (!prev) {
1018                 if (!first) {
1019                         error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1020                         return;
1021                 }
1022
1023                 prev = first;
1024         }
1025
1026         /* reset */
1027
1028         {
1029                 Glib::Threads::RWLock::WriterLock lm (lock);
1030                 /* cannot move the first tempo section */
1031                 *((Tempo*)prev) = newtempo;
1032                 recompute_map (false);
1033         }
1034
1035         PropertyChanged (PropertyChange ());
1036 }
1037
1038 const MeterSection&
1039 TempoMap::first_meter () const
1040 {
1041         const MeterSection *m = 0;
1042
1043         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1044                 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1045                         return *m;
1046                 }
1047         }
1048
1049         fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1050         abort(); /*NOTREACHED*/
1051         return *m;
1052 }
1053
1054 MeterSection&
1055 TempoMap::first_meter ()
1056 {
1057         MeterSection *m = 0;
1058
1059         /* CALLER MUST HOLD LOCK */
1060
1061         for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1062                 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1063                         return *m;
1064                 }
1065         }
1066
1067         fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1068         abort(); /*NOTREACHED*/
1069         return *m;
1070 }
1071
1072 const TempoSection&
1073 TempoMap::first_tempo () const
1074 {
1075         const TempoSection *t = 0;
1076
1077         /* CALLER MUST HOLD LOCK */
1078
1079         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1080                 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1081                         return *t;
1082                 }
1083         }
1084
1085         fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1086         abort(); /*NOTREACHED*/
1087         return *t;
1088 }
1089
1090 TempoSection&
1091 TempoMap::first_tempo ()
1092 {
1093         TempoSection *t = 0;
1094
1095         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1096                 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1097                         return *t;
1098                 }
1099         }
1100
1101         fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1102         abort(); /*NOTREACHED*/
1103         return *t;
1104 }
1105
1106 void
1107 TempoMap::recompute_map (bool reassign_tempo_bbt, framepos_t end)
1108 {
1109         /* CALLER MUST HOLD WRITE LOCK */
1110
1111         if (end < 0) {
1112
1113                 /* we will actually stop once we hit
1114                    the last metric.
1115                 */
1116                 end = max_framepos;
1117
1118         }
1119
1120         DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1121
1122         if (end == 0) {
1123                 /* silly call from Session::process() during startup
1124                  */
1125                 return;
1126         }
1127
1128         Metrics::const_iterator i;
1129
1130         TempoSection* prev_ts = 0;
1131
1132         for (i = metrics.begin(); i != metrics.end(); ++i) {
1133                 TempoSection* t;
1134
1135                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1136
1137                         if (prev_ts) {
1138                                 if (prev_ts->type() == TempoSection::Ramp) {
1139                                         prev_ts->set_c_func_from_tempo_and_beat (t->beats_per_minute(), t->beat() - prev_ts->beat(), _frame_rate);
1140                                         t->set_frame (prev_ts->frame_at_tempo (t->beats_per_minute(), _frame_rate) + prev_ts->frame());
1141                                 } else {
1142                                         double const ticks_relative_to_prev = (t->beat() - prev_ts->beat()) * BBT_Time::ticks_per_beat;
1143                                         framecnt_t const duration = (framecnt_t) floor (ticks_relative_to_prev * prev_ts->frames_per_beat (_frame_rate)
1144                                                                                          * BBT_Time::ticks_per_beat);
1145                                         prev_ts->set_c_func (0.0);
1146                                         t->set_frame (duration + prev_ts->frame());
1147                                 }
1148                         }
1149                         prev_ts = t;
1150                 }
1151         }
1152
1153         Metrics::const_iterator mi;
1154         MeterSection* meter = 0;
1155
1156         for (mi = metrics.begin(); mi != metrics.end(); ++mi) {
1157                 /* We now have the tempo map set. use it to set meter positions.*/
1158                 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1159                         meter->set_frame (frame_at_tick (meter->beat() * BBT_Time::ticks_per_beat));
1160                 }
1161         }
1162 }
1163
1164
1165 TempoMetric
1166 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1167 {
1168         Glib::Threads::RWLock::ReaderLock lm (lock);
1169         TempoMetric m (first_meter(), first_tempo());
1170
1171         /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1172            at something, because we insert the default tempo and meter during
1173            TempoMap construction.
1174
1175            now see if we can find better candidates.
1176         */
1177
1178         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1179
1180                 if ((*i)->frame() > frame) {
1181                         break;
1182                 }
1183
1184                 m.set_metric(*i);
1185
1186                 if (last) {
1187                         *last = i;
1188                 }
1189         }
1190
1191         return m;
1192 }
1193 /* XX meters only */
1194 TempoMetric
1195 TempoMap::metric_at (BBT_Time bbt) const
1196 {
1197         Glib::Threads::RWLock::ReaderLock lm (lock);
1198         TempoMetric m (first_meter(), first_tempo());
1199
1200         /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1201            at something, because we insert the default tempo and meter during
1202            TempoMap construction.
1203
1204            now see if we can find better candidates.
1205         */
1206
1207         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1208                 MeterSection* mw;
1209                 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1210                         BBT_Time section_start (mw->bbt());
1211
1212                         if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1213                                 break;
1214                         }
1215
1216                         m.set_metric (*i);
1217                 }
1218         }
1219
1220         return m;
1221 }
1222
1223 void
1224 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1225 {
1226         Glib::Threads::RWLock::ReaderLock lm (lock);
1227
1228         if (frame < 0) {
1229                 bbt.bars = 1;
1230                 bbt.beats = 1;
1231                 bbt.ticks = 0;
1232                 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1233                 return;
1234         }
1235         bbt = beats_to_bbt_locked (beat_at_frame (frame));
1236 }
1237
1238 double
1239 TempoMap::bbt_to_beats (Timecode::BBT_Time bbt)
1240 {
1241         Glib::Threads::RWLock::ReaderLock lm (lock);
1242         return bbt_to_beats_locked (bbt);
1243 }
1244
1245 double
1246 TempoMap::bbt_to_beats_locked (Timecode::BBT_Time bbt)
1247 {
1248         /* CALLER HOLDS READ LOCK */
1249
1250         double accumulated_beats = 0.0;
1251         double accumulated_bars = 0.0;
1252         MeterSection* prev_ms = 0;
1253
1254         Metrics::const_iterator i;
1255
1256         for (i = metrics.begin(); i != metrics.end(); ++i) {
1257                 MeterSection* m;
1258                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1259                         double bars_to_m = 0.0;
1260                         if (prev_ms) {
1261                                 bars_to_m = (m->beat() - prev_ms->beat()) / prev_ms->divisions_per_bar();
1262                         }
1263                         if ((bars_to_m + accumulated_bars) > (bbt.bars - 1)) {
1264                                 break;
1265                         }
1266                         if (prev_ms) {
1267                                 accumulated_beats += m->beat() - prev_ms->beat();
1268                                 accumulated_bars += bars_to_m;
1269                         }
1270                         prev_ms = m;
1271                 }
1272         }
1273
1274         double const remaining_bars = (bbt.bars - 1) - accumulated_bars;
1275         double const remaining_bars_in_beats = remaining_bars * prev_ms->divisions_per_bar();
1276         double const ret = remaining_bars_in_beats + accumulated_beats + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1277         return ret;
1278 }
1279
1280 Timecode::BBT_Time
1281 TempoMap::beats_to_bbt (double beats)
1282 {
1283         Glib::Threads::RWLock::ReaderLock lm (lock);
1284         return beats_to_bbt_locked (beats);
1285 }
1286
1287 Timecode::BBT_Time
1288 TempoMap::beats_to_bbt_locked (double beats)
1289 {
1290         /* CALLER HOLDS READ LOCK */
1291
1292         MeterSection* prev_ms = 0;
1293         uint32_t accumulated_bars = 0;
1294
1295         Metrics::const_iterator i;
1296
1297         for (i = metrics.begin(); i != metrics.end(); ++i) {
1298                 MeterSection* m = 0;
1299
1300                 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1301
1302                         if (beats < m->beat()) {
1303                                 /* this is the meter after the one our beat is on*/
1304                                 break;
1305                         }
1306
1307                         if (prev_ms) {
1308                                 /* we need a whole number of bars. */
1309                                 accumulated_bars += ((m->beat() - prev_ms->beat()) + 1) / prev_ms->divisions_per_bar();
1310                         }
1311
1312                         prev_ms = m;
1313                 }
1314         }
1315
1316         double const beats_in_ms = beats - prev_ms->beat();
1317         uint32_t const bars_in_ms = (uint32_t) floor (beats_in_ms / prev_ms->divisions_per_bar());
1318         uint32_t const total_bars = bars_in_ms + accumulated_bars;
1319         double const remaining_beats = beats_in_ms - (bars_in_ms * prev_ms->divisions_per_bar());
1320         double const remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1321
1322         BBT_Time ret;
1323
1324         ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1325         ret.beats = (uint32_t) floor (remaining_beats);
1326         ret.bars = total_bars;
1327
1328         /* 0 0 0 to 1 1 0 - based mapping*/
1329         ++ret.bars;
1330         ++ret.beats;
1331
1332         if (ret.ticks >= BBT_Time::ticks_per_beat) {
1333                 ++ret.beats;
1334                 ret.ticks -= BBT_Time::ticks_per_beat;
1335         }
1336
1337         if (ret.beats > prev_ms->divisions_per_bar()) {
1338                 ++ret.bars;
1339                 ret.beats = 1;
1340         }
1341
1342         return ret;
1343 }
1344
1345 double
1346 TempoMap::tick_at_frame (framecnt_t frame) const
1347 {
1348         /* HOLD (at least) THE READER LOCK */
1349
1350         Metrics::const_iterator i;
1351         TempoSection* prev_ts = 0;
1352         double accumulated_ticks = 0.0;
1353
1354         for (i = metrics.begin(); i != metrics.end(); ++i) {
1355                 TempoSection* t;
1356
1357                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1358
1359                         if ((prev_ts) && frame < t->frame()) {
1360                                 /*the previous ts is the one containing the frame */
1361
1362                                 framepos_t const time = frame - prev_ts->frame();
1363
1364                                 return prev_ts->tick_at_frame (time, _frame_rate) + accumulated_ticks;
1365                         }
1366
1367                         if (prev_ts && t->frame() > prev_ts->frame()) {
1368                                 accumulated_ticks = t->beat() * BBT_Time::ticks_per_beat;
1369                         }
1370
1371                         prev_ts = t;
1372                 }
1373         }
1374
1375         /* treated as constant for this ts */
1376         framecnt_t const frames_in_section = frame - prev_ts->frame();
1377         double const ticks_in_section = (frames_in_section / prev_ts->frames_per_beat (_frame_rate)) * Timecode::BBT_Time::ticks_per_beat;
1378
1379         return ticks_in_section + accumulated_ticks;
1380
1381 }
1382
1383 framecnt_t
1384 TempoMap::frame_at_tick (double tick) const
1385 {
1386         /* HOLD THE READER LOCK */
1387
1388         double accumulated_ticks = 0.0;
1389         double accumulated_ticks_to_prev = 0.0;
1390         const TempoSection* prev_ts = 0;
1391
1392         Metrics::const_iterator i;
1393
1394         for (i = metrics.begin(); i != metrics.end(); ++i) {
1395                 TempoSection* t;
1396                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1397
1398                         if (prev_ts && t->frame() > prev_ts->frame()) {
1399                                 accumulated_ticks = t->beat() * BBT_Time::ticks_per_beat;
1400                         }
1401
1402                         if (prev_ts && tick < accumulated_ticks) {
1403                                 /* prev_ts is the one affecting us. */
1404
1405                                 double const ticks_in_section = tick - accumulated_ticks_to_prev;
1406
1407                                 return prev_ts->frame_at_tick (ticks_in_section, _frame_rate) + prev_ts->frame();
1408                         }
1409                         accumulated_ticks_to_prev = accumulated_ticks;
1410                         prev_ts = t;
1411                 }
1412         }
1413         /* must be treated as constant, irrespective of _type */
1414         double const ticks_in_section = tick - accumulated_ticks_to_prev;
1415         double const dtime = (ticks_in_section / BBT_Time::ticks_per_beat) * prev_ts->frames_per_beat (_frame_rate);
1416
1417         framecnt_t const ret = ((framecnt_t) floor (dtime)) + prev_ts->frame();
1418
1419         return ret;
1420 }
1421
1422 double
1423 TempoMap::beat_at_frame (framecnt_t frame) const
1424 {
1425         Glib::Threads::RWLock::ReaderLock lm (lock);
1426
1427         return tick_at_frame (frame) / BBT_Time::ticks_per_beat;
1428 }
1429
1430 framecnt_t
1431 TempoMap::frame_at_beat (double beat) const
1432 {
1433         Glib::Threads::RWLock::ReaderLock lm (lock);
1434
1435         return frame_at_tick (beat * BBT_Time::ticks_per_beat);
1436 }
1437
1438 framepos_t
1439 TempoMap::frame_time (const BBT_Time& bbt)
1440 {
1441         if (bbt.bars < 1) {
1442                 warning << string_compose (_("tempo map asked for frame time at bar < 1  (%1)\n"), bbt) << endmsg;
1443                 return 0;
1444         }
1445
1446         if (bbt.beats < 1) {
1447                 throw std::logic_error ("beats are counted from one");
1448         }
1449         Glib::Threads::RWLock::ReaderLock lm (lock);
1450
1451         framepos_t const ret = frame_at_beat (bbt_to_beats_locked (bbt));
1452
1453         return ret;
1454 }
1455
1456
1457 framecnt_t
1458 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
1459 {
1460
1461         Glib::Threads::RWLock::ReaderLock lm (lock);
1462
1463         Metrics::const_iterator i;
1464         TempoSection* first = 0;
1465         TempoSection* second = 0;
1466
1467         for (i = metrics.begin(); i != metrics.end(); ++i) {
1468                 TempoSection* t;
1469
1470                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1471
1472                         if ((*i)->frame() > pos) {
1473                                 second = t;
1474                                 break;
1475                         }
1476
1477                         first = t;
1478                 }
1479         }
1480         if (first && second) {
1481                 framepos_t const time = pos - first->frame();
1482                 double const tick_at_time = first->tick_at_frame (time, _frame_rate);
1483                 double const bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
1484                 double const time_at_bbt = first->frame_at_tick (tick_at_time + bbt_ticks, _frame_rate);
1485
1486                 return time_at_bbt - time;
1487         }
1488
1489         double const ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
1490         return (framecnt_t) floor ((ticks / BBT_Time::ticks_per_beat) * first->frames_per_beat(_frame_rate));
1491 }
1492
1493 framepos_t
1494 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
1495 {
1496         return round_to_type (fr, dir, Bar);
1497 }
1498
1499 framepos_t
1500 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
1501 {
1502         return round_to_type (fr, dir, Beat);
1503 }
1504
1505 framepos_t
1506 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
1507 {
1508         Glib::Threads::RWLock::ReaderLock lm (lock);
1509
1510         uint32_t ticks = (uint32_t) floor (tick_at_frame (fr) + 0.5);
1511         uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
1512         uint32_t ticks_one_subdivisions_worth = (uint32_t)BBT_Time::ticks_per_beat / sub_num;
1513
1514         ticks -= beats * BBT_Time::ticks_per_beat;
1515
1516         if (dir > 0) {
1517                 /* round to next (or same iff dir == RoundUpMaybe) */
1518
1519                 uint32_t mod = ticks % ticks_one_subdivisions_worth;
1520
1521                 if (mod == 0 && dir == RoundUpMaybe) {
1522                         /* right on the subdivision, which is fine, so do nothing */
1523
1524                 } else if (mod == 0) {
1525                         /* right on the subdivision, so the difference is just the subdivision ticks */
1526                         ticks += ticks_one_subdivisions_worth;
1527
1528                 } else {
1529                         /* not on subdivision, compute distance to next subdivision */
1530
1531                         ticks += ticks_one_subdivisions_worth - mod;
1532                 }
1533
1534                 if (ticks >= BBT_Time::ticks_per_beat) {
1535                         ticks -= BBT_Time::ticks_per_beat;
1536                 }
1537         } else if (dir < 0) {
1538
1539                 /* round to previous (or same iff dir == RoundDownMaybe) */
1540
1541                 uint32_t difference = ticks % ticks_one_subdivisions_worth;
1542
1543                 if (difference == 0 && dir == RoundDownAlways) {
1544                         /* right on the subdivision, but force-rounding down,
1545                            so the difference is just the subdivision ticks */
1546                         difference = ticks_one_subdivisions_worth;
1547                 }
1548
1549                 if (ticks < difference) {
1550                         ticks = BBT_Time::ticks_per_beat - ticks;
1551                 } else {
1552                         ticks -= difference;
1553                 }
1554
1555         } else {
1556                 /* round to nearest */
1557                 double rem;
1558
1559                 /* compute the distance to the previous and next subdivision */
1560
1561                 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
1562
1563                         /* closer to the next subdivision, so shift forward */
1564
1565                         ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
1566
1567                         DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
1568
1569                         if (ticks > BBT_Time::ticks_per_beat) {
1570                                 ++beats;
1571                                 ticks -= BBT_Time::ticks_per_beat;
1572                                 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
1573                         }
1574
1575                 } else if (rem > 0) {
1576
1577                         /* closer to previous subdivision, so shift backward */
1578
1579                         if (rem > ticks) {
1580                                 if (beats == 0) {
1581                                         /* can't go backwards past zero, so ... */
1582                                         return 0;
1583                                 }
1584                                 /* step back to previous beat */
1585                                 --beats;
1586                                 ticks = lrint (BBT_Time::ticks_per_beat - rem);
1587                                 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
1588                         } else {
1589                                 ticks = lrint (ticks - rem);
1590                                 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
1591                         }
1592                 } else {
1593                         /* on the subdivision, do nothing */
1594                 }
1595         }
1596         return frame_at_tick ((beats * BBT_Time::ticks_per_beat) + ticks);
1597 }
1598
1599 framepos_t
1600 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
1601 {
1602         Glib::Threads::RWLock::ReaderLock lm (lock);
1603
1604         double const beat_at_framepos = beat_at_frame (frame);
1605
1606         BBT_Time bbt (beats_to_bbt_locked (beat_at_framepos));
1607
1608         switch (type) {
1609         case Bar:
1610                 if (dir < 0) {
1611                         /* find bar previous to 'frame' */
1612                         bbt.beats = 1;
1613                         bbt.ticks = 0;
1614                         return frame_time (bbt);
1615
1616                 } else if (dir > 0) {
1617                         /* find bar following 'frame' */
1618                         ++bbt.bars;
1619                         bbt.beats = 1;
1620                         bbt.ticks = 0;
1621                         return frame_time (bbt);
1622                 } else {
1623                         /* true rounding: find nearest bar */
1624                         framepos_t raw_ft = frame_time (bbt);
1625                         bbt.beats = 1;
1626                         bbt.ticks = 0;
1627                         framepos_t prev_ft = frame_time (bbt);
1628                         ++bbt.bars;
1629                         framepos_t next_ft = frame_time (bbt);
1630
1631                         if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) { 
1632                                 return next_ft;
1633                         } else {
1634                                 return prev_ft;
1635                         }
1636                 }
1637
1638                 break;
1639
1640         case Beat:
1641                 if (dir < 0) {
1642                         return frame_at_beat (floor (beat_at_framepos));
1643                 } else if (dir > 0) {
1644                         return frame_at_beat (ceil (beat_at_framepos));
1645                 } else {
1646                         return frame_at_beat (floor (beat_at_framepos + 0.5));
1647                 }
1648                 break;
1649         }
1650
1651         return 0;
1652 }
1653
1654 void
1655 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
1656                     framepos_t lower, framepos_t upper)
1657 {
1658         Glib::Threads::RWLock::ReaderLock lm (lock);
1659         uint32_t const upper_beat = (uint32_t) floor (beat_at_frame (upper));
1660         uint32_t cnt = (uint32_t) ceil (beat_at_frame (lower));
1661
1662         while (cnt <= upper_beat) {
1663                 framecnt_t const pos = frame_at_beat (cnt);
1664                 MeterSection const meter = meter_section_at (pos);
1665                 Tempo const tempo = tempo_at (pos);
1666                 BBT_Time const bbt = beats_to_bbt_locked ((double) cnt);
1667
1668                 points.push_back (BBTPoint (meter, tempo, pos, bbt.bars, bbt.beats));
1669                 ++cnt;
1670         }
1671 }
1672
1673 const TempoSection&
1674 TempoMap::tempo_section_at (framepos_t frame) const
1675 {
1676         Glib::Threads::RWLock::ReaderLock lm (lock);
1677
1678         Metrics::const_iterator i;
1679         TempoSection* prev = 0;
1680
1681         for (i = metrics.begin(); i != metrics.end(); ++i) {
1682                 TempoSection* t;
1683
1684                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1685
1686                         if ((*i)->frame() > frame) {
1687                                 break;
1688                         }
1689
1690                         prev = t;
1691                 }
1692         }
1693
1694         if (prev == 0) {
1695                 fatal << endmsg;
1696                 abort(); /*NOTREACHED*/
1697         }
1698
1699         return *prev;
1700 }
1701
1702 /* don't use this to calculate length (the tempo is only correct for this frame).
1703    do that stuff based on the beat_at_frame and frame_at_beat api
1704 */
1705 double
1706 TempoMap::frames_per_beat_at (framepos_t frame, framecnt_t sr) const
1707 {
1708         Glib::Threads::RWLock::ReaderLock lm (lock);
1709
1710         const TempoSection* ts_at = &tempo_section_at (frame);
1711         const TempoSection* ts_after = 0;
1712         Metrics::const_iterator i;
1713
1714         for (i = metrics.begin(); i != metrics.end(); ++i) {
1715                 TempoSection* t;
1716
1717                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1718
1719                         if ((*i)->frame() > frame) {
1720                                 ts_after = t;
1721                                 break;
1722                         }
1723                 }
1724         }
1725
1726         if (ts_after) {
1727                 return  (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame - ts_at->frame(), _frame_rate));
1728         }
1729         /* must be treated as constant tempo */
1730         return ts_at->frames_per_beat (_frame_rate);
1731 }
1732
1733 const Tempo
1734 TempoMap::tempo_at (framepos_t frame) const
1735 {
1736         Glib::Threads::RWLock::ReaderLock lm (lock);
1737
1738         TempoMetric m (metric_at (frame));
1739         TempoSection* prev_ts = 0;
1740
1741         Metrics::const_iterator i;
1742
1743         for (i = metrics.begin(); i != metrics.end(); ++i) {
1744                 TempoSection* t;
1745                 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1746                         if ((prev_ts) && t->frame() > frame) {
1747                                 /* this is the one past frame */
1748                                 framepos_t const time = frame - prev_ts->frame();
1749                                 double const ret = prev_ts->tempo_at_frame (time, _frame_rate);
1750                                 Tempo const ret_tempo (ret, m.tempo().note_type ());
1751                                 return ret_tempo;
1752                         }
1753                         prev_ts = t;
1754                 }
1755         }
1756
1757         return m.tempo();
1758
1759 }
1760
1761 const MeterSection&
1762 TempoMap::meter_section_at (framepos_t frame) const
1763 {
1764         Glib::Threads::RWLock::ReaderLock lm (lock);
1765         Metrics::const_iterator i;
1766         MeterSection* prev = 0;
1767
1768         for (i = metrics.begin(); i != metrics.end(); ++i) {
1769                 MeterSection* t;
1770
1771                 if ((t = dynamic_cast<MeterSection*> (*i)) != 0) {
1772
1773                         if ((*i)->frame() > frame) {
1774                                 break;
1775                         }
1776
1777                         prev = t;
1778                 }
1779         }
1780
1781         if (prev == 0) {
1782                 fatal << endmsg;
1783                 abort(); /*NOTREACHED*/
1784         }
1785
1786         return *prev;
1787 }
1788
1789 const Meter&
1790 TempoMap::meter_at (framepos_t frame) const
1791 {
1792         TempoMetric m (metric_at (frame));
1793         return m.meter();
1794 }
1795
1796 XMLNode&
1797 TempoMap::get_state ()
1798 {
1799         Metrics::const_iterator i;
1800         XMLNode *root = new XMLNode ("TempoMap");
1801
1802         {
1803                 Glib::Threads::RWLock::ReaderLock lm (lock);
1804                 for (i = metrics.begin(); i != metrics.end(); ++i) {
1805                         root->add_child_nocopy ((*i)->get_state());
1806                 }
1807         }
1808
1809         return *root;
1810 }
1811
1812 int
1813 TempoMap::set_state (const XMLNode& node, int /*version*/)
1814 {
1815         {
1816                 Glib::Threads::RWLock::WriterLock lm (lock);
1817
1818                 XMLNodeList nlist;
1819                 XMLNodeConstIterator niter;
1820                 Metrics old_metrics (metrics);
1821                 MeterSection* last_meter = 0;
1822                 metrics.clear();
1823
1824                 nlist = node.children();
1825
1826                 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
1827                         XMLNode* child = *niter;
1828
1829                         if (child->name() == TempoSection::xml_state_node_name) {
1830
1831                                 try {
1832                                         TempoSection* ts = new TempoSection (*child);
1833                                         metrics.push_back (ts);
1834
1835                                         if (ts->bar_offset() < 0.0) {
1836                                                 if (last_meter) {
1837                                                         //ts->update_bar_offset_from_bbt (*last_meter);
1838                                                 }
1839                                         }
1840                                 }
1841
1842                                 catch (failed_constructor& err){
1843                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1844                                         metrics = old_metrics;
1845                                         break;
1846                                 }
1847
1848                         } else if (child->name() == MeterSection::xml_state_node_name) {
1849
1850                                 try {
1851                                         MeterSection* ms = new MeterSection (*child);
1852                                         metrics.push_back (ms);
1853                                         last_meter = ms;
1854                                 }
1855
1856                                 catch (failed_constructor& err) {
1857                                         error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
1858                                         metrics = old_metrics;
1859                                         break;
1860                                 }
1861                         }
1862                 }
1863
1864                 if (niter == nlist.end()) {
1865                         MetricSectionSorter cmp;
1866                         metrics.sort (cmp);
1867                 }
1868                 /* check for legacy sessions where bbt was the base musical unit for tempo */
1869                 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1870                         MeterSection* prev_ms;
1871                         TempoSection* prev_ts;
1872                         if ((prev_ms = dynamic_cast<MeterSection*>(*i)) != 0) {
1873                                 if (prev_ms->beat() < 0.0) {
1874                                         /*XX we cannot possibly make this work??. */
1875                                         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());
1876                                         prev_ms->set_beat (start);
1877                                 }
1878                         } else if ((prev_ts = dynamic_cast<TempoSection*>(*i)) != 0) {
1879                                 if (prev_ts->beat() < 0.0) {
1880                                         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);
1881                                         prev_ts->set_beat (start);
1882
1883                                 }
1884                         }
1885                 }
1886                 /* check for multiple tempo/meters at the same location, which
1887                    ardour2 somehow allowed.
1888                 */
1889
1890                 Metrics::iterator prev = metrics.end();
1891                 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1892                         if (prev != metrics.end()) {
1893                                 MeterSection* ms;
1894                                 MeterSection* prev_ms;
1895                                 TempoSection* ts;
1896                                 TempoSection* prev_ts;
1897                                 if ((prev_ms = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
1898                                         if (prev_ms->beat() == ms->beat()) {
1899                                                 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_ms->beat()) << endmsg;
1900                                                 error << string_compose (_("Multiple meter definitions found at %1"), prev_ms->beat()) << endmsg;
1901                                                 return -1;
1902                                         }
1903                                 } else if ((prev_ts = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
1904                                         if (prev_ts->beat() == ts->beat()) {
1905                                                 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->beat()) << endmsg;
1906                                                 error << string_compose (_("Multiple tempo definitions found at %1"), prev_ts->beat()) << endmsg;
1907                                                 return -1;
1908                                         }
1909                                 }
1910                         }
1911                         prev = i;
1912                 }
1913
1914                 recompute_map (true, -1);
1915         }
1916
1917         PropertyChanged (PropertyChange ());
1918
1919         return 0;
1920 }
1921
1922 void
1923 TempoMap::dump (std::ostream& o) const
1924 {
1925         Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
1926         const MeterSection* m;
1927         const TempoSection* t;
1928
1929         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1930
1931                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
1932                         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? "
1933                           << t->movable() << ')' << endl;
1934                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
1935                         o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
1936                           << " (movable? " << m->movable() << ')' << endl;
1937                 }
1938         }
1939 }
1940
1941 int
1942 TempoMap::n_tempos() const
1943 {
1944         Glib::Threads::RWLock::ReaderLock lm (lock);
1945         int cnt = 0;
1946
1947         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1948                 if (dynamic_cast<const TempoSection*>(*i) != 0) {
1949                         cnt++;
1950                 }
1951         }
1952
1953         return cnt;
1954 }
1955
1956 int
1957 TempoMap::n_meters() const
1958 {
1959         Glib::Threads::RWLock::ReaderLock lm (lock);
1960         int cnt = 0;
1961
1962         for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1963                 if (dynamic_cast<const MeterSection*>(*i) != 0) {
1964                         cnt++;
1965                 }
1966         }
1967
1968         return cnt;
1969 }
1970
1971 void
1972 TempoMap::insert_time (framepos_t where, framecnt_t amount)
1973 {
1974         {
1975                 Glib::Threads::RWLock::WriterLock lm (lock);
1976                 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
1977                         if ((*i)->frame() >= where && (*i)->movable ()) {
1978                                 (*i)->set_frame ((*i)->frame() + amount);
1979                         }
1980                 }
1981
1982                 /* now reset the BBT time of all metrics, based on their new
1983                  * audio time. This is the only place where we do this reverse
1984                  * timestamp.
1985                  */
1986
1987                 Metrics::iterator i;
1988                 const MeterSection* meter;
1989                 const TempoSection* tempo;
1990                 MeterSection *m;
1991                 TempoSection *t;
1992
1993                 meter = &first_meter ();
1994                 tempo = &first_tempo ();
1995
1996                 BBT_Time start;
1997                 BBT_Time end;
1998
1999                 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
2000
2001                 bool first = true;
2002                 MetricSection* prev = 0;
2003
2004                 for (i = metrics.begin(); i != metrics.end(); ++i) {
2005
2006                         BBT_Time bbt;
2007                         //TempoMetric metric (*meter, *tempo);
2008                         MeterSection* ms = const_cast<MeterSection*>(meter);
2009                         TempoSection* ts = const_cast<TempoSection*>(tempo);
2010                         if (prev) {
2011                                 if (ts){
2012                                         if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
2013                                                 ts->set_beat (t->beat());
2014                                         }
2015                                         if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2016                                                 ts->set_beat (m->beat());
2017                                         }
2018                                         ts->set_frame (prev->frame());
2019
2020                                 }
2021                                 if (ms) {
2022                                         if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
2023                                                 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
2024                                                 ms->set_beat (start);
2025                                         }
2026                                         if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
2027                                                 pair<double, BBT_Time> start = make_pair (t->beat(), beats_to_bbt_locked (t->beat()));
2028                                                 ms->set_beat (start);
2029                                         }
2030                                         ms->set_frame (prev->frame());
2031                                 }
2032
2033                         } else {
2034                                 // metric will be at frames=0 bbt=1|1|0 by default
2035                                 // which is correct for our purpose
2036                         }
2037
2038                         // cerr << bbt << endl;
2039
2040                         if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
2041                                 t->set_beat (beat_at_frame (m->frame()));
2042                                 tempo = t;
2043                                 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->beat() <<endl;
2044                         } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
2045                                 bbt_time (m->frame(), bbt);
2046
2047                                 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
2048
2049                                 if (first) {
2050                                         first = false;
2051                                 } else {
2052
2053                                         if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
2054                                                 /* round up to next beat */
2055                                                 bbt.beats += 1;
2056                                         }
2057
2058                                         bbt.ticks = 0;
2059
2060                                         if (bbt.beats != 1) {
2061                                                 /* round up to next bar */
2062                                                 bbt.bars += 1;
2063                                                 bbt.beats = 1;
2064                                         }
2065                                 }
2066                                 pair<double, BBT_Time> start = make_pair (beat_at_frame (m->frame()), bbt);
2067                                 m->set_beat (start);
2068                                 meter = m;
2069                                 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->beat() <<endl;
2070                         } else {
2071                                 fatal << _("programming error: unhandled MetricSection type") << endmsg;
2072                                 abort(); /*NOTREACHED*/
2073                         }
2074
2075                         prev = (*i);
2076                 }
2077
2078                 recompute_map (true);
2079         }
2080
2081
2082         PropertyChanged (PropertyChange ());
2083 }
2084 bool
2085 TempoMap::remove_time (framepos_t where, framecnt_t amount)
2086 {
2087         bool moved = false;
2088
2089         std::list<MetricSection*> metric_kill_list;
2090
2091         TempoSection* last_tempo = NULL;
2092         MeterSection* last_meter = NULL;
2093         bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
2094         bool meter_after = false; // is there a meter marker likewise?
2095         {
2096                 Glib::Threads::RWLock::WriterLock lm (lock);
2097                 for (Metrics::iterator i = metrics.begin(); i != metrics.end(); ++i) {
2098                         if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
2099                                 metric_kill_list.push_back(*i);
2100                                 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
2101                                 if (lt)
2102                                         last_tempo = lt;
2103                                 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
2104                                 if (lm)
2105                                         last_meter = lm;
2106                         }
2107                         else if ((*i)->frame() >= where) {
2108                                 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
2109                                 (*i)->set_frame ((*i)->frame() - amount);
2110                                 if ((*i)->frame() == where) {
2111                                         // marker was immediately after end of range
2112                                         tempo_after = dynamic_cast<TempoSection*> (*i);
2113                                         meter_after = dynamic_cast<MeterSection*> (*i);
2114                                 }
2115                                 moved = true;
2116                         }
2117                 }
2118
2119                 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
2120                 if (last_tempo && !tempo_after) {
2121                         metric_kill_list.remove(last_tempo);
2122                         last_tempo->set_frame(where);
2123                         moved = true;
2124                 }
2125                 if (last_meter && !meter_after) {
2126                         metric_kill_list.remove(last_meter);
2127                         last_meter->set_frame(where);
2128                         moved = true;
2129                 }
2130
2131                 //remove all the remaining metrics
2132                 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
2133                         metrics.remove(*i);
2134                         moved = true;
2135                 }
2136
2137                 if (moved) {
2138                         recompute_map (true);
2139                 }
2140         }
2141         PropertyChanged (PropertyChange ());
2142         return moved;
2143 }
2144
2145 /** Add some (fractional) beats to a session frame position, and return the result in frames.
2146  *  pos can be -ve, if required.
2147  */
2148 framepos_t
2149 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
2150 {
2151         return frame_at_beat (beat_at_frame (pos) + beats.to_double());
2152 }
2153
2154 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
2155 framepos_t
2156 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
2157 {
2158         return frame_at_beat (beat_at_frame (pos) - beats.to_double());
2159 }
2160
2161 /** Add the BBT interval op to pos and return the result */
2162 framepos_t
2163 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
2164 {
2165         cerr << "framepos_plus_bbt - untested" << endl;
2166         Glib::Threads::RWLock::ReaderLock lm (lock);
2167
2168         Metrics::const_iterator i;
2169         const MeterSection* meter;
2170         const MeterSection* m;
2171         const TempoSection* tempo;
2172         const TempoSection* next_tempo = 0;
2173         const TempoSection* t;
2174         double frames_per_beat;
2175         framepos_t effective_pos = max (pos, (framepos_t) 0);
2176
2177         meter = &first_meter ();
2178         tempo = &first_tempo ();
2179
2180         assert (meter);
2181         assert (tempo);
2182
2183         /* find the starting metrics for tempo & meter */
2184
2185         for (i = metrics.begin(); i != metrics.end(); ++i) {
2186
2187                 if ((*i)->frame() > effective_pos) {
2188                         break;
2189                 }
2190
2191                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2192                         tempo = t;
2193                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2194                         meter = m;
2195                 }
2196         }
2197
2198         for (i = metrics.begin(); i != metrics.end(); ++i) {
2199                 if ((*i)->frame() > effective_pos) {
2200                         if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2201                                 next_tempo = t;
2202                         }
2203                         break;
2204                 }
2205         }
2206
2207         /* We now have:
2208
2209            meter -> the Meter for "pos"
2210            tempo -> the Tempo for "pos"
2211            next_tempo -> the Tempo after "pos" or 0
2212            i     -> for first new metric after "pos", possibly metrics.end()
2213         */
2214
2215         /* now comes the complicated part. we have to add one beat a time,
2216            checking for a new metric on every beat.
2217         */
2218
2219         uint64_t bars = 0;
2220         /* fpb is used for constant tempo */
2221         frames_per_beat = tempo->frames_per_beat (_frame_rate);
2222
2223         while (op.bars) {
2224
2225                 bars++;
2226                 op.bars--;
2227
2228                 /* check if we need to use a new metric section: has adding frames moved us
2229                    to or after the start of the next metric section? in which case, use it.
2230                 */
2231
2232                 if (i != metrics.end()) {
2233                         if ((*i)->frame() <= pos) {
2234
2235                                 /* about to change tempo or meter, so add the
2236                                  * number of frames for the bars we've just
2237                                  * traversed before we change the
2238                                  * frames_per_beat value.
2239                                  */
2240
2241                                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2242                                         next_tempo = t;
2243                                 }
2244
2245                                 if (next_tempo) {
2246                                         pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), _frame_rate);
2247                                 } else {
2248                                         pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2249                                 }
2250
2251                                 bars = 0;
2252
2253                                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2254                                         tempo = t;
2255                                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2256                                         meter = m;
2257                                 }
2258                                 ++i;
2259                                 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2260                         }
2261                 }
2262
2263         }
2264
2265         if (next_tempo) {
2266                 pos += tempo->frame_at_beat (bars * meter->divisions_per_bar(), _frame_rate);
2267         } else {
2268                 pos += llrint (frames_per_beat * (bars * meter->divisions_per_bar()));
2269         }
2270
2271         uint64_t beats = 0;
2272
2273         while (op.beats) {
2274
2275                 /* given the current meter, have we gone past the end of the bar ? */
2276
2277                 beats++;
2278                 op.beats--;
2279
2280                 /* check if we need to use a new metric section: has adding frames moved us
2281                    to or after the start of the next metric section? in which case, use it.
2282                 */
2283
2284                 if (i != metrics.end()) {
2285                         if ((*i)->frame() <= pos) {
2286
2287                                 /* about to change tempo or meter, so add the
2288                                  * number of frames for the beats we've just
2289                                  * traversed before we change the
2290                                  * frames_per_beat value.
2291                                  */
2292
2293                                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2294                                         next_tempo = t;
2295                                 }
2296
2297                                 if (next_tempo) {
2298                                         pos += tempo->frame_at_beat (beats, _frame_rate);
2299                                 } else {
2300                                         pos += llrint (beats * frames_per_beat);
2301                                 }
2302
2303                                 beats = 0;
2304
2305                                 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2306                                         tempo = t;
2307                                 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2308                                         meter = m;
2309                                 }
2310                                 ++i;
2311                                 frames_per_beat = tempo->frames_per_beat (_frame_rate);
2312                         }
2313                 }
2314         }
2315
2316         if (next_tempo) {
2317                 pos += tempo->frame_at_beat (beats, _frame_rate);
2318         } else {
2319                 pos += llrint (beats * frames_per_beat);
2320         }
2321
2322         if (op.ticks) {
2323                 pos += tempo->frame_at_tick (op.ticks, _frame_rate);
2324         }
2325
2326         return pos;
2327
2328 }
2329
2330 /** Count the number of beats that are equivalent to distance when going forward,
2331     starting at pos.
2332 */
2333 Evoral::Beats
2334 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
2335 {
2336         return Evoral::Beats(beat_at_frame (pos + distance) - beat_at_frame (pos));
2337 }
2338
2339 struct bbtcmp {
2340     bool operator() (const BBT_Time& a, const BBT_Time& b) {
2341             return a < b;
2342     }
2343 };
2344
2345 std::ostream&
2346 operator<< (std::ostream& o, const Meter& m) {
2347         return o << m.divisions_per_bar() << '/' << m.note_divisor();
2348 }
2349
2350 std::ostream&
2351 operator<< (std::ostream& o, const Tempo& t) {
2352         return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
2353 }
2354
2355 std::ostream&
2356 operator<< (std::ostream& o, const MetricSection& section) {
2357
2358         o << "MetricSection @ " << section.frame() << ' ';
2359
2360         const TempoSection* ts;
2361         const MeterSection* ms;
2362
2363         if ((ts = dynamic_cast<const TempoSection*> (&section)) != 0) {
2364                 o << *((const Tempo*) ts);
2365         } else if ((ms = dynamic_cast<const MeterSection*> (&section)) != 0) {
2366                 //o << *((const Meter*) ms);
2367         }
2368
2369         return o;
2370 }