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