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)
76 : MetricSection (0.0, 0, MusicTime)
77 , Tempo (TempoMap::default_tempo())
80 , _locked_to_meter (false)
82 XMLProperty const * prop;
88 _legacy_bbt = BBT_Time (0, 0, 0);
90 if ((prop = node.property ("start")) != 0) {
91 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
95 /* legacy session - start used to be in bbt*/
98 info << _("Legacy session detected. TempoSection XML node will be altered.") << endmsg;
102 if ((prop = node.property ("pulse")) != 0) {
103 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
104 error << _("TempoSection XML node has an illegal \"pulse\" value") << endmsg;
110 if ((prop = node.property ("frame")) != 0) {
111 if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) {
112 error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg;
118 if ((prop = node.property ("beats-per-minute")) == 0) {
119 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
120 throw failed_constructor();
123 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
124 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
125 throw failed_constructor();
128 if ((prop = node.property ("note-type")) == 0) {
129 /* older session, make note type be quarter by default */
132 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
133 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
134 throw failed_constructor();
138 if ((prop = node.property ("movable")) == 0) {
139 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
140 throw failed_constructor();
143 set_movable (string_is_affirmative (prop->value()));
145 if ((prop = node.property ("active")) == 0) {
146 warning << _("TempoSection XML node has no \"active\" property") << endmsg;
149 set_active (string_is_affirmative (prop->value()));
152 if ((prop = node.property ("tempo-type")) == 0) {
155 _type = Type (string_2_enum (prop->value(), _type));
158 if ((prop = node.property ("lock-style")) == 0) {
160 set_position_lock_style (MusicTime);
162 set_position_lock_style (AudioTime);
165 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
168 if ((prop = node.property ("locked-to-meter")) == 0) {
169 set_locked_to_meter (false);
171 set_locked_to_meter (string_is_affirmative (prop->value()));
176 TempoSection::get_state() const
178 XMLNode *root = new XMLNode (xml_state_node_name);
182 snprintf (buf, sizeof (buf), "%f", pulse());
183 root->add_property ("pulse", buf);
184 snprintf (buf, sizeof (buf), "%li", frame());
185 root->add_property ("frame", buf);
186 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
187 root->add_property ("beats-per-minute", buf);
188 snprintf (buf, sizeof (buf), "%f", _note_type);
189 root->add_property ("note-type", buf);
190 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
191 root->add_property ("movable", buf);
192 snprintf (buf, sizeof (buf), "%s", active()?"yes":"no");
193 root->add_property ("active", buf);
194 root->add_property ("tempo-type", enum_2_string (_type));
195 root->add_property ("lock-style", enum_2_string (position_lock_style()));
196 root->add_property ("locked-to-meter", locked_to_meter()?"yes":"no");
202 TempoSection::set_type (Type type)
207 /** returns the tempo in whole pulses per minute at the zero-based (relative to session) frame.
210 TempoSection::tempo_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
213 if (_type == Constant || _c_func == 0.0) {
214 return pulses_per_minute();
217 return pulse_tempo_at_time (frame_to_minute (f - frame(), frame_rate));
220 /** returns the zero-based frame (relative to session)
221 where the tempo in whole pulses per minute occurs in this section.
222 beat b is only used for constant tempos.
223 note that the tempo map may have multiple such values.
226 TempoSection::frame_at_tempo (const double& ppm, const double& b, const framecnt_t& frame_rate) const
228 if (_type == Constant || _c_func == 0.0) {
229 return ((b - pulse()) * frames_per_pulse (frame_rate)) + frame();
232 return minute_to_frame (time_at_pulse_tempo (ppm), frame_rate) + frame();
234 /** returns the tempo in pulses per minute at the zero-based (relative to session) beat.
237 TempoSection::tempo_at_pulse (const double& p) const
240 if (_type == Constant || _c_func == 0.0) {
241 return pulses_per_minute();
243 double const ppm = pulse_tempo_at_pulse (p - pulse());
247 /** returns the zero-based beat (relative to session)
248 where the tempo in whole pulses per minute occurs given frame f. frame f is only used for constant tempos.
249 note that the session tempo map may have multiple beats at a given tempo.
252 TempoSection::pulse_at_tempo (const double& ppm, const framepos_t& f, const framecnt_t& frame_rate) const
254 if (_type == Constant || _c_func == 0.0) {
255 double const pulses = ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
258 return pulse_at_pulse_tempo (ppm) + pulse();
261 /** returns the zero-based pulse (relative to session origin)
262 where the zero-based frame (relative to session)
266 TempoSection::pulse_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
268 if (_type == Constant || _c_func == 0.0) {
269 return ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
272 return pulse_at_time (frame_to_minute (f - frame(), frame_rate)) + pulse();
275 /** returns the zero-based frame (relative to session start frame)
276 where the zero-based pulse (relative to session start)
281 TempoSection::frame_at_pulse (const double& p, const framecnt_t& frame_rate) const
283 if (_type == Constant || _c_func == 0.0) {
284 return (framepos_t) floor ((p - pulse()) * frames_per_pulse (frame_rate)) + frame();
287 return minute_to_frame (time_at_pulse (p - pulse()), frame_rate) + frame();
295 Tt----|-----------------*|
296 Ta----|--------------|* |
302 _______________|___|____
303 time a t (next tempo)
306 Duration in beats at time a is the integral of some Tempo function.
307 In our case, the Tempo function (Tempo at time t) is
310 with function constant
315 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
316 b(t) = T0(e^(ct) - 1) / c
318 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:
319 t(b) = log((c.b / T0) + 1) / c
321 The time t at which Tempo T occurs is a as above:
322 t(T) = log(T / T0) / c
324 The beat at which a Tempo T occurs is:
327 The Tempo at which beat b occurs is:
330 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
331 Our problem is that we usually don't know t.
332 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.
333 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
334 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
336 By substituting our expanded t as a in the c function above, our problem is reduced to:
337 c = T0 (e^(log (Ta / T0)) - 1) / b
339 Of course the word 'beat' has been left loosely defined above.
340 In music, a beat is defined by the musical pulse (which comes from the tempo)
341 and the meter in use at a particular time (how many pulse divisions there are in one bar).
342 It would be more accurate to substitute the work 'pulse' for 'beat' above.
346 We can now store c for future time calculations.
347 If the following tempo section (the one that defines c in conjunction with this one)
348 is changed or moved, c is no longer valid.
350 The public methods are session-relative.
352 Most of this stuff is taken from this paper:
355 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
358 Zurich University of Arts
359 Institute for Computer Music and Sound Technology
361 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
366 compute this ramp's function constant using the end tempo (in whole pulses per minute)
367 and duration (pulses into global start) of some later tempo section.
370 TempoSection::compute_c_func_pulse (const double& end_bpm, const double& end_pulse, const framecnt_t& frame_rate)
372 double const log_tempo_ratio = log (end_bpm / pulses_per_minute());
373 return pulses_per_minute() * (expm1 (log_tempo_ratio)) / (end_pulse - pulse());
376 /* compute the function constant from some later tempo section, given tempo (whole pulses/min.) and distance (in frames) from session origin */
378 TempoSection::compute_c_func_frame (const double& end_bpm, const framepos_t& end_frame, const framecnt_t& frame_rate) const
380 return c_func (end_bpm, frame_to_minute (end_frame - frame(), frame_rate));
384 TempoSection::minute_to_frame (const double& time, const framecnt_t& frame_rate) const
386 return (framepos_t) floor ((time * 60.0 * frame_rate) + 0.5);
390 TempoSection::frame_to_minute (const framepos_t& frame, const framecnt_t& frame_rate) const
392 return (frame / (double) frame_rate) / 60.0;
395 /* position function */
397 TempoSection::a_func (double end_ppm, double c_func) const
399 return log (end_ppm / pulses_per_minute()) / c_func;
402 /*function constant*/
404 TempoSection::c_func (double end_ppm, double end_time) const
406 return log (end_ppm / pulses_per_minute()) / end_time;
409 /* tempo in ppm at time in minutes */
411 TempoSection::pulse_tempo_at_time (const double& time) const
413 return exp (_c_func * time) * pulses_per_minute();
416 /* time in minutes at tempo in ppm */
418 TempoSection::time_at_pulse_tempo (const double& pulse_tempo) const
420 return log (pulse_tempo / pulses_per_minute()) / _c_func;
423 /* tick at tempo in ppm */
425 TempoSection::pulse_at_pulse_tempo (const double& pulse_tempo) const
427 return (pulse_tempo - pulses_per_minute()) / _c_func;
430 /* tempo in ppm at tick */
432 TempoSection::pulse_tempo_at_pulse (const double& pulse) const
434 return (pulse * _c_func) + pulses_per_minute();
437 /* pulse at time in minutes */
439 TempoSection::pulse_at_time (const double& time) const
441 return expm1 (_c_func * time) * (pulses_per_minute() / _c_func);
444 /* time in minutes at pulse */
446 TempoSection::time_at_pulse (const double& pulse) const
448 return log1p ((_c_func * pulse) / pulses_per_minute()) / _c_func;
451 /***********************************************************************/
453 const string MeterSection::xml_state_node_name = "Meter";
455 MeterSection::MeterSection (const XMLNode& node)
456 : MetricSection (0.0, 0, MusicTime), Meter (TempoMap::default_meter())
458 XMLProperty const * prop;
463 framepos_t frame = 0;
464 pair<double, BBT_Time> start;
466 if ((prop = node.property ("start")) != 0) {
467 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
471 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
473 /* legacy session - start used to be in bbt*/
474 info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
479 if ((prop = node.property ("pulse")) != 0) {
480 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
481 error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
486 if ((prop = node.property ("beat")) != 0) {
487 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
488 error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
494 if ((prop = node.property ("bbt")) == 0) {
495 warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
496 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
500 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
501 throw failed_constructor();
507 if ((prop = node.property ("frame")) != 0) {
508 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
509 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
515 /* beats-per-bar is old; divisions-per-bar is new */
517 if ((prop = node.property ("divisions-per-bar")) == 0) {
518 if ((prop = node.property ("beats-per-bar")) == 0) {
519 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
520 throw failed_constructor();
523 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
524 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
525 throw failed_constructor();
528 if ((prop = node.property ("note-type")) == 0) {
529 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
530 throw failed_constructor();
532 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
533 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
534 throw failed_constructor();
537 if ((prop = node.property ("movable")) == 0) {
538 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
539 throw failed_constructor();
542 set_movable (string_is_affirmative (prop->value()));
544 if ((prop = node.property ("lock-style")) == 0) {
545 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
547 set_position_lock_style (MusicTime);
549 set_position_lock_style (AudioTime);
552 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
557 MeterSection::get_state() const
559 XMLNode *root = new XMLNode (xml_state_node_name);
563 snprintf (buf, sizeof (buf), "%lf", pulse());
564 root->add_property ("pulse", buf);
565 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
569 root->add_property ("bbt", buf);
570 snprintf (buf, sizeof (buf), "%lf", beat());
571 root->add_property ("beat", buf);
572 snprintf (buf, sizeof (buf), "%f", _note_type);
573 root->add_property ("note-type", buf);
574 snprintf (buf, sizeof (buf), "%li", frame());
575 root->add_property ("frame", buf);
576 root->add_property ("lock-style", enum_2_string (position_lock_style()));
577 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
578 root->add_property ("divisions-per-bar", buf);
579 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
580 root->add_property ("movable", buf);
585 /***********************************************************************/
589 Tempo can be thought of as a source of the musical pulse.
590 Meters divide that pulse into measures and beats.
591 Tempo pulses can be divided to be in sympathy with the meter, but this does not affect the beat
592 at any particular time.
593 Note that Tempo::beats_per_minute() has nothing to do with musical beats.
594 It should rather be thought of as tempo note divisions per minute.
596 TempoSections, which are nice to think of in whole pulses per minute,
597 and MeterSecions which divide tempo pulses into measures (via divisions_per_bar)
598 and beats (via note_divisor) are used to form a tempo map.
599 TempoSections and MeterSections may be locked to either audio or music (position lock style).
600 We construct the tempo map by first using the frame or pulse position (depending on position lock style) of each tempo.
601 We then use this pulse/frame layout to find the beat & pulse or frame position of each meter (again depending on lock style).
603 Having done this, we can now find any one of tempo, beat, frame or pulse if a beat, frame, pulse or tempo is known.
605 The first tempo and first meter are special. they must move together, and must be locked to audio.
606 Audio locked tempos which lie before the first meter are made inactive.
607 They will be re-activated if the first meter is again placed before them.
609 Both tempos and meters have a pulse position and a frame position.
610 Meters also have a beat position, which is always 0.0 for the first meter.
612 A tempo locked to music is locked to musical pulses.
613 A meter locked to music is locked to beats.
615 Recomputing the tempo map is the process where the 'missing' position
616 (tempo pulse or meter pulse & beat in the case of AudioTime, frame for MusicTime) is calculated.
618 It is important to keep the _metrics in an order that makes sense.
619 Because ramped MusicTime and AudioTime tempos can interact with each other,
620 reordering is frequent. Care must be taken to keep _metrics in a solved state.
621 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
623 struct MetricSectionSorter {
624 bool operator() (const MetricSection* a, const MetricSection* b) {
625 return a->pulse() < b->pulse();
629 struct MetricSectionFrameSorter {
630 bool operator() (const MetricSection* a, const MetricSection* b) {
631 return a->frame() < b->frame();
635 TempoMap::TempoMap (framecnt_t fr)
638 BBT_Time start (1, 1, 0);
640 TempoSection *t = new TempoSection (0.0, 0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Constant, AudioTime);
641 MeterSection *m = new MeterSection (0.0, 0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime);
643 t->set_movable (false);
644 m->set_movable (false);
646 /* note: frame time is correct (zero) for both of these */
648 _metrics.push_back (t);
649 _metrics.push_back (m);
653 TempoMap::~TempoMap ()
655 Metrics::const_iterator d = _metrics.begin();
656 while (d != _metrics.end()) {
664 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
666 bool removed = false;
669 Glib::Threads::RWLock::WriterLock lm (lock);
670 if ((removed = remove_tempo_locked (tempo))) {
671 if (complete_operation) {
672 recompute_map (_metrics);
677 if (removed && complete_operation) {
678 PropertyChanged (PropertyChange ());
683 TempoMap::remove_tempo_locked (const TempoSection& tempo)
687 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
688 if (dynamic_cast<TempoSection*> (*i) != 0) {
689 if (tempo.frame() == (*i)->frame()) {
690 if ((*i)->movable()) {
703 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
705 bool removed = false;
708 Glib::Threads::RWLock::WriterLock lm (lock);
709 if ((removed = remove_meter_locked (tempo))) {
710 if (complete_operation) {
711 recompute_map (_metrics);
716 if (removed && complete_operation) {
717 PropertyChanged (PropertyChange ());
722 TempoMap::remove_meter_locked (const MeterSection& meter)
726 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
728 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
729 if (meter.frame() == (*i)->frame()) {
730 if (t->locked_to_meter()) {
739 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
740 if (dynamic_cast<MeterSection*> (*i) != 0) {
741 if (meter.frame() == (*i)->frame()) {
742 if ((*i)->movable()) {
755 TempoMap::do_insert (MetricSection* section)
757 bool need_add = true;
758 /* we only allow new meters to be inserted on beat 1 of an existing
762 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
764 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
766 pair<double, BBT_Time> corrected = make_pair (m->pulse(), m->bbt());
767 corrected.second.beats = 1;
768 corrected.second.ticks = 0;
769 corrected.first = bbt_to_beats_locked (_metrics, corrected.second);
770 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
771 m->bbt(), corrected.second) << endmsg;
772 //m->set_pulse (corrected);
776 /* Look for any existing MetricSection that is of the same type and
777 in the same bar as the new one, and remove it before adding
778 the new one. Note that this means that if we find a matching,
779 existing section, we can break out of the loop since we're
780 guaranteed that there is only one such match.
783 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
785 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
786 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
787 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
788 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
790 if (tempo && insert_tempo) {
793 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
794 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
796 if (!tempo->movable()) {
798 /* can't (re)move this section, so overwrite
799 * its data content (but not its properties as
803 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
804 (*i)->set_position_lock_style (AudioTime);
806 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
807 t->set_type (insert_tempo->type());
817 } else if (meter && insert_meter) {
821 bool const ipm = insert_meter->position_lock_style() == MusicTime;
823 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
825 if (!meter->movable()) {
827 /* can't (re)move this section, so overwrite
828 * its data content (but not its properties as
832 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
833 (*i)->set_position_lock_style (AudioTime);
843 /* non-matching types, so we don't care */
847 /* Add the given MetricSection, if we didn't just reset an existing
852 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
853 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
856 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
857 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
860 bool const ipm = insert_meter->position_lock_style() == MusicTime;
861 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
866 } else if (insert_tempo) {
867 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
868 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
871 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
872 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
879 _metrics.insert (i, section);
880 //dump (_metrics, std::cout);
885 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, TempoSection::Type type, PositionLockStyle pls)
887 const bool locked_to_meter = ts.locked_to_meter();
890 Glib::Threads::RWLock::WriterLock lm (lock);
891 TempoSection& first (first_tempo());
892 if (ts.frame() != first.frame()) {
893 remove_tempo_locked (ts);
894 add_tempo_locked (tempo, pulse, frame, type, pls, true, locked_to_meter);
896 first.set_type (type);
897 first.set_pulse (0.0);
898 first.set_frame (frame);
899 first.set_position_lock_style (AudioTime);
901 /* cannot move the first tempo section */
902 *static_cast<Tempo*>(&first) = tempo;
903 recompute_map (_metrics);
908 PropertyChanged (PropertyChange ());
912 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, ARDOUR::TempoSection::Type type, PositionLockStyle pls)
914 TempoSection* ts = 0;
916 Glib::Threads::RWLock::WriterLock lm (lock);
917 ts = add_tempo_locked (tempo, pulse, frame, type, pls, true);
921 PropertyChanged (PropertyChange ());
927 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, framepos_t frame
928 , TempoSection::Type type, PositionLockStyle pls, bool recompute, bool locked_to_meter)
930 TempoSection* t = new TempoSection (pulse, frame, tempo.beats_per_minute(), tempo.note_type(), type, pls);
931 t->set_locked_to_meter (locked_to_meter);
936 if (pls == AudioTime) {
937 solve_map_frame (_metrics, t, t->frame());
939 solve_map_pulse (_metrics, t, t->pulse());
941 recompute_meters (_metrics);
948 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, const framepos_t& frame, PositionLockStyle pls)
951 Glib::Threads::RWLock::WriterLock lm (lock);
952 const double beat = bbt_to_beats_locked (_metrics, where);
955 remove_meter_locked (ms);
956 add_meter_locked (meter, beat, where, frame, pls, true);
958 MeterSection& first (first_meter());
959 TempoSection& first_t (first_tempo());
960 /* cannot move the first meter section */
961 *static_cast<Meter*>(&first) = meter;
962 first.set_position_lock_style (AudioTime);
963 first.set_pulse (0.0);
964 first.set_frame (frame);
965 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
966 first.set_beat (beat);
967 first_t.set_frame (first.frame());
968 first_t.set_pulse (0.0);
969 first_t.set_position_lock_style (AudioTime);
971 recompute_map (_metrics);
973 PropertyChanged (PropertyChange ());
977 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, const framepos_t& frame, PositionLockStyle pls)
981 Glib::Threads::RWLock::WriterLock lm (lock);
982 m = add_meter_locked (meter, beat, where, frame, pls, true);
987 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
988 dump (_metrics, std::cerr);
992 PropertyChanged (PropertyChange ());
997 TempoMap::add_meter_locked (const Meter& meter, double beat, const Timecode::BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute)
999 const MeterSection& prev_m = meter_section_at_locked (_metrics, frame - 1);
1000 const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1002 if (pls == AudioTime) {
1003 /* add meter-locked tempo */
1004 add_tempo_locked (tempo_at_locked (_metrics, frame), pulse, frame, TempoSection::Ramp, AudioTime, true, true);
1007 MeterSection* new_meter = new MeterSection (pulse, frame, beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls);
1009 do_insert (new_meter);
1013 if (pls == AudioTime) {
1014 solve_map_frame (_metrics, new_meter, frame);
1016 solve_map_bbt (_metrics, new_meter, where);
1024 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1026 Tempo newtempo (beats_per_minute, note_type);
1029 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1030 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1035 Glib::Threads::RWLock::WriterLock lm (lock);
1036 *((Tempo*) t) = newtempo;
1037 recompute_map (_metrics);
1039 PropertyChanged (PropertyChange ());
1046 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1048 Tempo newtempo (beats_per_minute, note_type);
1051 TempoSection* first;
1052 Metrics::iterator i;
1054 /* find the TempoSection immediately preceding "where"
1057 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1059 if ((*i)->frame() > where) {
1065 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1078 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1088 Glib::Threads::RWLock::WriterLock lm (lock);
1089 /* cannot move the first tempo section */
1090 *((Tempo*)prev) = newtempo;
1091 recompute_map (_metrics);
1094 PropertyChanged (PropertyChange ());
1098 TempoMap::first_meter () const
1100 const MeterSection *m = 0;
1102 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1103 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1108 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1109 abort(); /*NOTREACHED*/
1114 TempoMap::first_meter ()
1116 MeterSection *m = 0;
1118 /* CALLER MUST HOLD LOCK */
1120 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1121 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1126 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1127 abort(); /*NOTREACHED*/
1132 TempoMap::first_tempo () const
1134 const TempoSection *t = 0;
1136 /* CALLER MUST HOLD LOCK */
1138 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1139 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1143 if (!t->movable()) {
1149 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1150 abort(); /*NOTREACHED*/
1155 TempoMap::first_tempo ()
1157 TempoSection *t = 0;
1159 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1160 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1164 if (!t->movable()) {
1170 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1171 abort(); /*NOTREACHED*/
1175 TempoMap::recompute_tempos (Metrics& metrics)
1177 TempoSection* prev_t = 0;
1179 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1182 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1186 if (!t->movable()) {
1194 if (t->position_lock_style() == AudioTime) {
1195 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1196 if (!t->locked_to_meter()) {
1197 t->set_pulse (prev_t->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
1201 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1202 t->set_frame (prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
1209 prev_t->set_c_func (0.0);
1212 /* tempos must be positioned correctly.
1213 the current approach is to use a meter's bbt time as its base position unit.
1214 this means that a meter's beat may change, but its bbt may not.
1215 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1216 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1219 TempoMap::recompute_meters (Metrics& metrics)
1221 MeterSection* meter = 0;
1222 MeterSection* prev_m = 0;
1224 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1225 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1226 if (meter->position_lock_style() == AudioTime) {
1228 pair<double, BBT_Time> b_bbt;
1229 TempoSection* meter_locked_tempo = 0;
1230 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1232 if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
1233 if ((t->locked_to_meter() || !t->movable()) && t->frame() == meter->frame()) {
1234 meter_locked_tempo = t;
1241 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1242 if (beats + prev_m->beat() != meter->beat()) {
1243 /* reordering caused a bbt change */
1244 b_bbt = make_pair (beats + prev_m->beat()
1245 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1246 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1248 } else if (meter->movable()) {
1249 b_bbt = make_pair (meter->beat(), meter->bbt());
1250 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1253 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1255 if (meter_locked_tempo) {
1256 meter_locked_tempo->set_pulse (pulse);
1258 meter->set_beat (b_bbt);
1259 meter->set_pulse (pulse);
1264 pair<double, BBT_Time> new_beat;
1266 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1267 if (beats + prev_m->beat() != meter->beat()) {
1268 /* reordering caused a bbt change */
1269 new_beat = make_pair (beats + prev_m->beat()
1270 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1272 new_beat = make_pair (beats + prev_m->beat(), meter->bbt());
1274 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1276 /* shouldn't happen - the first is audio-locked */
1277 pulse = pulse_at_beat_locked (metrics, meter->beat());
1278 new_beat = make_pair (meter->beat(), meter->bbt());
1281 meter->set_beat (new_beat);
1282 meter->set_pulse (pulse);
1283 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1292 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1294 /* CALLER MUST HOLD WRITE LOCK */
1298 /* we will actually stop once we hit
1305 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1308 /* silly call from Session::process() during startup
1313 recompute_tempos (metrics);
1314 recompute_meters (metrics);
1318 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1320 Glib::Threads::RWLock::ReaderLock lm (lock);
1321 TempoMetric m (first_meter(), first_tempo());
1323 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1324 at something, because we insert the default tempo and meter during
1325 TempoMap construction.
1327 now see if we can find better candidates.
1330 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1332 if ((*i)->frame() > frame) {
1346 /* XX meters only */
1348 TempoMap::metric_at (BBT_Time bbt) const
1350 Glib::Threads::RWLock::ReaderLock lm (lock);
1351 TempoMetric m (first_meter(), first_tempo());
1353 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1354 at something, because we insert the default tempo and meter during
1355 TempoMap construction.
1357 now see if we can find better candidates.
1360 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1362 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1363 BBT_Time section_start (mw->bbt());
1365 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1377 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1379 MeterSection* prev_m = 0;
1381 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1383 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1384 if (prev_m && m->beat() > beat) {
1391 double const ret = prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1396 TempoMap::pulse_at_beat (const double& beat) const
1398 Glib::Threads::RWLock::ReaderLock lm (lock);
1399 return pulse_at_beat_locked (_metrics, beat);
1403 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1405 MeterSection* prev_m = 0;
1407 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1409 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1410 if (prev_m && m->pulse() > pulse) {
1411 if (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > m->beat()) {
1419 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1424 TempoMap::beat_at_pulse (const double& pulse) const
1426 Glib::Threads::RWLock::ReaderLock lm (lock);
1427 return beat_at_pulse_locked (_metrics, pulse);
1430 /* tempo section based */
1432 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1434 /* HOLD (at least) THE READER LOCK */
1435 TempoSection* prev_t = 0;
1437 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1439 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1443 if (prev_t && t->frame() > frame) {
1444 /*the previous ts is the one containing the frame */
1445 const double ret = prev_t->pulse_at_frame (frame, _frame_rate);
1452 /* treated as constant for this ts */
1453 const double pulses_in_section = (frame - prev_t->frame()) / prev_t->frames_per_pulse (_frame_rate);
1455 return pulses_in_section + prev_t->pulse();
1459 TempoMap::pulse_at_frame (const framecnt_t& frame) const
1461 Glib::Threads::RWLock::ReaderLock lm (lock);
1462 return pulse_at_frame_locked (_metrics, frame);
1465 /* tempo section based */
1467 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1469 /* HOLD THE READER LOCK */
1471 const TempoSection* prev_t = 0;
1473 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1476 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1480 if (prev_t && t->pulse() > pulse) {
1481 return prev_t->frame_at_pulse (pulse, _frame_rate);
1487 /* must be treated as constant, irrespective of _type */
1488 double const pulses_in_section = pulse - prev_t->pulse();
1489 double const dtime = pulses_in_section * prev_t->frames_per_pulse (_frame_rate);
1491 framecnt_t const ret = (framecnt_t) floor (dtime) + prev_t->frame();
1497 TempoMap::frame_at_pulse (const double& pulse) const
1499 Glib::Threads::RWLock::ReaderLock lm (lock);
1500 return frame_at_pulse_locked (_metrics, pulse);
1503 /* meter section based */
1505 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1507 const TempoSection& ts = tempo_section_at_locked (metrics, frame);
1508 MeterSection* prev_m = 0;
1509 MeterSection* next_m = 0;
1511 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1513 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1514 if (prev_m && m->frame() > frame) {
1521 if (frame < prev_m->frame()) {
1524 const double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
1526 if (next_m && next_m->beat() < beat) {
1527 return next_m->beat();
1534 TempoMap::beat_at_frame (const framecnt_t& frame) const
1536 Glib::Threads::RWLock::ReaderLock lm (lock);
1537 return beat_at_frame_locked (_metrics, frame);
1540 /* meter section based */
1542 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1544 const TempoSection& prev_t = tempo_section_at_beat_locked (metrics, beat);
1545 MeterSection* prev_m = 0;
1547 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1549 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1550 if (prev_m && m->beat() > beat) {
1557 return prev_t.frame_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse(), _frame_rate);
1561 TempoMap::frame_at_beat (const double& beat) const
1563 Glib::Threads::RWLock::ReaderLock lm (lock);
1564 return frame_at_beat_locked (_metrics, beat);
1568 TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1570 /* CALLER HOLDS READ LOCK */
1572 MeterSection* prev_m = 0;
1574 /* because audio-locked meters have 'fake' integral beats,
1575 there is no pulse offset here.
1577 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1579 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1581 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1582 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1590 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1591 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1592 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1598 TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
1600 Glib::Threads::RWLock::ReaderLock lm (lock);
1601 return bbt_to_beats_locked (_metrics, bbt);
1605 TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
1607 /* CALLER HOLDS READ LOCK */
1608 MeterSection* prev_m = 0;
1609 const double beats = max (0.0, b);
1611 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1612 MeterSection* m = 0;
1614 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1616 if (m->beat() > beats) {
1617 /* this is the meter after the one our beat is on*/
1626 const double beats_in_ms = beats - prev_m->beat();
1627 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1628 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1629 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1630 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1634 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1635 ret.beats = (uint32_t) floor (remaining_beats);
1636 ret.bars = total_bars;
1638 /* 0 0 0 to 1 1 0 - based mapping*/
1642 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1644 ret.ticks -= BBT_Time::ticks_per_beat;
1647 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1656 TempoMap::beats_to_bbt (const double& beats)
1658 Glib::Threads::RWLock::ReaderLock lm (lock);
1659 return beats_to_bbt_locked (_metrics, beats);
1663 TempoMap::pulse_to_bbt (const double& pulse)
1665 Glib::Threads::RWLock::ReaderLock lm (lock);
1666 MeterSection* prev_m = 0;
1668 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1669 MeterSection* m = 0;
1671 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1674 double const pulses_to_m = m->pulse() - prev_m->pulse();
1675 if (prev_m->pulse() + pulses_to_m > pulse) {
1676 /* this is the meter after the one our beat is on*/
1685 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1686 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1687 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1688 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1689 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1693 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1694 ret.beats = (uint32_t) floor (remaining_beats);
1695 ret.bars = total_bars;
1697 /* 0 0 0 to 1 1 0 mapping*/
1701 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1703 ret.ticks -= BBT_Time::ticks_per_beat;
1706 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1715 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1722 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1725 Glib::Threads::RWLock::ReaderLock lm (lock);
1726 const double beat = beat_at_frame_locked (_metrics, frame);
1728 bbt = beats_to_bbt_locked (_metrics, beat);
1731 /* meter section based */
1733 TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
1735 /* HOLD THE READER LOCK */
1737 const framepos_t ret = frame_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt));
1742 TempoMap::frame_time (const BBT_Time& bbt)
1745 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1749 if (bbt.beats < 1) {
1750 throw std::logic_error ("beats are counted from one");
1752 Glib::Threads::RWLock::ReaderLock lm (lock);
1754 return frame_time_locked (_metrics, bbt);
1758 TempoMap::check_solved (const Metrics& metrics, bool by_frame) const
1760 TempoSection* prev_t = 0;
1761 MeterSection* prev_m = 0;
1763 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1766 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1771 if ((by_frame && t->frame() < prev_t->frame()) || (!by_frame && t->pulse() < prev_t->pulse())) {
1775 if (t->frame() == prev_t->frame()) {
1779 /* precision check ensures pulses and frames align.*/
1780 if (t->frame() != prev_t->frame_at_pulse (t->pulse(), _frame_rate)) {
1781 if (!t->locked_to_meter()) {
1789 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1790 if (prev_m && m->position_lock_style() == AudioTime) {
1791 TempoSection* t = const_cast<TempoSection*>(&tempo_section_at_locked (metrics, m->frame() - 1));
1792 const double nascent_m_pulse = ((m->beat() - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse();
1793 const framepos_t nascent_m_frame = t->frame_at_pulse (nascent_m_pulse, _frame_rate);
1795 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
1809 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
1811 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1813 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1814 if (!t->movable()) {
1815 t->set_active (true);
1818 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
1819 t->set_active (false);
1821 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
1822 t->set_active (true);
1823 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
1832 TempoMap::solve_map_frame (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
1834 TempoSection* prev_t = 0;
1835 TempoSection* section_prev = 0;
1836 framepos_t first_m_frame = 0;
1838 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1840 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1841 if (!m->movable()) {
1842 first_m_frame = m->frame();
1847 if (section->movable() && frame <= first_m_frame) {
1851 section->set_active (true);
1852 section->set_frame (frame);
1854 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1856 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1863 section_prev = prev_t;
1866 if (t->position_lock_style() == MusicTime) {
1867 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1868 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1870 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1871 if (!t->locked_to_meter()) {
1872 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1881 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
1882 if (!section->locked_to_meter()) {
1883 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
1887 if (section->position_lock_style() == MusicTime) {
1888 /* we're setting the frame */
1889 section->set_position_lock_style (AudioTime);
1890 recompute_tempos (imaginary);
1891 section->set_position_lock_style (MusicTime);
1893 recompute_tempos (imaginary);
1896 if (check_solved (imaginary, true)) {
1900 MetricSectionFrameSorter fcmp;
1901 imaginary.sort (fcmp);
1902 if (section->position_lock_style() == MusicTime) {
1903 /* we're setting the frame */
1904 section->set_position_lock_style (AudioTime);
1905 recompute_tempos (imaginary);
1906 section->set_position_lock_style (MusicTime);
1908 recompute_tempos (imaginary);
1911 if (check_solved (imaginary, true)) {
1919 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
1921 TempoSection* prev_t = 0;
1922 TempoSection* section_prev = 0;
1924 section->set_pulse (pulse);
1926 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1928 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1932 if (!t->movable()) {
1939 section_prev = prev_t;
1942 if (t->position_lock_style() == MusicTime) {
1943 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1944 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1946 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1947 if (!t->locked_to_meter()) {
1948 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1956 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
1957 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
1960 if (section->position_lock_style() == AudioTime) {
1961 /* we're setting the pulse */
1962 section->set_position_lock_style (MusicTime);
1963 recompute_tempos (imaginary);
1964 section->set_position_lock_style (AudioTime);
1966 recompute_tempos (imaginary);
1969 if (check_solved (imaginary, false)) {
1973 MetricSectionSorter cmp;
1974 imaginary.sort (cmp);
1975 if (section->position_lock_style() == AudioTime) {
1976 /* we're setting the pulse */
1977 section->set_position_lock_style (MusicTime);
1978 recompute_tempos (imaginary);
1979 section->set_position_lock_style (AudioTime);
1981 recompute_tempos (imaginary);
1984 if (check_solved (imaginary, false)) {
1992 TempoMap::solve_map_frame (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
1994 /* disallow moving first meter past any subsequent one, and any movable meter before the first one */
1995 const MeterSection* other = &meter_section_at_locked (imaginary, frame);
1996 if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->frame() >= frame)) {
2000 if (!section->movable()) {
2001 /* lock the first tempo to our first meter */
2002 if (!set_active_tempos (imaginary, frame)) {
2007 /* it would make sense to bail out if there is no audio-locked meter,
2008 however it may be desirable to move a music-locked meter by frame at some point.
2010 TempoSection* meter_locked_tempo = 0;
2011 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2013 if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
2014 if ((t->locked_to_meter() || !t->movable()) && t->frame() == section->frame()) {
2015 meter_locked_tempo = t;
2021 MeterSection* prev_m = 0;
2023 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2025 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2027 if (prev_m && section->movable()) {
2028 const double beats = (pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor();
2029 if (beats + prev_m->beat() < section->beat()) {
2030 /* disallow position change if it will alter our beat
2031 we allow tempo changes to do this in recompute_meters().
2032 blocking this is an option, but i'm not convinced that
2033 this is what the user would actually want.
2034 here we set the frame/pulse corresponding to its musical position.
2037 if (meter_locked_tempo) {
2039 bool solved = false;
2040 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2041 const double new_pulse = ((section->beat() - prev_m->beat())
2042 / prev_m->note_divisor()) + prev_m->pulse();
2043 const framepos_t smallest_frame = frame_at_pulse_locked (future_map, new_pulse);
2044 if ((solved = solve_map_frame (future_map, tempo_copy, smallest_frame))) {
2045 meter_locked_tempo->set_pulse (new_pulse);
2046 solve_map_frame (imaginary, meter_locked_tempo, smallest_frame);
2047 section->set_frame (smallest_frame);
2048 section->set_pulse (new_pulse);
2053 Metrics::const_iterator d = future_map.begin();
2054 while (d != future_map.end()) {
2065 if (meter_locked_tempo) {
2067 bool solved = false;
2069 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2070 MeterSection* meter_copy = const_cast<MeterSection*> (&meter_section_at_locked (future_map, section->frame()));
2071 meter_copy->set_frame (frame);
2073 if ((solved = solve_map_frame (future_map, tempo_copy, frame))) {
2074 section->set_frame (frame);
2075 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2076 / prev_m->note_divisor()) + prev_m->pulse());
2077 solve_map_frame (imaginary, meter_locked_tempo, frame);
2082 Metrics::const_iterator d = future_map.begin();
2083 while (d != future_map.end()) {
2094 /* not movable (first meter atm) */
2095 if (meter_locked_tempo) {
2097 bool solved = false;
2098 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2100 tempo_copy->set_frame (frame);
2101 tempo_copy->set_pulse (0.0);
2103 if ((solved = solve_map_frame (future_map, tempo_copy, frame))) {
2104 section->set_frame (frame);
2105 meter_locked_tempo->set_frame (frame);
2106 meter_locked_tempo->set_pulse (0.0);
2107 solve_map_frame (imaginary, meter_locked_tempo, frame);
2112 Metrics::const_iterator d = future_map.begin();
2113 while (d != future_map.end()) {
2125 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2126 section->set_beat (b_bbt);
2127 section->set_pulse (0.0);
2137 MetricSectionFrameSorter fcmp;
2138 imaginary.sort (fcmp);
2139 if (section->position_lock_style() == MusicTime) {
2140 /* we're setting the frame */
2141 section->set_position_lock_style (AudioTime);
2142 recompute_meters (imaginary);
2143 section->set_position_lock_style (MusicTime);
2145 recompute_meters (imaginary);
2152 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2154 /* disallow setting section to an existing meter's bbt */
2155 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2157 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2158 if (m != section && m->bbt().bars == when.bars) {
2164 MeterSection* prev_m = 0;
2165 MeterSection* section_prev = 0;
2167 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2169 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2170 pair<double, BBT_Time> b_bbt;
2171 double new_pulse = 0.0;
2173 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2174 section_prev = prev_m;
2175 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2176 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2177 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2179 section->set_beat (b_bbt);
2180 section->set_pulse (pulse);
2181 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2186 if (m->position_lock_style() == AudioTime) {
2187 TempoSection* meter_locked_tempo = 0;
2188 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2190 if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
2191 if ((t->locked_to_meter() || !t->movable()) && t->frame() == m->frame()) {
2192 meter_locked_tempo = t;
2199 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2201 if (beats + prev_m->beat() != m->beat()) {
2202 /* tempo/ meter change caused a change in beat (bar). */
2203 b_bbt = make_pair (beats + prev_m->beat()
2204 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2205 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2206 } else if (m->movable()) {
2207 b_bbt = make_pair (m->beat(), m->bbt());
2208 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2211 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2213 if (meter_locked_tempo) {
2214 meter_locked_tempo->set_pulse (new_pulse);
2215 recompute_tempos (imaginary);
2217 m->set_beat (b_bbt);
2218 m->set_pulse (new_pulse);
2222 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2223 if (beats + prev_m->beat() != m->beat()) {
2224 /* tempo/ meter change caused a change in beat (bar). */
2225 b_bbt = make_pair (beats + prev_m->beat()
2226 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2228 b_bbt = make_pair (beats + prev_m->beat()
2231 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2232 m->set_beat (b_bbt);
2233 m->set_pulse (new_pulse);
2234 m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2241 if (!section_prev) {
2243 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
2244 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2245 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
2247 section->set_beat (b_bbt);
2248 section->set_pulse (pulse);
2249 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2252 MetricSectionSorter cmp;
2253 imaginary.sort (cmp);
2255 if (section->position_lock_style() == AudioTime) {
2256 /* we're setting the pulse */
2257 section->set_position_lock_style (MusicTime);
2258 recompute_meters (imaginary);
2259 section->set_position_lock_style (AudioTime);
2261 recompute_meters (imaginary);
2267 /** places a copy of _metrics into copy and returns a pointer
2268 * to section's equivalent in copy.
2271 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
2273 TempoSection* ret = 0;
2275 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2278 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2280 ret = new TempoSection (*t);
2281 copy.push_back (ret);
2285 TempoSection* cp = new TempoSection (*t);
2286 copy.push_back (cp);
2288 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2289 MeterSection* cp = new MeterSection (*m);
2290 copy.push_back (cp);
2298 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
2300 MeterSection* ret = 0;
2302 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2305 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2306 TempoSection* cp = new TempoSection (*t);
2307 copy.push_back (cp);
2310 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2312 ret = new MeterSection (*m);
2313 copy.push_back (ret);
2316 MeterSection* cp = new MeterSection (*m);
2317 copy.push_back (cp);
2325 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2328 TempoSection* tempo_copy = 0;
2331 Glib::Threads::RWLock::ReaderLock lm (lock);
2332 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
2338 const double beat = bbt_to_beats_locked (copy, bbt);
2339 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_beat_locked (copy, beat));
2341 Metrics::const_iterator d = copy.begin();
2342 while (d != copy.end()) {
2351 * 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,
2352 * taking any possible reordering as a consequence of this into account.
2353 * @param section - the section to be altered
2354 * @param bpm - the new Tempo
2355 * @param bbt - the bbt where the altered tempo will fall
2356 * @return returns - the position in frames where the new tempo section will lie.
2359 TempoMap::predict_tempo_frame (TempoSection* section, const BBT_Time& bbt)
2361 Glib::Threads::RWLock::ReaderLock lm (lock);
2364 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
2368 const double beat = bbt_to_beats_locked (future_map, bbt);
2370 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
2371 ret = tempo_copy->frame();
2373 ret = section->frame();
2376 Metrics::const_iterator d = future_map.begin();
2377 while (d != future_map.end()) {
2385 TempoMap::predict_tempo_pulse (TempoSection* section, const framepos_t& frame)
2387 Glib::Threads::RWLock::ReaderLock lm (lock);
2390 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
2392 if (solve_map_frame (future_map, tempo_copy, frame)) {
2393 ret = tempo_copy->pulse();
2395 ret = section->pulse();
2398 Metrics::const_iterator d = future_map.begin();
2399 while (d != future_map.end()) {
2407 TempoMap::gui_move_tempo_frame (TempoSection* ts, const framepos_t& frame)
2411 Glib::Threads::RWLock::WriterLock lm (lock);
2412 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2413 if (solve_map_frame (future_map, tempo_copy, frame)) {
2414 solve_map_frame (_metrics, ts, frame);
2415 recompute_meters (_metrics);
2419 Metrics::const_iterator d = future_map.begin();
2420 while (d != future_map.end()) {
2425 MetricPositionChanged (); // Emit Signal
2429 TempoMap::gui_move_tempo_beat (TempoSection* ts, const double& beat)
2433 Glib::Threads::RWLock::WriterLock lm (lock);
2434 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2435 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
2436 solve_map_pulse (_metrics, ts, pulse_at_beat_locked (_metrics, beat));
2437 recompute_meters (_metrics);
2441 Metrics::const_iterator d = future_map.begin();
2442 while (d != future_map.end()) {
2447 MetricPositionChanged (); // Emit Signal
2451 TempoMap::gui_move_meter_frame (MeterSection* ms, const framepos_t& frame)
2455 Glib::Threads::RWLock::WriterLock lm (lock);
2456 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2457 if (solve_map_frame (future_map, copy, frame)) {
2458 solve_map_frame (_metrics, ms, frame);
2459 recompute_tempos (_metrics);
2463 Metrics::const_iterator d = future_map.begin();
2464 while (d != future_map.end()) {
2469 MetricPositionChanged (); // Emit Signal
2473 TempoMap::gui_move_meter_bbt (MeterSection* ms, const Timecode::BBT_Time& bbt)
2477 Glib::Threads::RWLock::WriterLock lm (lock);
2478 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2479 if (solve_map_bbt (future_map, copy, bbt)) {
2480 solve_map_bbt (_metrics, ms, bbt);
2481 recompute_tempos (_metrics);
2485 Metrics::const_iterator d = future_map.begin();
2486 while (d != future_map.end()) {
2491 MetricPositionChanged (); // Emit Signal
2495 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2498 bool can_solve = false;
2500 Glib::Threads::RWLock::WriterLock lm (lock);
2501 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2502 tempo_copy->set_beats_per_minute (bpm.beats_per_minute());
2503 recompute_tempos (future_map);
2505 if (check_solved (future_map, true)) {
2506 ts->set_beats_per_minute (bpm.beats_per_minute());
2507 recompute_map (_metrics);
2512 Metrics::const_iterator d = future_map.begin();
2513 while (d != future_map.end()) {
2518 MetricPositionChanged (); // Emit Signal
2524 TempoMap::gui_dilate_tempo (MeterSection* ms, const framepos_t& frame)
2527 TempoSection* ts = 0;
2529 if (frame <= first_meter().frame()) {
2533 if (ms->position_lock_style() == AudioTime) {
2534 /* disabled for now due to faked tempo locked to meter pulse */
2539 Glib::Threads::RWLock::WriterLock lm (lock);
2540 ts = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, ms->frame() - 1));
2544 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
2545 TempoSection* prev_to_prev_t = 0;
2546 const frameoffset_t fr_off = frame - ms->frame();
2547 double new_bpm = 0.0;
2549 if (prev_t && prev_t->pulse() > 0.0) {
2550 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (future_map, prev_t->frame() - 1));
2553 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
2554 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
2556 double contribution = 0.0;
2557 frameoffset_t frame_contribution = 0;
2558 frameoffset_t prev_t_frame_contribution = fr_off;
2560 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2561 /* prev to prev_t's position will remain constant in terms of frame and pulse. lets use frames. */
2562 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (ms->frame() - prev_to_prev_t->frame());
2563 frame_contribution = contribution * (double) fr_off;
2564 prev_t_frame_contribution = fr_off - frame_contribution;
2567 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
2569 if (prev_t->position_lock_style() == MusicTime) {
2570 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2571 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2572 / (double) (ms->frame() + prev_t_frame_contribution - prev_t->frame()));
2575 /* prev to prev is irrelevant */
2576 const double meter_pulse = prev_t->pulse_at_frame (ms->frame(), _frame_rate);
2577 const double frame_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
2579 if (frame_pulse != prev_t->pulse()) {
2580 new_bpm = prev_t->beats_per_minute() * ((meter_pulse - prev_t->pulse()) / (frame_pulse - prev_t->pulse()));
2582 new_bpm = prev_t->beats_per_minute();
2587 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2588 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2589 / (double) (ms->frame() + prev_t_frame_contribution - prev_t->frame()));
2591 /* prev_to_prev_t is irrelevant */
2593 if (frame != prev_t->frame()) {
2594 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame()) / (double) (frame - prev_t->frame()));
2596 new_bpm = prev_t->beats_per_minute();
2600 } else if (prev_t->c_func() < 0.0) {
2601 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2602 new_bpm = prev_t->tempo_at_frame (prev_t->frame() + frame_contribution, _frame_rate) * (double) prev_t->note_type();
2604 /* prev_to_prev_t is irrelevant */
2605 new_bpm = prev_t->tempo_at_frame (prev_t->frame() + fr_off, _frame_rate) * (double) prev_t->note_type();
2608 } else if (prev_t->c_func() > 0.0) {
2609 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2610 new_bpm = prev_t->tempo_at_frame (prev_t->frame() - frame_contribution, _frame_rate) * (double) prev_t->note_type();
2612 /* prev_to_prev_t is irrelevant */
2613 new_bpm = prev_t->tempo_at_frame (prev_t->frame() - fr_off, _frame_rate) * (double) prev_t->note_type();
2617 /* limits - a bit clunky, but meh */
2618 const double diff = (prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type()) - prev_t->beats_per_minute();
2619 if (diff > -1.0 && diff < 1.0) {
2620 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2621 / (double) ((ms->frame() + prev_t_frame_contribution) - prev_t->frame()));
2624 prev_t->set_beats_per_minute (new_bpm);
2625 recompute_tempos (future_map);
2626 recompute_meters (future_map);
2628 if (check_solved (future_map, true)) {
2630 prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, ms->frame() - 1));
2631 prev_t->set_beats_per_minute (new_bpm);
2632 recompute_tempos (_metrics);
2634 if (ms->position_lock_style() == AudioTime) {
2635 ms->set_frame (frame);
2638 recompute_meters (_metrics);
2642 Metrics::const_iterator d = future_map.begin();
2643 while (d != future_map.end()) {
2648 MetricPositionChanged (); // Emit Signal
2652 TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame, const double& pulse)
2655 Ts (future prev_t) Tnext
2658 |----------|----------
2665 Glib::Threads::RWLock::WriterLock lm (lock);
2671 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
2672 TempoSection* prev_to_prev_t = 0;
2673 const frameoffset_t fr_off = end_frame - frame;
2675 if (prev_t && prev_t->pulse() > 0.0) {
2676 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (future_map, prev_t->frame() - 1));
2679 TempoSection* next_t = 0;
2680 for (Metrics::iterator i = future_map.begin(); i != future_map.end(); ++i) {
2681 TempoSection* t = 0;
2682 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2683 if (t->frame() > ts->frame()) {
2690 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
2691 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
2693 double contribution = 0.0;
2694 double start_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
2696 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2697 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
2700 frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
2701 double end_pulse = prev_t->pulse_at_frame (end_frame, _frame_rate);
2704 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
2706 if (prev_t->position_lock_style() == MusicTime) {
2707 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2709 new_bpm = prev_t->beats_per_minute() * ((frame - prev_to_prev_t->frame())
2710 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
2713 /* prev to prev is irrelevant */
2715 if (start_pulse != prev_t->pulse()) {
2716 new_bpm = prev_t->beats_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
2718 new_bpm = prev_t->beats_per_minute();
2723 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2724 new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame())
2725 / (double) ((frame + prev_t_frame_contribution) - prev_t->frame()));
2727 /* prev_to_prev_t is irrelevant */
2729 if (end_frame != prev_t->frame()) {
2730 new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
2732 new_bpm = prev_t->beats_per_minute();
2740 const framepos_t pulse_pos = prev_t->frame_at_pulse (pulse, _frame_rate);
2742 if (prev_to_prev_t) {
2744 frame_ratio = (((pulse_pos - fr_off) - prev_to_prev_t->frame()) / (double) ((pulse_pos) - prev_to_prev_t->frame()));
2745 pulse_ratio = ((start_pulse - prev_to_prev_t->pulse()) / (end_pulse - prev_to_prev_t->pulse()));
2748 frame_ratio = (((pulse_pos - fr_off) - prev_t->frame()) / (double) ((pulse_pos) - prev_t->frame()));
2749 pulse_ratio = (start_pulse / end_pulse);
2751 new_bpm = prev_t->beats_per_minute() * (pulse_ratio * frame_ratio);
2754 prev_t->set_beats_per_minute (new_bpm);
2755 recompute_tempos (future_map);
2756 recompute_meters (future_map);
2758 if (check_solved (future_map, true)) {
2759 ts->set_beats_per_minute (new_bpm);
2760 recompute_tempos (_metrics);
2761 recompute_meters (_metrics);
2765 Metrics::const_iterator d = future_map.begin();
2766 while (d != future_map.end()) {
2771 MetricPositionChanged (); // Emit Signal
2775 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2777 Glib::Threads::RWLock::ReaderLock lm (lock);
2779 const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2780 const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2781 const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2783 return frame_at_beat_locked (_metrics, total_beats);
2787 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2789 return round_to_type (fr, dir, Bar);
2793 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2795 return round_to_type (fr, dir, Beat);
2799 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2801 Glib::Threads::RWLock::ReaderLock lm (lock);
2802 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2803 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2804 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2806 ticks -= beats * BBT_Time::ticks_per_beat;
2809 /* round to next (or same iff dir == RoundUpMaybe) */
2811 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2813 if (mod == 0 && dir == RoundUpMaybe) {
2814 /* right on the subdivision, which is fine, so do nothing */
2816 } else if (mod == 0) {
2817 /* right on the subdivision, so the difference is just the subdivision ticks */
2818 ticks += ticks_one_subdivisions_worth;
2821 /* not on subdivision, compute distance to next subdivision */
2823 ticks += ticks_one_subdivisions_worth - mod;
2826 if (ticks >= BBT_Time::ticks_per_beat) {
2827 ticks -= BBT_Time::ticks_per_beat;
2829 } else if (dir < 0) {
2831 /* round to previous (or same iff dir == RoundDownMaybe) */
2833 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2835 if (difference == 0 && dir == RoundDownAlways) {
2836 /* right on the subdivision, but force-rounding down,
2837 so the difference is just the subdivision ticks */
2838 difference = ticks_one_subdivisions_worth;
2841 if (ticks < difference) {
2842 ticks = BBT_Time::ticks_per_beat - ticks;
2844 ticks -= difference;
2848 /* round to nearest */
2851 /* compute the distance to the previous and next subdivision */
2853 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2855 /* closer to the next subdivision, so shift forward */
2857 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2859 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2861 if (ticks > BBT_Time::ticks_per_beat) {
2863 ticks -= BBT_Time::ticks_per_beat;
2864 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2867 } else if (rem > 0) {
2869 /* closer to previous subdivision, so shift backward */
2873 /* can't go backwards past zero, so ... */
2876 /* step back to previous beat */
2878 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2879 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2881 ticks = lrint (ticks - rem);
2882 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2885 /* on the subdivision, do nothing */
2889 const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2895 TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num, RoundMode dir)
2897 if (sub_num == -1) {
2902 } else if (dir < 0) {
2906 const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2907 if ((double) when.beats > bpb / 2.0) {
2916 } else if (sub_num == 0) {
2917 const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2918 if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
2920 while ((double) when.beats > bpb) {
2922 when.beats -= (uint32_t) floor (bpb);
2930 const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
2933 /* round to next (or same iff dir == RoundUpMaybe) */
2935 uint32_t mod = when.ticks % ticks_one_subdivisions_worth;
2937 if (mod == 0 && dir == RoundUpMaybe) {
2938 /* right on the subdivision, which is fine, so do nothing */
2940 } else if (mod == 0) {
2941 /* right on the subdivision, so the difference is just the subdivision ticks */
2942 when.ticks += ticks_one_subdivisions_worth;
2945 /* not on subdivision, compute distance to next subdivision */
2947 when.ticks += ticks_one_subdivisions_worth - mod;
2950 if (when.ticks >= BBT_Time::ticks_per_beat) {
2951 when.ticks -= BBT_Time::ticks_per_beat;
2954 } else if (dir < 0) {
2955 /* round to previous (or same iff dir == RoundDownMaybe) */
2957 uint32_t difference = when.ticks % ticks_one_subdivisions_worth;
2959 if (difference == 0 && dir == RoundDownAlways) {
2960 /* right on the subdivision, but force-rounding down,
2961 so the difference is just the subdivision ticks */
2962 difference = ticks_one_subdivisions_worth;
2965 if (when.ticks < difference) {
2966 when.ticks = BBT_Time::ticks_per_beat - when.ticks;
2968 when.ticks -= difference;
2972 /* round to nearest */ double rem;
2973 if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
2974 /* closer to the next subdivision, so shift forward */
2976 when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
2978 if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
2980 when.ticks -= Timecode::BBT_Time::ticks_per_beat;
2983 } else if (rem > 0) {
2984 /* closer to previous subdivision, so shift backward */
2986 if (rem > when.ticks) {
2987 if (when.beats == 0) {
2988 /* can't go backwards past zero, so ... */
2990 /* step back to previous beat */
2992 when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
2994 when.ticks = when.ticks - rem;
3001 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
3003 Glib::Threads::RWLock::ReaderLock lm (lock);
3005 const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
3006 BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
3011 /* find bar previous to 'frame' */
3014 return frame_time_locked (_metrics, bbt);
3016 } else if (dir > 0) {
3017 /* find bar following 'frame' */
3021 return frame_time_locked (_metrics, bbt);
3023 /* true rounding: find nearest bar */
3024 framepos_t raw_ft = frame_time_locked (_metrics, bbt);
3027 framepos_t prev_ft = frame_time_locked (_metrics, bbt);
3029 framepos_t next_ft = frame_time_locked (_metrics, bbt);
3031 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
3042 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
3043 } else if (dir > 0) {
3044 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
3046 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
3055 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
3056 framepos_t lower, framepos_t upper)
3058 Glib::Threads::RWLock::ReaderLock lm (lock);
3059 int32_t cnt = ceil (beat_at_frame_locked (_metrics, lower));
3061 /* although the map handles negative beats, bbt doesn't. */
3065 while (pos < upper) {
3066 pos = frame_at_beat_locked (_metrics, cnt);
3067 const TempoSection tempo = tempo_section_at_locked (_metrics, pos);
3068 const MeterSection meter = meter_section_at_locked (_metrics, pos);
3069 const BBT_Time bbt = beats_to_bbt (cnt);
3070 points.push_back (BBTPoint (meter, tempo_at_locked (_metrics, pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
3076 TempoMap::tempo_section_at (framepos_t frame) const
3078 Glib::Threads::RWLock::ReaderLock lm (lock);
3079 return tempo_section_at_locked (_metrics, frame);
3083 TempoMap::tempo_section_at_locked (const Metrics& metrics, framepos_t frame) const
3085 Metrics::const_iterator i;
3086 TempoSection* prev = 0;
3088 for (i = metrics.begin(); i != metrics.end(); ++i) {
3091 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3095 if (prev && t->frame() > frame) {
3105 abort(); /*NOTREACHED*/
3112 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3114 TempoSection* prev_t = 0;
3115 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
3117 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3119 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3120 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
3131 TempoMap::tempo_section_at_pulse_locked (const Metrics& metrics, const double& pulse) const
3133 TempoSection* prev_t = 0;
3135 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3137 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3138 if (prev_t && t->pulse() > pulse) {
3148 /* don't use this to calculate length (the tempo is only correct for this frame).
3149 do that stuff based on the beat_at_frame and frame_at_beat api
3152 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
3154 Glib::Threads::RWLock::ReaderLock lm (lock);
3156 const TempoSection* ts_at = &tempo_section_at_locked (_metrics, frame);
3157 const TempoSection* ts_after = 0;
3158 Metrics::const_iterator i;
3160 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3163 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3167 if ((*i)->frame() > frame) {
3175 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
3177 /* must be treated as constant tempo */
3178 return ts_at->frames_per_beat (_frame_rate);
3182 TempoMap::tempo_at_locked (const Metrics& metrics, const framepos_t& frame) const
3184 TempoSection* prev_t = 0;
3186 Metrics::const_iterator i;
3188 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3190 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3194 if ((prev_t) && t->frame() > frame) {
3195 /* t is the section past frame */
3196 const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
3197 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
3204 const double ret = prev_t->beats_per_minute();
3205 const Tempo ret_tempo (ret, prev_t->note_type ());
3211 TempoMap::tempo_at (const framepos_t& frame) const
3213 Glib::Threads::RWLock::ReaderLock lm (lock);
3214 return tempo_at_locked (_metrics, frame);
3218 TempoMap::meter_section_at_locked (const Metrics& metrics, framepos_t frame) const
3220 Metrics::const_iterator i;
3221 MeterSection* prev = 0;
3223 for (i = metrics.begin(); i != metrics.end(); ++i) {
3226 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
3228 if (prev && (*i)->frame() > frame) {
3238 abort(); /*NOTREACHED*/
3246 TempoMap::meter_section_at (framepos_t frame) const
3248 Glib::Threads::RWLock::ReaderLock lm (lock);
3249 return meter_section_at_locked (_metrics, frame);
3253 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3255 MeterSection* prev_m = 0;
3257 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3259 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
3260 if (prev_m && m->beat() > beat) {
3271 TempoMap::meter_section_at_beat (double beat) const
3273 Glib::Threads::RWLock::ReaderLock lm (lock);
3274 return meter_section_at_beat_locked (_metrics, beat);
3278 TempoMap::meter_at (framepos_t frame) const
3280 TempoMetric m (metric_at (frame));
3285 TempoMap::fix_legacy_session ()
3287 MeterSection* prev_m = 0;
3288 TempoSection* prev_t = 0;
3290 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3294 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3295 if (!m->movable()) {
3296 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3300 m->set_position_lock_style (AudioTime);
3305 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
3306 + (m->bbt().beats - 1)
3307 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
3309 m->set_beat (start);
3310 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
3311 + (m->bbt().beats - 1)
3312 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
3313 m->set_pulse (start_beat / prev_m->note_divisor());
3316 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3322 if (!t->movable()) {
3325 t->set_position_lock_style (AudioTime);
3331 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
3332 + (t->legacy_bbt().beats - 1)
3333 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
3335 t->set_pulse (beat / prev_m->note_divisor());
3337 /* really shouldn't happen but.. */
3338 t->set_pulse (beat / 4.0);
3347 TempoMap::get_state ()
3349 Metrics::const_iterator i;
3350 XMLNode *root = new XMLNode ("TempoMap");
3353 Glib::Threads::RWLock::ReaderLock lm (lock);
3354 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3355 root->add_child_nocopy ((*i)->get_state());
3363 TempoMap::set_state (const XMLNode& node, int /*version*/)
3366 Glib::Threads::RWLock::WriterLock lm (lock);
3369 XMLNodeConstIterator niter;
3370 Metrics old_metrics (_metrics);
3373 nlist = node.children();
3375 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
3376 XMLNode* child = *niter;
3378 if (child->name() == TempoSection::xml_state_node_name) {
3381 TempoSection* ts = new TempoSection (*child);
3382 _metrics.push_back (ts);
3385 catch (failed_constructor& err){
3386 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3387 _metrics = old_metrics;
3391 } else if (child->name() == MeterSection::xml_state_node_name) {
3394 MeterSection* ms = new MeterSection (*child);
3395 _metrics.push_back (ms);
3398 catch (failed_constructor& err) {
3399 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3400 _metrics = old_metrics;
3406 if (niter == nlist.end()) {
3407 MetricSectionSorter cmp;
3408 _metrics.sort (cmp);
3411 /* check for legacy sessions where bbt was the base musical unit for tempo */
3412 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3414 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3415 if (t->legacy_bbt().bars != 0) {
3416 fix_legacy_session();
3423 /* check for multiple tempo/meters at the same location, which
3424 ardour2 somehow allowed.
3427 Metrics::iterator prev = _metrics.end();
3428 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3429 if (prev != _metrics.end()) {
3431 MeterSection* prev_m;
3433 TempoSection* prev_t;
3434 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
3435 if (prev_m->pulse() == ms->pulse()) {
3436 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3437 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3440 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
3441 if (prev_t->pulse() == ts->pulse()) {
3442 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3443 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3451 recompute_map (_metrics);
3454 PropertyChanged (PropertyChange ());
3460 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
3462 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
3463 const MeterSection* m;
3464 const TempoSection* t;
3465 const TempoSection* prev_t = 0;
3467 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3469 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
3470 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
3471 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
3472 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
3474 o << "previous : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
3475 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;
3478 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
3479 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
3480 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
3483 o << "------" << std::endl;
3487 TempoMap::n_tempos() const
3489 Glib::Threads::RWLock::ReaderLock lm (lock);
3492 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3493 if (dynamic_cast<const TempoSection*>(*i) != 0) {
3502 TempoMap::n_meters() const
3504 Glib::Threads::RWLock::ReaderLock lm (lock);
3507 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3508 if (dynamic_cast<const MeterSection*>(*i) != 0) {
3517 TempoMap::insert_time (framepos_t where, framecnt_t amount)
3520 Glib::Threads::RWLock::WriterLock lm (lock);
3521 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3522 if ((*i)->frame() >= where && (*i)->movable ()) {
3523 (*i)->set_frame ((*i)->frame() + amount);
3527 /* now reset the BBT time of all metrics, based on their new
3528 * audio time. This is the only place where we do this reverse
3532 Metrics::iterator i;
3533 const MeterSection* meter;
3534 const TempoSection* tempo;
3538 meter = &first_meter ();
3539 tempo = &first_tempo ();
3544 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
3547 MetricSection* prev = 0;
3549 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3552 //TempoMetric metric (*meter, *tempo);
3553 MeterSection* ms = const_cast<MeterSection*>(meter);
3554 TempoSection* ts = const_cast<TempoSection*>(tempo);
3557 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3561 ts->set_pulse (t->pulse());
3563 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3564 ts->set_pulse (m->pulse());
3566 ts->set_frame (prev->frame());
3570 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3571 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3572 ms->set_beat (start);
3573 ms->set_pulse (m->pulse());
3575 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3579 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3580 pair<double, BBT_Time> start = make_pair (beat, beats_to_bbt_locked (_metrics, beat));
3581 ms->set_beat (start);
3582 ms->set_pulse (t->pulse());
3584 ms->set_frame (prev->frame());
3588 // metric will be at frames=0 bbt=1|1|0 by default
3589 // which is correct for our purpose
3592 // cerr << bbt << endl;
3594 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3598 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3600 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3601 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3602 bbt_time (m->frame(), bbt);
3604 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3610 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3611 /* round up to next beat */
3617 if (bbt.beats != 1) {
3618 /* round up to next bar */
3623 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3624 m->set_beat (start);
3625 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3627 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3629 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3630 abort(); /*NOTREACHED*/
3636 recompute_map (_metrics);
3640 PropertyChanged (PropertyChange ());
3643 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3647 std::list<MetricSection*> metric_kill_list;
3649 TempoSection* last_tempo = NULL;
3650 MeterSection* last_meter = NULL;
3651 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3652 bool meter_after = false; // is there a meter marker likewise?
3654 Glib::Threads::RWLock::WriterLock lm (lock);
3655 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3656 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3657 metric_kill_list.push_back(*i);
3658 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3661 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3665 else if ((*i)->frame() >= where) {
3666 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3667 (*i)->set_frame ((*i)->frame() - amount);
3668 if ((*i)->frame() == where) {
3669 // marker was immediately after end of range
3670 tempo_after = dynamic_cast<TempoSection*> (*i);
3671 meter_after = dynamic_cast<MeterSection*> (*i);
3677 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3678 if (last_tempo && !tempo_after) {
3679 metric_kill_list.remove(last_tempo);
3680 last_tempo->set_frame(where);
3683 if (last_meter && !meter_after) {
3684 metric_kill_list.remove(last_meter);
3685 last_meter->set_frame(where);
3689 //remove all the remaining metrics
3690 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3691 _metrics.remove(*i);
3696 recompute_map (_metrics);
3699 PropertyChanged (PropertyChange ());
3703 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3704 * pos can be -ve, if required.
3707 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3709 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
3712 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3714 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3716 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
3719 /** Add the BBT interval op to pos and return the result */
3721 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3723 Glib::Threads::RWLock::ReaderLock lm (lock);
3725 BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3726 pos_bbt.ticks += op.ticks;
3727 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3729 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3731 pos_bbt.beats += op.beats;
3732 /* the meter in effect will start on the bar */
3733 double divisions_per_bar = meter_section_at_beat (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3734 while (pos_bbt.beats >= divisions_per_bar + 1) {
3736 divisions_per_bar = meter_section_at_beat (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3737 pos_bbt.beats -= divisions_per_bar;
3739 pos_bbt.bars += op.bars;
3741 return frame_time_locked (_metrics, pos_bbt);
3744 /** Count the number of beats that are equivalent to distance when going forward,
3748 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3750 return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
3754 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3760 operator<< (std::ostream& o, const Meter& m) {
3761 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3765 operator<< (std::ostream& o, const Tempo& t) {
3766 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3770 operator<< (std::ostream& o, const MetricSection& section) {
3772 o << "MetricSection @ " << section.frame() << ' ';
3774 const TempoSection* ts;
3775 const MeterSection* ms;
3777 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3778 o << *((const Tempo*) ts);
3779 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3780 o << *((const Meter*) ms);