2 Copyright (C) 2000-2002 Paul Davis
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.
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.
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.
26 #include <glibmm/threads.h>
28 #include "pbd/enumwriter.h"
29 #include "pbd/xml++.h"
30 #include "evoral/Beats.hpp"
32 #include "ardour/debug.h"
33 #include "ardour/lmath.h"
34 #include "ardour/tempo.h"
40 using namespace ARDOUR;
43 using Timecode::BBT_Time;
45 /* _default tempo is 4/4 qtr=120 */
47 Meter TempoMap::_default_meter (4.0, 4.0);
48 Tempo TempoMap::_default_tempo (120.0);
50 /***********************************************************************/
53 Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
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.
59 The return value IS NOT interpretable in terms of "beats".
62 return (60.0 * sr) / (tempo.beats_per_minute() * (_note_type/tempo.note_type()));
66 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
68 return frames_per_grid (tempo, sr) * _divisions_per_bar;
71 /***********************************************************************/
73 const string TempoSection::xml_state_node_name = "Tempo";
75 TempoSection::TempoSection (const XMLNode& node)
77 , Tempo (TempoMap::default_tempo())
81 const XMLProperty *prop;
87 if ((prop = node.property ("start")) != 0) {
88 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
92 /* legacy session - start used to be in bbt*/
95 info << _("Legacy session detected. TempoSection XML node will be altered.") << endmsg;
99 if ((prop = node.property ("pulse")) != 0) {
100 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
101 error << _("TempoSection XML node has an illegal \"pulse\" value") << endmsg;
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;
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();
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();
125 if ((prop = node.property ("note-type")) == 0) {
126 /* older session, make note type be quarter by default */
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();
135 if ((prop = node.property ("movable")) == 0) {
136 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
137 throw failed_constructor();
140 set_movable (string_is_affirmative (prop->value()));
142 if ((prop = node.property ("active")) == 0) {
143 warning << _("TempoSection XML node has no \"active\" property") << endmsg;
146 set_active (string_is_affirmative (prop->value()));
149 if ((prop = node.property ("tempo-type")) == 0) {
152 _type = Type (string_2_enum (prop->value(), _type));
155 if ((prop = node.property ("lock-style")) == 0) {
156 set_position_lock_style (MusicTime);
158 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
163 TempoSection::get_state() const
165 XMLNode *root = new XMLNode (xml_state_node_name);
169 snprintf (buf, sizeof (buf), "%f", pulse());
170 root->add_property ("pulse", buf);
171 snprintf (buf, sizeof (buf), "%li", frame());
172 root->add_property ("frame", buf);
173 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
174 root->add_property ("beats-per-minute", buf);
175 snprintf (buf, sizeof (buf), "%f", _note_type);
176 root->add_property ("note-type", buf);
177 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
178 root->add_property ("movable", buf);
179 snprintf (buf, sizeof (buf), "%s", active()?"yes":"no");
180 root->add_property ("active", buf);
181 root->add_property ("tempo-type", enum_2_string (_type));
182 root->add_property ("lock-style", enum_2_string (position_lock_style()));
188 TempoSection::set_type (Type type)
193 /** returns the tempo in whole pulses per minute at the zero-based (relative to session) frame.
196 TempoSection::tempo_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
199 if (_type == Constant || _c_func == 0.0) {
200 return pulses_per_minute();
203 return pulse_tempo_at_time (frame_to_minute (f - frame(), frame_rate));
206 /** returns the zero-based frame (relative to session)
207 where the tempo in whole pulses per minute occurs in this section.
208 beat b is only used for constant tempos.
209 note that the tempo map may have multiple such values.
212 TempoSection::frame_at_tempo (const double& ppm, const double& b, const framecnt_t& frame_rate) const
214 if (_type == Constant || _c_func == 0.0) {
215 return ((b - pulse()) * frames_per_pulse (frame_rate)) + frame();
218 return minute_to_frame (time_at_pulse_tempo (ppm), frame_rate) + frame();
220 /** returns the tempo in pulses per minute at the zero-based (relative to session) beat.
223 TempoSection::tempo_at_pulse (const double& p) const
226 if (_type == Constant || _c_func == 0.0) {
227 return pulses_per_minute();
229 double const ppm = pulse_tempo_at_pulse (p - pulse());
233 /** returns the zero-based beat (relative to session)
234 where the tempo in whole pulses per minute occurs given frame f. frame f is only used for constant tempos.
235 note that the session tempo map may have multiple beats at a given tempo.
238 TempoSection::pulse_at_tempo (const double& ppm, const framepos_t& f, const framecnt_t& frame_rate) const
240 if (_type == Constant || _c_func == 0.0) {
241 double const pulses = ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
244 return pulse_at_pulse_tempo (ppm) + pulse();
247 /** returns the zero-based pulse (relative to session origin)
248 where the zero-based frame (relative to session)
252 TempoSection::pulse_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
254 if (_type == Constant || _c_func == 0.0) {
255 return ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
258 return pulse_at_time (frame_to_minute (f - frame(), frame_rate)) + pulse();
261 /** returns the zero-based frame (relative to session start frame)
262 where the zero-based pulse (relative to session start)
267 TempoSection::frame_at_pulse (const double& p, const framecnt_t& frame_rate) const
269 if (_type == Constant || _c_func == 0.0) {
270 return (framepos_t) floor ((p - pulse()) * frames_per_pulse (frame_rate)) + frame();
273 return minute_to_frame (time_at_pulse (p - pulse()), frame_rate) + frame();
281 Tt----|-----------------*|
282 Ta----|--------------|* |
288 _______________|___|____
289 time a t (next tempo)
292 Duration in beats at time a is the integral of some Tempo function.
293 In our case, the Tempo function (Tempo at time t) is
296 with function constant
301 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
302 b(t) = T0(e^(ct) - 1) / c
304 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:
305 t(b) = log((cb / T0) + 1) / c
307 The time t at which Tempo T occurs is a as above:
308 t(T) = log(T / T0) / c
310 The beat at which a Tempo T occurs is:
313 The Tempo at which beat b occurs is:
316 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
317 Our problem is that we usually don't know t.
318 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.
319 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
320 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
322 By substituting our expanded t as a in the c function above, our problem is reduced to:
323 c = T0 (e^(log (Ta / T0)) - 1) / b
325 We can now store c for future time calculations.
326 If the following tempo section (the one that defines c in conjunction with this one)
327 is changed or moved, c is no longer valid.
329 The public methods are session-relative.
331 Most of this stuff is taken from this paper:
334 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
337 Zurich University of Arts
338 Institute for Computer Music and Sound Technology
340 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
345 compute this ramp's function constant using the end tempo (in whole pulses per minute)
346 and duration (pulses into global start) of some later tempo section.
349 TempoSection::compute_c_func_pulse (const double& end_bpm, const double& end_pulse, const framecnt_t& frame_rate)
351 double const log_tempo_ratio = log (end_bpm / pulses_per_minute());
352 return pulses_per_minute() * (expm1 (log_tempo_ratio)) / (end_pulse - pulse());
355 /* compute the function constant from some later tempo section, given tempo (whole pulses/min.) and distance (in frames) from session origin */
357 TempoSection::compute_c_func_frame (const double& end_bpm, const framepos_t& end_frame, const framecnt_t& frame_rate) const
359 return c_func (end_bpm, frame_to_minute (end_frame - frame(), frame_rate));
363 TempoSection::minute_to_frame (const double& time, const framecnt_t& frame_rate) const
365 return (framecnt_t) floor ((time * 60.0 * frame_rate) + 0.5);
369 TempoSection::frame_to_minute (const framecnt_t& frame, const framecnt_t& frame_rate) const
371 return (frame / (double) frame_rate) / 60.0;
374 /* position function */
376 TempoSection::a_func (double end_bpm, double c_func) const
378 return log (end_bpm / pulses_per_minute()) / c_func;
381 /*function constant*/
383 TempoSection::c_func (double end_bpm, double end_time) const
385 return log (end_bpm / pulses_per_minute()) / end_time;
388 /* tempo in ppm at time in minutes */
390 TempoSection::pulse_tempo_at_time (const double& time) const
392 return exp (_c_func * time) * pulses_per_minute();
395 /* time in minutes at tempo in ppm */
397 TempoSection::time_at_pulse_tempo (const double& pulse_tempo) const
399 return log (pulse_tempo / pulses_per_minute()) / _c_func;
402 /* tick at tempo in ppm */
404 TempoSection::pulse_at_pulse_tempo (const double& pulse_tempo) const
406 return (pulse_tempo - pulses_per_minute()) / _c_func;
409 /* tempo in ppm at tick */
411 TempoSection::pulse_tempo_at_pulse (const double& pulse) const
413 return (pulse * _c_func) + pulses_per_minute();
416 /* pulse at time in minutes */
418 TempoSection::pulse_at_time (const double& time) const
420 return expm1 (_c_func * time) * (pulses_per_minute() / _c_func);
423 /* time in minutes at pulse */
425 TempoSection::time_at_pulse (const double& pulse) const
427 return log1p ((_c_func * pulse) / pulses_per_minute()) / _c_func;
430 /***********************************************************************/
432 const string MeterSection::xml_state_node_name = "Meter";
434 MeterSection::MeterSection (const XMLNode& node)
435 : MetricSection (0.0), Meter (TempoMap::default_meter())
437 XMLProperty const * prop;
440 const XMLProperty *prop;
444 framepos_t frame = 0;
445 pair<double, BBT_Time> start;
447 if ((prop = node.property ("start")) != 0) {
448 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
452 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
454 /* legacy session - start used to be in bbt*/
455 info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
460 if ((prop = node.property ("pulse")) != 0) {
461 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
462 error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
467 if ((prop = node.property ("beat")) != 0) {
468 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
469 error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
475 if ((prop = node.property ("bbt")) == 0) {
476 warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
477 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
481 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
482 throw failed_constructor();
488 if ((prop = node.property ("frame")) != 0) {
489 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
490 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
496 /* beats-per-bar is old; divisions-per-bar is new */
498 if ((prop = node.property ("divisions-per-bar")) == 0) {
499 if ((prop = node.property ("beats-per-bar")) == 0) {
500 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
501 throw failed_constructor();
504 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
505 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
506 throw failed_constructor();
509 if ((prop = node.property ("note-type")) == 0) {
510 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
511 throw failed_constructor();
513 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
514 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
515 throw failed_constructor();
518 if ((prop = node.property ("lock-style")) == 0) {
519 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
520 set_position_lock_style (MusicTime);
522 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
525 if ((prop = node.property ("movable")) == 0) {
526 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
527 throw failed_constructor();
530 set_movable (string_is_affirmative (prop->value()));
534 MeterSection::get_state() const
536 XMLNode *root = new XMLNode (xml_state_node_name);
540 snprintf (buf, sizeof (buf), "%lf", pulse());
541 root->add_property ("pulse", buf);
542 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
546 root->add_property ("bbt", buf);
547 snprintf (buf, sizeof (buf), "%lf", beat());
548 root->add_property ("beat", buf);
549 snprintf (buf, sizeof (buf), "%f", _note_type);
550 root->add_property ("note-type", buf);
551 snprintf (buf, sizeof (buf), "%li", frame());
552 root->add_property ("frame", buf);
553 root->add_property ("lock-style", enum_2_string (position_lock_style()));
554 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
555 root->add_property ("divisions-per-bar", buf);
556 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
557 root->add_property ("movable", buf);
562 /***********************************************************************/
566 Tempo can be thought of as a source of the musical pulse.
567 Meters divide that pulse into measures and beats.
568 Tempo pulses can be divided to be in sympathy with the meter, but this does not affect the beat
569 at any particular time.
570 Note that Tempo::beats_per_minute() has nothing to do with musical beats.
571 It should rather be thought of as tempo note divisions per minute.
573 TempoSections, which are nice to think of in whole pulses per minute,
574 and MeterSecions which divide tempo pulses into measures (via divisions_per_bar)
575 and beats (via note_divisor) are used to form a tempo map.
576 TempoSections and MeterSections may be locked to either audio or music (position lock style).
577 We construct the tempo map by first using the frame or pulse position (depending on position lock style) of each tempo.
578 We then use this pulse/frame layout to find the beat & pulse or frame position of each meter (again depending on lock style).
580 Having done this, we can now find any one of tempo, beat, frame or pulse if a beat, frame, pulse or tempo is known.
582 The first tempo and first meter are special. they must move together, and must be locked to audio.
583 Audio locked tempos which lie before the first meter are made inactive.
584 They will be re-activated if the first meter is again placed before them.
586 Both tempos and meters have a pulse position and a frame position.
587 Meters also have a beat position, which is always 0.0 for the first meter.
589 A tempo locked to music is locked to musical pulses.
590 A meter locked to music is locked to beats.
592 Recomputing the tempo map is the process where the 'missing' position
593 (tempo pulse or meter pulse & beat in the case of AudioTime, frame for MusicTime) is calculated.
595 It is important to keep the _metrics in an order that makes sense.
596 Because ramped MusicTime and AudioTime tempos can interact with each other,
597 reordering is frequent. Care must be taken to keep _metrics in a solved state.
598 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
600 struct MetricSectionSorter {
601 bool operator() (const MetricSection* a, const MetricSection* b) {
602 return a->pulse() < b->pulse();
606 struct MetricSectionFrameSorter {
607 bool operator() (const MetricSection* a, const MetricSection* b) {
608 return a->frame() < b->frame();
612 TempoMap::TempoMap (framecnt_t fr)
615 BBT_Time start (1, 1, 0);
617 TempoSection *t = new TempoSection (0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Constant);
618 MeterSection *m = new MeterSection (0.0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor());
620 t->set_movable (false);
621 m->set_movable (false);
623 /* note: frame time is correct (zero) for both of these */
625 _metrics.push_back (t);
626 _metrics.push_back (m);
630 TempoMap::~TempoMap ()
635 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
637 bool removed = false;
640 Glib::Threads::RWLock::WriterLock lm (lock);
641 if ((removed = remove_tempo_locked (tempo))) {
642 if (complete_operation) {
643 recompute_map (_metrics);
648 if (removed && complete_operation) {
649 PropertyChanged (PropertyChange ());
654 TempoMap::remove_tempo_locked (const TempoSection& tempo)
658 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
659 if (dynamic_cast<TempoSection*> (*i) != 0) {
660 if (tempo.frame() == (*i)->frame()) {
661 if ((*i)->movable()) {
673 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
675 bool removed = false;
678 Glib::Threads::RWLock::WriterLock lm (lock);
679 if ((removed = remove_meter_locked (tempo))) {
680 if (complete_operation) {
681 recompute_map (_metrics);
686 if (removed && complete_operation) {
687 PropertyChanged (PropertyChange ());
692 TempoMap::remove_meter_locked (const MeterSection& tempo)
696 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
697 if (dynamic_cast<MeterSection*> (*i) != 0) {
698 if (tempo.frame() == (*i)->frame()) {
699 if ((*i)->movable()) {
711 TempoMap::do_insert (MetricSection* section)
713 bool need_add = true;
714 /* we only allow new meters to be inserted on beat 1 of an existing
718 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
719 //assert (m->bbt().ticks == 0);
721 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
723 pair<double, BBT_Time> corrected = make_pair (m->pulse(), m->bbt());
724 corrected.second.beats = 1;
725 corrected.second.ticks = 0;
726 corrected.first = bbt_to_beats_locked (_metrics, corrected.second);
727 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
728 m->bbt(), corrected.second) << endmsg;
729 //m->set_pulse (corrected);
733 /* Look for any existing MetricSection that is of the same type and
734 in the same bar as the new one, and remove it before adding
735 the new one. Note that this means that if we find a matching,
736 existing section, we can break out of the loop since we're
737 guaranteed that there is only one such match.
740 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
742 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
743 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
744 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
745 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
747 if (tempo && insert_tempo) {
750 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
751 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
753 if (!tempo->movable()) {
755 /* can't (re)move this section, so overwrite
756 * its data content (but not its properties as
760 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
761 (*i)->set_position_lock_style (AudioTime);
763 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
764 t->set_type (insert_tempo->type());
773 } else if (meter && insert_meter) {
777 bool const ipm = insert_meter->position_lock_style() == MusicTime;
779 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
781 if (!meter->movable()) {
783 /* can't (re)move this section, so overwrite
784 * its data content (but not its properties as
788 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
789 (*i)->set_position_lock_style (insert_meter->position_lock_style());
798 /* non-matching types, so we don't care */
802 /* Add the given MetricSection, if we didn't just reset an existing
807 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
808 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
811 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
812 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
815 bool const ipm = insert_meter->position_lock_style() == MusicTime;
816 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
821 } else if (insert_tempo) {
822 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
823 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
826 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
827 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
834 _metrics.insert (i, section);
835 //dump (_metrics, std::cerr);
840 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, TempoSection::Type type)
843 Glib::Threads::RWLock::WriterLock lm (lock);
844 TempoSection& first (first_tempo());
845 if (ts.pulse() != first.pulse()) {
846 remove_tempo_locked (ts);
847 add_tempo_locked (tempo, pulse, true, type);
849 first.set_type (type);
851 /* cannot move the first tempo section */
852 *static_cast<Tempo*>(&first) = tempo;
853 recompute_map (_metrics);
858 PropertyChanged (PropertyChange ());
862 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const framepos_t& frame, TempoSection::Type type)
865 Glib::Threads::RWLock::WriterLock lm (lock);
866 TempoSection& first (first_tempo());
867 if (ts.frame() != first.frame()) {
868 remove_tempo_locked (ts);
869 add_tempo_locked (tempo, frame, true, type);
871 first.set_type (type);
872 first.set_pulse (0.0);
873 first.set_position_lock_style (AudioTime);
875 /* cannot move the first tempo section */
876 *static_cast<Tempo*>(&first) = tempo;
877 recompute_map (_metrics);
882 PropertyChanged (PropertyChange ());
886 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, ARDOUR::TempoSection::Type type)
888 TempoSection* ts = 0;
890 Glib::Threads::RWLock::WriterLock lm (lock);
891 ts = add_tempo_locked (tempo, pulse, true, type);
894 PropertyChanged (PropertyChange ());
900 TempoMap::add_tempo (const Tempo& tempo, const framepos_t& frame, ARDOUR::TempoSection::Type type)
902 TempoSection* ts = 0;
904 Glib::Threads::RWLock::WriterLock lm (lock);
905 ts = add_tempo_locked (tempo, frame, true, type);
909 PropertyChanged (PropertyChange ());
915 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, bool recompute, ARDOUR::TempoSection::Type type)
917 TempoSection* t = new TempoSection (pulse, tempo.beats_per_minute(), tempo.note_type(), type);
922 solve_map (_metrics, t, t->pulse());
929 TempoMap::add_tempo_locked (const Tempo& tempo, framepos_t frame, bool recompute, ARDOUR::TempoSection::Type type)
931 TempoSection* t = new TempoSection (frame, tempo.beats_per_minute(), tempo.note_type(), type);
936 solve_map (_metrics, t, t->frame());
943 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where)
946 Glib::Threads::RWLock::WriterLock lm (lock);
949 remove_meter_locked (ms);
950 add_meter_locked (meter, bbt_to_beats_locked (_metrics, where), where, true);
952 MeterSection& first (first_meter());
953 const PositionLockStyle pl = ms.position_lock_style();
954 /* cannot move the first meter section */
955 *static_cast<Meter*>(&first) = meter;
956 first.set_position_lock_style (pl);
958 recompute_map (_metrics);
961 PropertyChanged (PropertyChange ());
965 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const framepos_t& frame)
968 Glib::Threads::RWLock::WriterLock lm (lock);
970 const double beat = ms.beat();
971 const BBT_Time bbt = ms.bbt();
974 remove_meter_locked (ms);
975 add_meter_locked (meter, frame, beat, bbt, true);
977 MeterSection& first (first_meter());
978 TempoSection& first_t (first_tempo());
979 /* cannot move the first meter section */
980 *static_cast<Meter*>(&first) = meter;
981 first.set_position_lock_style (AudioTime);
982 first.set_pulse (0.0);
983 first.set_frame (frame);
984 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
985 first.set_beat (beat);
986 first_t.set_frame (first.frame());
987 first_t.set_pulse (0.0);
988 first_t.set_position_lock_style (AudioTime);
990 recompute_map (_metrics);
992 PropertyChanged (PropertyChange ());
997 TempoMap::add_meter (const Meter& meter, const double& beat, const BBT_Time& where)
1001 Glib::Threads::RWLock::WriterLock lm (lock);
1002 m = add_meter_locked (meter, beat, where, true);
1007 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1008 dump (_metrics, std::cerr);
1012 PropertyChanged (PropertyChange ());
1018 TempoMap::add_meter (const Meter& meter, const framepos_t& frame, const double& beat, const Timecode::BBT_Time& where)
1020 MeterSection* m = 0;
1022 Glib::Threads::RWLock::WriterLock lm (lock);
1023 m = add_meter_locked (meter, frame, beat, where, true);
1028 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1029 dump (_metrics, std::cerr);
1033 PropertyChanged (PropertyChange ());
1039 TempoMap::add_meter_locked (const Meter& meter, double beat, BBT_Time where, bool recompute)
1041 /* a new meter always starts a new bar on the first beat. so
1042 round the start time appropriately. remember that
1043 `where' is based on the existing tempo map, not
1044 the result after we insert the new meter.
1048 if (where.beats != 1) {
1052 /* new meters *always* start on a beat. */
1054 const double pulse = pulse_at_beat_locked (_metrics, beat);
1055 MeterSection* new_meter = new MeterSection (pulse, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1056 do_insert (new_meter);
1059 solve_map (_metrics, new_meter, pulse);
1066 TempoMap::add_meter_locked (const Meter& meter, framepos_t frame, double beat, Timecode::BBT_Time where, bool recompute)
1068 MeterSection* new_meter = new MeterSection (frame, beat, where, meter.divisions_per_bar(), meter.note_divisor());
1070 double pulse = pulse_at_frame_locked (_metrics, frame);
1071 new_meter->set_pulse (pulse);
1073 do_insert (new_meter);
1076 solve_map (_metrics, new_meter, frame);
1083 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1085 Tempo newtempo (beats_per_minute, note_type);
1088 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1089 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1094 Glib::Threads::RWLock::WriterLock lm (lock);
1095 *((Tempo*) t) = newtempo;
1096 recompute_map (_metrics);
1098 PropertyChanged (PropertyChange ());
1105 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1107 Tempo newtempo (beats_per_minute, note_type);
1110 TempoSection* first;
1111 Metrics::iterator i;
1113 /* find the TempoSection immediately preceding "where"
1116 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1118 if ((*i)->frame() > where) {
1124 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1137 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1147 Glib::Threads::RWLock::WriterLock lm (lock);
1148 /* cannot move the first tempo section */
1149 *((Tempo*)prev) = newtempo;
1150 recompute_map (_metrics);
1153 PropertyChanged (PropertyChange ());
1157 TempoMap::first_meter () const
1159 const MeterSection *m = 0;
1161 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1162 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1167 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1168 abort(); /*NOTREACHED*/
1173 TempoMap::first_meter ()
1175 MeterSection *m = 0;
1177 /* CALLER MUST HOLD LOCK */
1179 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1180 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1185 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1186 abort(); /*NOTREACHED*/
1191 TempoMap::first_tempo () const
1193 const TempoSection *t = 0;
1195 /* CALLER MUST HOLD LOCK */
1197 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1198 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1202 if (!t->movable()) {
1208 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1209 abort(); /*NOTREACHED*/
1214 TempoMap::first_tempo ()
1216 TempoSection *t = 0;
1218 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1219 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1223 if (!t->movable()) {
1229 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1230 abort(); /*NOTREACHED*/
1234 TempoMap::recompute_tempos (Metrics& metrics)
1236 TempoSection* prev_t = 0;
1238 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1241 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1245 if (!t->movable()) {
1251 if (t->position_lock_style() == AudioTime) {
1252 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1253 t->set_pulse (prev_t->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
1256 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1257 t->set_frame (prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
1264 prev_t->set_c_func (0.0);
1267 /* tempos must be positioned correctly */
1269 TempoMap::recompute_meters (Metrics& metrics)
1271 MeterSection* meter = 0;
1272 MeterSection* prev_m = 0;
1274 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1275 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1276 if (meter->position_lock_style() == AudioTime) {
1278 pair<double, BBT_Time> b_bbt;
1279 if (meter->movable()) {
1280 const double beats = ((pulse_at_frame_locked (metrics, meter->frame()) - prev_m->pulse()) * prev_m->note_divisor());
1281 const double floor_beats = beats - fmod (beats, prev_m->divisions_per_bar());
1282 b_bbt = make_pair (floor_beats + prev_m->beat(), BBT_Time ((floor_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1283 const double true_pulse = prev_m->pulse() + (floor_beats / prev_m->note_divisor());
1284 const double pulse_off = true_pulse - (beats / prev_m->note_divisor()) - prev_m->pulse();
1285 pulse = true_pulse - pulse_off;
1287 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1289 meter->set_beat (b_bbt);
1290 meter->set_pulse (pulse);
1294 pulse = prev_m->pulse() + ((meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar() / prev_m->note_divisor());
1296 /* shouldn't happen - the first is audio-locked */
1297 pulse = pulse_at_beat_locked (metrics, meter->beat());
1299 pair<double, BBT_Time> new_beat (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), meter->bbt());
1300 meter->set_beat (new_beat);
1301 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1302 meter->set_pulse (pulse);
1308 //dump (_metrics, std::cerr;
1312 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1314 /* CALLER MUST HOLD WRITE LOCK */
1318 /* we will actually stop once we hit
1325 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1328 /* silly call from Session::process() during startup
1333 recompute_tempos (metrics);
1334 recompute_meters (metrics);
1338 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1340 Glib::Threads::RWLock::ReaderLock lm (lock);
1341 TempoMetric m (first_meter(), first_tempo());
1343 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1344 at something, because we insert the default tempo and meter during
1345 TempoMap construction.
1347 now see if we can find better candidates.
1350 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1352 if ((*i)->frame() > frame) {
1366 /* XX meters only */
1368 TempoMap::metric_at (BBT_Time bbt) const
1370 Glib::Threads::RWLock::ReaderLock lm (lock);
1371 TempoMetric m (first_meter(), first_tempo());
1373 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1374 at something, because we insert the default tempo and meter during
1375 TempoMap construction.
1377 now see if we can find better candidates.
1380 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1382 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1383 BBT_Time section_start (mw->bbt());
1385 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1397 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1399 MeterSection* prev_m = 0;
1401 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1403 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1404 if (prev_m && m->beat() > beat) {
1411 double const ret = prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1416 TempoMap::pulse_at_beat (const double& beat) const
1418 Glib::Threads::RWLock::ReaderLock lm (lock);
1419 return pulse_at_beat_locked (_metrics, beat);
1423 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1425 MeterSection* prev_m = 0;
1427 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1429 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1430 if (prev_m && m->pulse() > pulse) {
1437 double const beats_in_section = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1439 return beats_in_section + prev_m->beat();
1443 TempoMap::beat_at_pulse (const double& pulse) const
1445 Glib::Threads::RWLock::ReaderLock lm (lock);
1446 return beat_at_pulse_locked (_metrics, pulse);
1450 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1452 /* HOLD (at least) THE READER LOCK */
1453 TempoSection* prev_t = 0;
1455 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1457 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1461 if (prev_t && t->frame() > frame) {
1462 /*the previous ts is the one containing the frame */
1463 const double ret = prev_t->pulse_at_frame (frame, _frame_rate);
1470 /* treated as constant for this ts */
1471 const double pulses_in_section = (frame - prev_t->frame()) / prev_t->frames_per_pulse (_frame_rate);
1473 return pulses_in_section + prev_t->pulse();
1477 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1479 /* HOLD THE READER LOCK */
1481 const TempoSection* prev_t = 0;
1483 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1486 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1490 if (prev_t && t->pulse() > pulse) {
1491 return prev_t->frame_at_pulse (pulse, _frame_rate);
1497 /* must be treated as constant, irrespective of _type */
1498 double const pulses_in_section = pulse - prev_t->pulse();
1499 double const dtime = pulses_in_section * prev_t->frames_per_pulse (_frame_rate);
1501 framecnt_t const ret = (framecnt_t) floor (dtime) + prev_t->frame();
1507 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1509 const double pulse = pulse_at_frame_locked (metrics, frame);
1510 return beat_at_pulse_locked (metrics, pulse);
1514 TempoMap::beat_at_frame (const framecnt_t& frame) const
1516 Glib::Threads::RWLock::ReaderLock lm (lock);
1517 return beat_at_frame_locked (_metrics, frame);
1521 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1523 const framecnt_t frame = frame_at_pulse_locked (metrics, pulse_at_beat_locked (metrics, beat));
1528 TempoMap::frame_at_beat (const double& beat) const
1530 Glib::Threads::RWLock::ReaderLock lm (lock);
1531 return frame_at_beat_locked (_metrics, beat);
1535 TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1537 /* CALLER HOLDS READ LOCK */
1539 MeterSection* prev_m = 0;
1541 /* because audio-locked meters have 'fake' integral beats,
1542 there is no pulse offset here.
1544 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1546 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1548 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1549 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1557 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1558 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1559 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1565 TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
1567 Glib::Threads::RWLock::ReaderLock lm (lock);
1568 return bbt_to_beats_locked (_metrics, bbt);
1572 TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
1574 /* CALLER HOLDS READ LOCK */
1575 MeterSection* prev_m = 0;
1576 const double beats = (b < 0.0) ? 0.0 : b;
1578 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1579 MeterSection* m = 0;
1581 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1583 if (m->beat() > beats) {
1584 /* this is the meter after the one our beat is on*/
1593 const double beats_in_ms = beats - prev_m->beat();
1594 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1595 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1596 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1597 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1601 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1602 ret.beats = (uint32_t) floor (remaining_beats);
1603 ret.bars = total_bars;
1605 /* 0 0 0 to 1 1 0 - based mapping*/
1609 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1611 ret.ticks -= BBT_Time::ticks_per_beat;
1614 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1623 TempoMap::beats_to_bbt (const double& beats)
1625 Glib::Threads::RWLock::ReaderLock lm (lock);
1626 return beats_to_bbt_locked (_metrics, beats);
1630 TempoMap::pulse_to_bbt (const double& pulse)
1632 Glib::Threads::RWLock::ReaderLock lm (lock);
1633 MeterSection* prev_m = 0;
1635 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1636 MeterSection* m = 0;
1638 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1641 double const pulses_to_m = m->pulse() - prev_m->pulse();
1642 if (prev_m->pulse() + pulses_to_m > pulse) {
1643 /* this is the meter after the one our beat is on*/
1652 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1653 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1654 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1655 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1656 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1660 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1661 ret.beats = (uint32_t) floor (remaining_beats);
1662 ret.bars = total_bars;
1664 /* 0 0 0 to 1 1 0 mapping*/
1668 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1670 ret.ticks -= BBT_Time::ticks_per_beat;
1673 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1682 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1689 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1692 Glib::Threads::RWLock::ReaderLock lm (lock);
1693 const double beat = beat_at_frame_locked (_metrics, frame);
1695 bbt = beats_to_bbt_locked (_metrics, beat);
1699 TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
1701 /* HOLD THE READER LOCK */
1703 const framepos_t ret = frame_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt));
1708 TempoMap::frame_time (const BBT_Time& bbt)
1711 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1715 if (bbt.beats < 1) {
1716 throw std::logic_error ("beats are counted from one");
1718 Glib::Threads::RWLock::ReaderLock lm (lock);
1720 return frame_time_locked (_metrics, bbt);
1724 TempoMap::check_solved (Metrics& metrics, bool by_frame)
1726 TempoSection* prev_t = 0;
1728 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1730 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1735 if ((by_frame && t->frame() < prev_t->frame()) || (!by_frame && t->pulse() < prev_t->pulse())) {
1739 if (t->frame() == prev_t->frame()) {
1743 /* precision check ensures pulses and frames align.*/
1744 if (t->frame() != prev_t->frame_at_pulse (t->pulse(), _frame_rate)) {
1756 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
1758 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1760 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1761 if (!t->movable()) {
1762 t->set_active (true);
1765 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
1766 t->set_active (false);
1768 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
1769 t->set_active (true);
1770 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
1779 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
1781 TempoSection* prev_t = 0;
1782 TempoSection* section_prev = 0;
1783 framepos_t first_m_frame = 0;
1785 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1787 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1788 if (!m->movable()) {
1789 first_m_frame = m->frame();
1794 if (section->movable() && frame <= first_m_frame) {
1798 section->set_active (true);
1799 section->set_frame (frame);
1801 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1803 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1810 section_prev = prev_t;
1813 if (t->position_lock_style() == MusicTime) {
1814 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1815 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1817 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1818 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1826 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
1827 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
1830 if (section->position_lock_style() == MusicTime) {
1831 /* we're setting the frame */
1832 section->set_position_lock_style (AudioTime);
1833 recompute_tempos (imaginary);
1834 section->set_position_lock_style (MusicTime);
1836 recompute_tempos (imaginary);
1839 if (check_solved (imaginary, true)) {
1840 recompute_meters (imaginary);
1844 MetricSectionFrameSorter fcmp;
1845 imaginary.sort (fcmp);
1846 if (section->position_lock_style() == MusicTime) {
1847 /* we're setting the frame */
1848 section->set_position_lock_style (AudioTime);
1849 recompute_tempos (imaginary);
1850 section->set_position_lock_style (MusicTime);
1852 recompute_tempos (imaginary);
1855 if (check_solved (imaginary, true)) {
1856 recompute_meters (imaginary);
1860 MetricSectionSorter cmp;
1861 imaginary.sort (cmp);
1862 if (section->position_lock_style() == MusicTime) {
1863 /* we're setting the frame */
1864 section->set_position_lock_style (AudioTime);
1865 recompute_tempos (imaginary);
1866 section->set_position_lock_style (MusicTime);
1868 recompute_tempos (imaginary);
1871 if (check_solved (imaginary, true)) {
1872 recompute_meters (imaginary);
1876 //dump (imaginary, std::cerr);
1882 TempoMap::solve_map (Metrics& imaginary, TempoSection* section, const double& pulse)
1884 TempoSection* prev_t = 0;
1885 TempoSection* section_prev = 0;
1887 section->set_pulse (pulse);
1889 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1891 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1895 if (!t->movable()) {
1902 section_prev = prev_t;
1905 if (t->position_lock_style() == MusicTime) {
1906 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1907 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1909 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1910 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1917 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
1918 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
1921 if (section->position_lock_style() == AudioTime) {
1922 /* we're setting the pulse */
1923 section->set_position_lock_style (MusicTime);
1924 recompute_tempos (imaginary);
1925 section->set_position_lock_style (AudioTime);
1927 recompute_tempos (imaginary);
1930 if (check_solved (imaginary, false)) {
1931 recompute_meters (imaginary);
1935 MetricSectionSorter cmp;
1936 imaginary.sort (cmp);
1937 if (section->position_lock_style() == AudioTime) {
1938 /* we're setting the pulse */
1939 section->set_position_lock_style (MusicTime);
1940 recompute_tempos (imaginary);
1941 section->set_position_lock_style (AudioTime);
1943 recompute_tempos (imaginary);
1946 if (check_solved (imaginary, false)) {
1947 recompute_meters (imaginary);
1951 MetricSectionFrameSorter fcmp;
1952 imaginary.sort (fcmp);
1953 if (section->position_lock_style() == AudioTime) {
1954 /* we're setting the pulse */
1955 section->set_position_lock_style (MusicTime);
1956 recompute_tempos (imaginary);
1957 section->set_position_lock_style (AudioTime);
1959 recompute_tempos (imaginary);
1962 if (check_solved (imaginary, false)) {
1963 recompute_meters (imaginary);
1967 //dump (imaginary, std::cerr);
1973 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
1975 MeterSection* prev_m = 0;
1977 if (!section->movable()) {
1978 /* lock the first tempo to our first meter */
1979 if (!set_active_tempos (imaginary, frame)) {
1982 TempoSection* first_t = &first_tempo();
1984 TempoSection* new_section = copy_metrics_and_point (future_map, first_t);
1986 new_section->set_frame (frame);
1987 new_section->set_pulse (0.0);
1988 new_section->set_active (true);
1990 if (solve_map (future_map, new_section, frame)) {
1991 first_t->set_frame (frame);
1992 first_t->set_pulse (0.0);
1993 first_t->set_active (true);
1994 solve_map (imaginary, first_t, frame);
2000 section->set_frame (frame);
2002 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2004 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2007 here we set the beat for this frame.
2008 we set it 'incorrectly' to the next bar's first beat
2009 and use the delat to find the meter's pulse.
2011 double new_pulse = 0.0;
2012 pair<double, BBT_Time> b_bbt;
2014 if (section->movable()) {
2015 const double beats = ((pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor());
2016 const double floor_beats = beats - fmod (beats, prev_m->divisions_per_bar());
2017 b_bbt = make_pair (floor_beats + prev_m->beat(), BBT_Time ((floor_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2018 const double true_pulse = prev_m->pulse() + (floor_beats / prev_m->note_divisor());
2019 const double pulse_off = true_pulse - (beats / prev_m->note_divisor()) - prev_m->pulse();
2020 new_pulse = true_pulse - pulse_off;
2022 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2025 section->set_beat (b_bbt);
2026 section->set_pulse (new_pulse);
2032 double new_pulse = 0.0;
2033 if (m->position_lock_style() == MusicTime) {
2034 new_pulse = prev_m->pulse() + ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar() / prev_m->note_divisor());
2035 m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2036 if (m->frame() > section->frame()) {
2037 /* moving 'section' will affect later meters' beat (but not bbt).*/
2038 pair<double, BBT_Time> new_beat (((new_pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), m->bbt());
2039 m->set_beat (new_beat);
2042 pair<double, BBT_Time> b_bbt;
2044 const double beats = ((pulse_at_frame_locked (imaginary, m->frame()) - prev_m->pulse()) * prev_m->note_divisor());
2045 const double floor_beats = beats - fmod (beats , prev_m->divisions_per_bar());
2046 b_bbt = make_pair (floor_beats + prev_m->beat()
2047 , BBT_Time ((floor_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2048 const double true_pulse = prev_m->pulse() + (floor_beats / prev_m->note_divisor());
2049 const double pulse_off = true_pulse - (beats / prev_m->note_divisor()) - prev_m->pulse();
2050 new_pulse = true_pulse - pulse_off;
2052 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2055 m->set_beat (b_bbt);
2057 m->set_pulse (new_pulse);
2063 MetricSectionFrameSorter fcmp;
2064 imaginary.sort (fcmp);
2065 if (section->position_lock_style() == MusicTime) {
2066 /* we're setting the frame */
2067 section->set_position_lock_style (AudioTime);
2068 recompute_meters (imaginary);
2069 section->set_position_lock_style (MusicTime);
2071 recompute_meters (imaginary);
2073 //dump (imaginary, std::cerr);
2077 TempoMap::solve_map (Metrics& imaginary, MeterSection* section, const double& pulse)
2079 MeterSection* prev_m = 0;
2080 MeterSection* section_prev = 0;
2082 section->set_pulse (pulse);
2084 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2086 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2088 section_prev = prev_m;
2092 double new_pulse = 0.0;
2093 if (m->position_lock_style() == MusicTime) {
2094 new_pulse = prev_m->pulse() + ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar() / prev_m->note_divisor());
2095 m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2097 if (new_pulse > section->pulse()) {
2098 /* moving 'section' will affect later meters' beat (but not bbt).*/
2099 pair<double, BBT_Time> new_beat (((new_pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat(), m->bbt());
2100 m->set_beat (new_beat);
2103 pair<double, BBT_Time> b_bbt;
2105 const double beats = ((pulse_at_frame_locked (imaginary, m->frame()) - prev_m->pulse()) * prev_m->note_divisor());
2106 const double floor_beats = beats - fmod (beats, prev_m->divisions_per_bar());
2107 b_bbt = make_pair (floor_beats + prev_m->beat()
2108 , BBT_Time ((floor_beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2109 const double true_pulse = prev_m->pulse() + (floor_beats / prev_m->note_divisor());
2110 const double pulse_off = true_pulse - (beats / prev_m->note_divisor()) - prev_m->pulse();
2111 new_pulse = true_pulse - pulse_off;
2113 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2115 m->set_beat (b_bbt);
2117 m->set_pulse (new_pulse);
2124 const double beats = ((pulse - section_prev->pulse()) * section_prev->note_divisor());
2125 const int32_t bars = (beats + 1) / section_prev->divisions_per_bar();
2126 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), BBT_Time (bars + section_prev->bbt().bars, 1, 0));
2127 section->set_beat (b_bbt);
2128 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2131 MetricSectionSorter cmp;
2132 imaginary.sort (cmp);
2133 if (section->position_lock_style() == AudioTime) {
2134 /* we're setting the pulse */
2135 section->set_position_lock_style (MusicTime);
2136 recompute_meters (imaginary);
2137 section->set_position_lock_style (AudioTime);
2139 recompute_meters (imaginary);
2143 /** places a copy of _metrics into copy and returns a pointer
2144 * to section's equivalent in copy.
2147 TempoMap::copy_metrics_and_point (Metrics& copy, TempoSection* section)
2151 TempoSection* ret = 0;
2153 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2154 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2156 if (t->position_lock_style() == MusicTime) {
2157 ret = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
2158 ret->set_frame (t->frame());
2160 ret = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
2161 ret->set_pulse (t->pulse());
2163 ret->set_active (t->active());
2164 ret->set_movable (t->movable());
2165 copy.push_back (ret);
2168 TempoSection* cp = 0;
2169 if (t->position_lock_style() == MusicTime) {
2170 cp = new TempoSection (t->pulse(), t->beats_per_minute(), t->note_type(), t->type());
2171 cp->set_frame (t->frame());
2173 cp = new TempoSection (t->frame(), t->beats_per_minute(), t->note_type(), t->type());
2174 cp->set_pulse (t->pulse());
2176 cp->set_active (t->active());
2177 cp->set_movable (t->movable());
2178 copy.push_back (cp);
2180 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2181 MeterSection* cp = 0;
2182 if (m->position_lock_style() == MusicTime) {
2183 cp = new MeterSection (m->pulse(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
2184 cp->set_frame (m->frame());
2186 cp = new MeterSection (m->frame(), m->beat(), m->bbt(), m->divisions_per_bar(), m->note_divisor());
2187 cp->set_pulse (m->pulse());
2189 cp->set_movable (m->movable());
2190 copy.push_back (cp);
2198 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2201 TempoSection* new_section = 0;
2204 Glib::Threads::RWLock::ReaderLock lm (lock);
2205 new_section = copy_metrics_and_point (copy, ts);
2208 double const beat = bbt_to_beats_locked (copy, bbt);
2209 bool ret = solve_map (copy, new_section, pulse_at_beat_locked (copy, beat));
2211 Metrics::const_iterator d = copy.begin();
2212 while (d != copy.end()) {
2221 * This is for a gui that needs to know the frame of a tempo section if it were to be moved to some bbt time,
2222 * taking any possible reordering as a consequence of this into account.
2223 * @param section - the section to be altered
2224 * @param bpm - the new Tempo
2225 * @param bbt - the bbt where the altered tempo will fall
2226 * @return returns - the position in frames where the new tempo section will lie.
2229 TempoMap::predict_tempo_frame (TempoSection* section, const BBT_Time& bbt)
2231 Glib::Threads::RWLock::ReaderLock lm (lock);
2234 TempoSection* new_section = copy_metrics_and_point (future_map, section);
2236 const double beat = bbt_to_beats_locked (future_map, bbt);
2238 if (solve_map (future_map, new_section, pulse_at_beat_locked (future_map, beat))) {
2239 ret = new_section->frame();
2241 ret = frame_at_beat_locked (_metrics, beat);
2244 Metrics::const_iterator d = future_map.begin();
2245 while (d != future_map.end()) {
2253 TempoMap::predict_tempo_pulse (TempoSection* section, const framepos_t& frame)
2255 Glib::Threads::RWLock::ReaderLock lm (lock);
2258 TempoSection* new_section = copy_metrics_and_point (future_map, section);
2260 if (solve_map (future_map, new_section, frame)) {
2261 ret = new_section->pulse();
2263 ret = pulse_at_frame_locked (_metrics, frame);
2266 Metrics::const_iterator d = future_map.begin();
2267 while (d != future_map.end()) {
2275 TempoMap::gui_move_tempo_frame (TempoSection* ts, const framepos_t& frame)
2279 Glib::Threads::RWLock::WriterLock lm (lock);
2280 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2281 if (solve_map (future_map, new_section, frame)) {
2282 solve_map (_metrics, ts, frame);
2286 Metrics::const_iterator d = future_map.begin();
2287 while (d != future_map.end()) {
2292 MetricPositionChanged (); // Emit Signal
2296 TempoMap::gui_move_tempo_beat (TempoSection* ts, const double& beat)
2300 Glib::Threads::RWLock::WriterLock lm (lock);
2301 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2302 if (solve_map (future_map, new_section, pulse_at_beat_locked (future_map, beat))) {
2303 solve_map (_metrics, ts, pulse_at_beat_locked (_metrics, beat));
2307 Metrics::const_iterator d = future_map.begin();
2308 while (d != future_map.end()) {
2313 MetricPositionChanged (); // Emit Signal
2317 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
2320 Glib::Threads::RWLock::WriterLock lm (lock);
2321 solve_map (_metrics, ms, frame);
2324 MetricPositionChanged (); // Emit Signal
2328 TempoMap::gui_move_meter (MeterSection* ms, const double& beat)
2331 Glib::Threads::RWLock::WriterLock lm (lock);
2332 solve_map (_metrics, ms, pulse_at_beat_locked (_metrics, beat));
2335 MetricPositionChanged (); // Emit Signal
2339 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2342 bool can_solve = false;
2344 Glib::Threads::RWLock::WriterLock lm (lock);
2345 TempoSection* new_section = copy_metrics_and_point (future_map, ts);
2346 new_section->set_beats_per_minute (bpm.beats_per_minute());
2347 recompute_tempos (future_map);
2349 if (check_solved (future_map, true)) {
2350 ts->set_beats_per_minute (bpm.beats_per_minute());
2351 recompute_map (_metrics);
2356 Metrics::const_iterator d = future_map.begin();
2357 while (d != future_map.end()) {
2362 MetricPositionChanged (); // Emit Signal
2368 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2370 Glib::Threads::RWLock::ReaderLock lm (lock);
2372 const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2373 const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2374 const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2376 return frame_at_beat_locked (_metrics, total_beats);
2380 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2382 return round_to_type (fr, dir, Bar);
2386 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2388 return round_to_type (fr, dir, Beat);
2392 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2394 Glib::Threads::RWLock::ReaderLock lm (lock);
2395 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2396 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2397 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2399 ticks -= beats * BBT_Time::ticks_per_beat;
2402 /* round to next (or same iff dir == RoundUpMaybe) */
2404 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2406 if (mod == 0 && dir == RoundUpMaybe) {
2407 /* right on the subdivision, which is fine, so do nothing */
2409 } else if (mod == 0) {
2410 /* right on the subdivision, so the difference is just the subdivision ticks */
2411 ticks += ticks_one_subdivisions_worth;
2414 /* not on subdivision, compute distance to next subdivision */
2416 ticks += ticks_one_subdivisions_worth - mod;
2419 if (ticks >= BBT_Time::ticks_per_beat) {
2420 ticks -= BBT_Time::ticks_per_beat;
2422 } else if (dir < 0) {
2424 /* round to previous (or same iff dir == RoundDownMaybe) */
2426 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2428 if (difference == 0 && dir == RoundDownAlways) {
2429 /* right on the subdivision, but force-rounding down,
2430 so the difference is just the subdivision ticks */
2431 difference = ticks_one_subdivisions_worth;
2434 if (ticks < difference) {
2435 ticks = BBT_Time::ticks_per_beat - ticks;
2437 ticks -= difference;
2441 /* round to nearest */
2444 /* compute the distance to the previous and next subdivision */
2446 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2448 /* closer to the next subdivision, so shift forward */
2450 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2452 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2454 if (ticks > BBT_Time::ticks_per_beat) {
2456 ticks -= BBT_Time::ticks_per_beat;
2457 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2460 } else if (rem > 0) {
2462 /* closer to previous subdivision, so shift backward */
2466 /* can't go backwards past zero, so ... */
2469 /* step back to previous beat */
2471 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2472 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2474 ticks = lrint (ticks - rem);
2475 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2478 /* on the subdivision, do nothing */
2482 const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2488 TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num)
2490 if (sub_num == -1) {
2491 const double bpb = meter_section_at (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2492 if ((double) when.beats > bpb / 2.0) {
2498 } else if (sub_num == 0) {
2499 const double bpb = meter_section_at (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2500 if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
2502 while ((double) when.beats > bpb) {
2504 when.beats -= (uint32_t) floor (bpb);
2510 const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
2512 if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
2513 /* closer to the next subdivision, so shift forward */
2515 when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
2517 if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
2519 when.ticks -= Timecode::BBT_Time::ticks_per_beat;
2522 } else if (rem > 0) {
2523 /* closer to previous subdivision, so shift backward */
2525 if (rem > when.ticks) {
2526 if (when.beats == 0) {
2527 /* can't go backwards past zero, so ... */
2529 /* step back to previous beat */
2531 when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
2533 when.ticks = when.ticks - rem;
2539 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
2541 Glib::Threads::RWLock::ReaderLock lm (lock);
2543 const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
2544 BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
2549 /* find bar previous to 'frame' */
2552 return frame_time_locked (_metrics, bbt);
2554 } else if (dir > 0) {
2555 /* find bar following 'frame' */
2559 return frame_time_locked (_metrics, bbt);
2561 /* true rounding: find nearest bar */
2562 framepos_t raw_ft = frame_time_locked (_metrics, bbt);
2565 framepos_t prev_ft = frame_time_locked (_metrics, bbt);
2567 framepos_t next_ft = frame_time_locked (_metrics, bbt);
2569 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
2580 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
2581 } else if (dir > 0) {
2582 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
2584 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
2593 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2594 framepos_t lower, framepos_t upper)
2596 Glib::Threads::RWLock::ReaderLock lm (lock);
2597 const int32_t upper_beat = (int32_t) ceil (beat_at_frame_locked (_metrics, upper));
2598 int32_t cnt = floor (beat_at_frame_locked (_metrics, lower));
2599 /* although the map handles negative beats, bbt doesn't. */
2603 while (cnt <= upper_beat) {
2604 framecnt_t pos = frame_at_beat_locked (_metrics, cnt);
2605 const TempoSection tempo = tempo_section_at_locked (pos);
2606 const MeterSection meter = meter_section_at_locked (pos);
2607 const BBT_Time bbt = beats_to_bbt (cnt);
2608 points.push_back (BBTPoint (meter, tempo_at_locked (pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
2614 TempoMap::tempo_section_at (framepos_t frame) const
2616 Glib::Threads::RWLock::ReaderLock lm (lock);
2617 return tempo_section_at_locked (frame);
2621 TempoMap::tempo_section_at_locked (framepos_t frame) const
2623 Metrics::const_iterator i;
2624 TempoSection* prev = 0;
2626 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2629 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2633 if (t->frame() > frame) {
2643 abort(); /*NOTREACHED*/
2650 /* don't use this to calculate length (the tempo is only correct for this frame).
2651 do that stuff based on the beat_at_frame and frame_at_beat api
2654 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
2656 Glib::Threads::RWLock::ReaderLock lm (lock);
2658 const TempoSection* ts_at = &tempo_section_at_locked (frame);
2659 const TempoSection* ts_after = 0;
2660 Metrics::const_iterator i;
2662 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2665 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2669 if ((*i)->frame() > frame) {
2677 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
2679 /* must be treated as constant tempo */
2680 return ts_at->frames_per_beat (_frame_rate);
2684 TempoMap::tempo_at (const framepos_t& frame) const
2686 Glib::Threads::RWLock::ReaderLock lm (lock);
2687 return tempo_at_locked (frame);
2691 TempoMap::tempo_at_locked (const framepos_t& frame) const
2693 TempoSection* prev_t = 0;
2695 Metrics::const_iterator i;
2697 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2699 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2703 if ((prev_t) && t->frame() > frame) {
2704 /* t is the section past frame */
2705 const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
2706 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
2713 const double ret = prev_t->beats_per_minute();
2714 const Tempo ret_tempo (ret, prev_t->note_type ());
2720 TempoMap::meter_section_at (framepos_t frame) const
2722 Glib::Threads::RWLock::ReaderLock lm (lock);
2723 return meter_section_at_locked (frame);
2727 TempoMap::meter_section_at_locked (framepos_t frame) const
2729 Metrics::const_iterator i;
2730 MeterSection* prev = 0;
2732 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2735 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2737 if (prev && (*i)->frame() > frame) {
2747 abort(); /*NOTREACHED*/
2754 TempoMap::meter_at (framepos_t frame) const
2756 TempoMetric m (metric_at (frame));
2761 TempoMap::meter_section_at (const double& beat) const
2763 MeterSection* prev_m = 0;
2764 Glib::Threads::RWLock::ReaderLock lm (lock);
2766 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2768 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2769 if (prev_m && m->beat() > beat) {
2780 TempoMap::get_state ()
2782 Metrics::const_iterator i;
2783 XMLNode *root = new XMLNode ("TempoMap");
2786 Glib::Threads::RWLock::ReaderLock lm (lock);
2787 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2788 root->add_child_nocopy ((*i)->get_state());
2796 TempoMap::set_state (const XMLNode& node, int /*version*/)
2799 Glib::Threads::RWLock::WriterLock lm (lock);
2802 XMLNodeConstIterator niter;
2803 Metrics old_metrics (_metrics);
2806 nlist = node.children();
2808 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
2809 XMLNode* child = *niter;
2811 if (child->name() == TempoSection::xml_state_node_name) {
2814 TempoSection* ts = new TempoSection (*child);
2815 _metrics.push_back (ts);
2818 catch (failed_constructor& err){
2819 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2820 _metrics = old_metrics;
2824 } else if (child->name() == MeterSection::xml_state_node_name) {
2827 MeterSection* ms = new MeterSection (*child);
2828 _metrics.push_back (ms);
2831 catch (failed_constructor& err) {
2832 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
2833 _metrics = old_metrics;
2839 if (niter == nlist.end()) {
2840 MetricSectionSorter cmp;
2841 _metrics.sort (cmp);
2844 /* check for legacy sessions where bbt was the base musical unit for tempo */
2845 MeterSection* prev_m = 0;
2846 TempoSection* prev_t = 0;
2848 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2852 /* if one is < 0.0, they all are. this is a legacy session */
2853 if ((m = dynamic_cast<MeterSection*>(*i)) != 0 && m->pulse() < 0.0) {
2854 if (!m->movable()) {
2855 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2859 m->set_position_lock_style (AudioTime);
2864 /*XX we cannot possibly make this work??. */
2865 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
2866 + (m->bbt().beats - 1)
2867 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
2869 m->set_beat (start);
2870 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
2871 + (m->bbt().beats - 1)
2872 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
2873 m->set_pulse (start_beat / prev_m->note_divisor());
2876 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0 && t->pulse() < 0.0) {
2882 if (!t->movable()) {
2885 t->set_position_lock_style (AudioTime);
2891 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
2892 + (t->legacy_bbt().beats - 1)
2893 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
2895 t->set_pulse (beat / prev_m->note_divisor());
2897 /* really shouldn't happen but.. */
2898 t->set_pulse (beat / 4.0);
2904 /* check for multiple tempo/meters at the same location, which
2905 ardour2 somehow allowed.
2908 Metrics::iterator prev = _metrics.end();
2909 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2910 if (prev != _metrics.end()) {
2912 MeterSection* prev_m;
2914 TempoSection* prev_t;
2915 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
2916 if (prev_m->pulse() == ms->pulse()) {
2917 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
2918 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
2921 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
2922 if (prev_t->pulse() == ts->pulse()) {
2923 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
2924 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
2932 recompute_map (_metrics);
2935 PropertyChanged (PropertyChange ());
2941 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
2943 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2944 const MeterSection* m;
2945 const TempoSection* t;
2946 const TempoSection* prev_t = 0;
2948 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2950 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
2951 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
2952 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
2953 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
2955 o << "previous : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
2956 o << "calculated : " << prev_t->tempo_at_pulse (t->pulse()) * prev_t->note_type() << " | " << prev_t->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate) << " | " << prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate) << std::endl;
2959 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
2960 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
2961 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
2964 o << "------" << std::endl;
2968 TempoMap::n_tempos() const
2970 Glib::Threads::RWLock::ReaderLock lm (lock);
2973 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2974 if (dynamic_cast<const TempoSection*>(*i) != 0) {
2983 TempoMap::n_meters() const
2985 Glib::Threads::RWLock::ReaderLock lm (lock);
2988 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
2989 if (dynamic_cast<const MeterSection*>(*i) != 0) {
2998 TempoMap::insert_time (framepos_t where, framecnt_t amount)
3001 Glib::Threads::RWLock::WriterLock lm (lock);
3002 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3003 if ((*i)->frame() >= where && (*i)->movable ()) {
3004 (*i)->set_frame ((*i)->frame() + amount);
3008 /* now reset the BBT time of all metrics, based on their new
3009 * audio time. This is the only place where we do this reverse
3013 Metrics::iterator i;
3014 const MeterSection* meter;
3015 const TempoSection* tempo;
3019 meter = &first_meter ();
3020 tempo = &first_tempo ();
3025 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
3028 MetricSection* prev = 0;
3030 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3033 //TempoMetric metric (*meter, *tempo);
3034 MeterSection* ms = const_cast<MeterSection*>(meter);
3035 TempoSection* ts = const_cast<TempoSection*>(tempo);
3038 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3042 ts->set_pulse (t->pulse());
3044 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3045 ts->set_pulse (m->pulse());
3047 ts->set_frame (prev->frame());
3051 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3052 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3053 ms->set_beat (start);
3054 ms->set_pulse (m->pulse());
3056 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3060 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3061 pair<double, BBT_Time> start = make_pair (beat, beats_to_bbt_locked (_metrics, beat));
3062 ms->set_beat (start);
3063 ms->set_pulse (t->pulse());
3065 ms->set_frame (prev->frame());
3069 // metric will be at frames=0 bbt=1|1|0 by default
3070 // which is correct for our purpose
3073 // cerr << bbt << endl;
3075 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3079 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3081 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3082 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3083 bbt_time (m->frame(), bbt);
3085 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3091 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3092 /* round up to next beat */
3098 if (bbt.beats != 1) {
3099 /* round up to next bar */
3104 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3105 m->set_beat (start);
3106 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3108 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3110 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3111 abort(); /*NOTREACHED*/
3117 recompute_map (_metrics);
3121 PropertyChanged (PropertyChange ());
3124 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3128 std::list<MetricSection*> metric_kill_list;
3130 TempoSection* last_tempo = NULL;
3131 MeterSection* last_meter = NULL;
3132 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3133 bool meter_after = false; // is there a meter marker likewise?
3135 Glib::Threads::RWLock::WriterLock lm (lock);
3136 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3137 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3138 metric_kill_list.push_back(*i);
3139 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3142 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3146 else if ((*i)->frame() >= where) {
3147 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3148 (*i)->set_frame ((*i)->frame() - amount);
3149 if ((*i)->frame() == where) {
3150 // marker was immediately after end of range
3151 tempo_after = dynamic_cast<TempoSection*> (*i);
3152 meter_after = dynamic_cast<MeterSection*> (*i);
3158 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3159 if (last_tempo && !tempo_after) {
3160 metric_kill_list.remove(last_tempo);
3161 last_tempo->set_frame(where);
3164 if (last_meter && !meter_after) {
3165 metric_kill_list.remove(last_meter);
3166 last_meter->set_frame(where);
3170 //remove all the remaining metrics
3171 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3172 _metrics.remove(*i);
3177 recompute_map (_metrics);
3180 PropertyChanged (PropertyChange ());
3184 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3185 * pos can be -ve, if required.
3188 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3190 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
3193 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3195 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3197 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
3200 /** Add the BBT interval op to pos and return the result */
3202 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3204 Glib::Threads::RWLock::ReaderLock lm (lock);
3206 BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3207 pos_bbt.ticks += op.ticks;
3208 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3210 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3212 pos_bbt.beats += op.beats;
3213 /* the meter in effect will start on the bar */
3214 double divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3215 while (pos_bbt.beats >= divisions_per_bar + 1) {
3217 divisions_per_bar = meter_section_at (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3218 pos_bbt.beats -= divisions_per_bar;
3220 pos_bbt.bars += op.bars;
3222 return frame_time_locked (_metrics, pos_bbt);
3225 /** Count the number of beats that are equivalent to distance when going forward,
3229 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3231 return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
3235 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3241 operator<< (std::ostream& o, const Meter& m) {
3242 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3246 operator<< (std::ostream& o, const Tempo& t) {
3247 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3251 operator<< (std::ostream& o, const MetricSection& section) {
3253 o << "MetricSection @ " << section.frame() << ' ';
3255 const TempoSection* ts;
3256 const MeterSection* ms;
3258 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3259 o << *((const Tempo*) ts);
3260 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3261 o << *((const Meter*) ms);