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_pulse (const Tempo& tempo, const double& pulse, ARDOUR::TempoSection::Type type)
914 TempoSection* ts = 0;
915 const framepos_t frame = frame_at_pulse_locked (_metrics, pulse);
917 Glib::Threads::RWLock::WriterLock lm (lock);
918 ts = add_tempo_locked (tempo, pulse, frame, type, MusicTime, true);
921 PropertyChanged (PropertyChange ());
927 TempoMap::add_tempo_frame (const Tempo& tempo, const framepos_t& frame, ARDOUR::TempoSection::Type type)
929 TempoSection* ts = 0;
930 const double pulse = pulse_at_frame_locked (_metrics, frame);
932 Glib::Threads::RWLock::WriterLock lm (lock);
933 ts = add_tempo_locked (tempo, pulse, frame, type, AudioTime, true);
937 PropertyChanged (PropertyChange ());
944 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, ARDOUR::TempoSection::Type type, PositionLockStyle pls)
946 TempoSection* ts = 0;
948 Glib::Threads::RWLock::WriterLock lm (lock);
949 ts = add_tempo_locked (tempo, pulse, frame, type, pls, true);
953 PropertyChanged (PropertyChange ());
959 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, framepos_t frame
960 , TempoSection::Type type, PositionLockStyle pls, bool recompute, bool locked_to_meter)
962 TempoSection* t = new TempoSection (pulse, frame, tempo.beats_per_minute(), tempo.note_type(), type, pls);
963 t->set_locked_to_meter (locked_to_meter);
968 if (pls == AudioTime) {
969 solve_map_frame (_metrics, t, t->frame());
971 solve_map_pulse (_metrics, t, t->pulse());
973 recompute_meters (_metrics);
980 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, const framepos_t& frame, PositionLockStyle pls)
983 Glib::Threads::RWLock::WriterLock lm (lock);
984 const double beat = bbt_to_beats_locked (_metrics, where);
987 remove_meter_locked (ms);
988 add_meter_locked (meter, beat, where, frame, pls, true);
990 MeterSection& first (first_meter());
991 TempoSection& first_t (first_tempo());
992 /* cannot move the first meter section */
993 *static_cast<Meter*>(&first) = meter;
994 first.set_position_lock_style (AudioTime);
995 first.set_pulse (0.0);
996 first.set_frame (frame);
997 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
998 first.set_beat (beat);
999 first_t.set_frame (first.frame());
1000 first_t.set_pulse (0.0);
1001 first_t.set_position_lock_style (AudioTime);
1003 recompute_map (_metrics);
1005 PropertyChanged (PropertyChange ());
1009 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, const framepos_t& frame, PositionLockStyle pls)
1011 MeterSection* m = 0;
1013 Glib::Threads::RWLock::WriterLock lm (lock);
1014 m = add_meter_locked (meter, beat, where, frame, pls, true);
1019 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1020 dump (_metrics, std::cerr);
1024 PropertyChanged (PropertyChange ());
1029 TempoMap::add_meter_locked (const Meter& meter, double beat, const Timecode::BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute)
1031 const MeterSection& prev_m = meter_section_at_locked (_metrics, frame - 1);
1032 const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1034 if (pls == AudioTime) {
1035 /* add meter-locked tempo */
1036 add_tempo_locked (tempo_at_locked (_metrics, frame), pulse, frame, TempoSection::Ramp, AudioTime, true, true);
1039 MeterSection* new_meter = new MeterSection (pulse, frame, beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls);
1041 do_insert (new_meter);
1045 if (pls == AudioTime) {
1046 solve_map_frame (_metrics, new_meter, frame);
1048 solve_map_bbt (_metrics, new_meter, where);
1056 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1058 Tempo newtempo (beats_per_minute, note_type);
1061 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1062 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1067 Glib::Threads::RWLock::WriterLock lm (lock);
1068 *((Tempo*) t) = newtempo;
1069 recompute_map (_metrics);
1071 PropertyChanged (PropertyChange ());
1078 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1080 Tempo newtempo (beats_per_minute, note_type);
1083 TempoSection* first;
1084 Metrics::iterator i;
1086 /* find the TempoSection immediately preceding "where"
1089 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1091 if ((*i)->frame() > where) {
1097 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1110 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1120 Glib::Threads::RWLock::WriterLock lm (lock);
1121 /* cannot move the first tempo section */
1122 *((Tempo*)prev) = newtempo;
1123 recompute_map (_metrics);
1126 PropertyChanged (PropertyChange ());
1130 TempoMap::first_meter () const
1132 const MeterSection *m = 0;
1134 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1135 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1140 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1141 abort(); /*NOTREACHED*/
1146 TempoMap::first_meter ()
1148 MeterSection *m = 0;
1150 /* CALLER MUST HOLD LOCK */
1152 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1153 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1158 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1159 abort(); /*NOTREACHED*/
1164 TempoMap::first_tempo () const
1166 const TempoSection *t = 0;
1168 /* CALLER MUST HOLD LOCK */
1170 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1171 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1175 if (!t->movable()) {
1181 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1182 abort(); /*NOTREACHED*/
1187 TempoMap::first_tempo ()
1189 TempoSection *t = 0;
1191 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1192 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1196 if (!t->movable()) {
1202 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1203 abort(); /*NOTREACHED*/
1207 TempoMap::recompute_tempos (Metrics& metrics)
1209 TempoSection* prev_t = 0;
1211 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1214 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1218 if (!t->movable()) {
1226 if (t->position_lock_style() == AudioTime) {
1227 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1228 if (!t->locked_to_meter()) {
1229 t->set_pulse (prev_t->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
1233 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1234 t->set_frame (prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
1241 prev_t->set_c_func (0.0);
1244 /* tempos must be positioned correctly.
1245 the current approach is to use a meter's bbt time as its base position unit.
1246 this means that a meter's beat may change, but its bbt may not.
1247 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1248 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1251 TempoMap::recompute_meters (Metrics& metrics)
1253 MeterSection* meter = 0;
1254 MeterSection* prev_m = 0;
1256 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1257 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1258 if (meter->position_lock_style() == AudioTime) {
1260 pair<double, BBT_Time> b_bbt;
1261 TempoSection* meter_locked_tempo = 0;
1262 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1264 if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
1265 if ((t->locked_to_meter() || !t->movable()) && t->frame() == meter->frame()) {
1266 meter_locked_tempo = t;
1273 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1274 if (beats + prev_m->beat() != meter->beat()) {
1275 /* reordering caused a bbt change */
1276 b_bbt = make_pair (beats + prev_m->beat()
1277 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1278 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1280 } else if (meter->movable()) {
1281 b_bbt = make_pair (meter->beat(), meter->bbt());
1282 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1285 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1287 if (meter_locked_tempo) {
1288 meter_locked_tempo->set_pulse (pulse);
1290 meter->set_beat (b_bbt);
1291 meter->set_pulse (pulse);
1296 pair<double, BBT_Time> new_beat;
1298 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1299 if (beats + prev_m->beat() != meter->beat()) {
1300 /* reordering caused a bbt change */
1301 new_beat = make_pair (beats + prev_m->beat()
1302 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1304 new_beat = make_pair (beats + prev_m->beat(), meter->bbt());
1306 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1308 /* shouldn't happen - the first is audio-locked */
1309 pulse = pulse_at_beat_locked (metrics, meter->beat());
1310 new_beat = make_pair (meter->beat(), meter->bbt());
1313 meter->set_beat (new_beat);
1314 meter->set_pulse (pulse);
1315 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1324 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1326 /* CALLER MUST HOLD WRITE LOCK */
1330 /* we will actually stop once we hit
1337 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1340 /* silly call from Session::process() during startup
1345 recompute_tempos (metrics);
1346 recompute_meters (metrics);
1350 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1352 Glib::Threads::RWLock::ReaderLock lm (lock);
1353 TempoMetric m (first_meter(), first_tempo());
1355 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1356 at something, because we insert the default tempo and meter during
1357 TempoMap construction.
1359 now see if we can find better candidates.
1362 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1364 if ((*i)->frame() > frame) {
1378 /* XX meters only */
1380 TempoMap::metric_at (BBT_Time bbt) const
1382 Glib::Threads::RWLock::ReaderLock lm (lock);
1383 TempoMetric m (first_meter(), first_tempo());
1385 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1386 at something, because we insert the default tempo and meter during
1387 TempoMap construction.
1389 now see if we can find better candidates.
1392 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1394 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1395 BBT_Time section_start (mw->bbt());
1397 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1409 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1411 MeterSection* prev_m = 0;
1413 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1415 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1416 if (prev_m && m->beat() > beat) {
1423 double const ret = prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1428 TempoMap::pulse_at_beat (const double& beat) const
1430 Glib::Threads::RWLock::ReaderLock lm (lock);
1431 return pulse_at_beat_locked (_metrics, beat);
1435 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1437 MeterSection* prev_m = 0;
1439 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1441 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1442 if (prev_m && m->pulse() > pulse) {
1443 if (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > m->beat()) {
1451 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1456 TempoMap::beat_at_pulse (const double& pulse) const
1458 Glib::Threads::RWLock::ReaderLock lm (lock);
1459 return beat_at_pulse_locked (_metrics, pulse);
1462 /* tempo section based */
1464 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1466 /* HOLD (at least) THE READER LOCK */
1467 TempoSection* prev_t = 0;
1469 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1471 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1475 if (prev_t && t->frame() > frame) {
1476 /*the previous ts is the one containing the frame */
1477 const double ret = prev_t->pulse_at_frame (frame, _frame_rate);
1484 /* treated as constant for this ts */
1485 const double pulses_in_section = (frame - prev_t->frame()) / prev_t->frames_per_pulse (_frame_rate);
1487 return pulses_in_section + prev_t->pulse();
1491 TempoMap::pulse_at_frame (const framecnt_t& frame) const
1493 Glib::Threads::RWLock::ReaderLock lm (lock);
1494 return pulse_at_frame_locked (_metrics, frame);
1497 /* tempo section based */
1499 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1501 /* HOLD THE READER LOCK */
1503 const TempoSection* prev_t = 0;
1505 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1508 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1512 if (prev_t && t->pulse() > pulse) {
1513 return prev_t->frame_at_pulse (pulse, _frame_rate);
1519 /* must be treated as constant, irrespective of _type */
1520 double const pulses_in_section = pulse - prev_t->pulse();
1521 double const dtime = pulses_in_section * prev_t->frames_per_pulse (_frame_rate);
1523 framecnt_t const ret = (framecnt_t) floor (dtime) + prev_t->frame();
1529 TempoMap::frame_at_pulse (const double& pulse) const
1531 Glib::Threads::RWLock::ReaderLock lm (lock);
1532 return frame_at_pulse_locked (_metrics, pulse);
1535 /* meter section based */
1537 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1539 const TempoSection& ts = tempo_section_at_locked (metrics, frame);
1540 MeterSection* prev_m = 0;
1541 MeterSection* next_m = 0;
1543 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1545 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1546 if (prev_m && m->frame() > frame) {
1553 if (frame < prev_m->frame()) {
1556 const double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
1558 if (next_m && next_m->beat() < beat) {
1559 return next_m->beat();
1566 TempoMap::beat_at_frame (const framecnt_t& frame) const
1568 Glib::Threads::RWLock::ReaderLock lm (lock);
1569 return beat_at_frame_locked (_metrics, frame);
1572 /* meter section based */
1574 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1576 const TempoSection& prev_t = tempo_section_at_beat_locked (metrics, beat);
1577 MeterSection* prev_m = 0;
1579 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1581 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1582 if (prev_m && m->beat() > beat) {
1589 return prev_t.frame_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse(), _frame_rate);
1593 TempoMap::frame_at_beat (const double& beat) const
1595 Glib::Threads::RWLock::ReaderLock lm (lock);
1596 return frame_at_beat_locked (_metrics, beat);
1600 TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1602 /* CALLER HOLDS READ LOCK */
1604 MeterSection* prev_m = 0;
1606 /* because audio-locked meters have 'fake' integral beats,
1607 there is no pulse offset here.
1609 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1611 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1613 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1614 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1622 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1623 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1624 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1630 TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
1632 Glib::Threads::RWLock::ReaderLock lm (lock);
1633 return bbt_to_beats_locked (_metrics, bbt);
1637 TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
1639 /* CALLER HOLDS READ LOCK */
1640 MeterSection* prev_m = 0;
1641 const double beats = max (0.0, b);
1643 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1644 MeterSection* m = 0;
1646 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1648 if (m->beat() > beats) {
1649 /* this is the meter after the one our beat is on*/
1658 const double beats_in_ms = beats - prev_m->beat();
1659 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1660 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1661 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1662 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1666 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1667 ret.beats = (uint32_t) floor (remaining_beats);
1668 ret.bars = total_bars;
1670 /* 0 0 0 to 1 1 0 - based mapping*/
1674 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1676 ret.ticks -= BBT_Time::ticks_per_beat;
1679 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1688 TempoMap::beats_to_bbt (const double& beats)
1690 Glib::Threads::RWLock::ReaderLock lm (lock);
1691 return beats_to_bbt_locked (_metrics, beats);
1695 TempoMap::pulse_to_bbt (const double& pulse)
1697 Glib::Threads::RWLock::ReaderLock lm (lock);
1698 MeterSection* prev_m = 0;
1700 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1701 MeterSection* m = 0;
1703 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1706 double const pulses_to_m = m->pulse() - prev_m->pulse();
1707 if (prev_m->pulse() + pulses_to_m > pulse) {
1708 /* this is the meter after the one our beat is on*/
1717 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1718 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1719 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1720 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1721 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1725 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1726 ret.beats = (uint32_t) floor (remaining_beats);
1727 ret.bars = total_bars;
1729 /* 0 0 0 to 1 1 0 mapping*/
1733 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1735 ret.ticks -= BBT_Time::ticks_per_beat;
1738 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1747 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1754 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1757 Glib::Threads::RWLock::ReaderLock lm (lock);
1758 const double beat = beat_at_frame_locked (_metrics, frame);
1760 bbt = beats_to_bbt_locked (_metrics, beat);
1763 /* meter section based */
1765 TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
1767 /* HOLD THE READER LOCK */
1769 const framepos_t ret = frame_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt));
1774 TempoMap::frame_time (const BBT_Time& bbt)
1777 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1781 if (bbt.beats < 1) {
1782 throw std::logic_error ("beats are counted from one");
1784 Glib::Threads::RWLock::ReaderLock lm (lock);
1786 return frame_time_locked (_metrics, bbt);
1790 TempoMap::check_solved (const Metrics& metrics, bool by_frame) const
1792 TempoSection* prev_t = 0;
1793 MeterSection* prev_m = 0;
1795 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1798 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1803 if ((by_frame && t->frame() < prev_t->frame()) || (!by_frame && t->pulse() < prev_t->pulse())) {
1807 if (t->frame() == prev_t->frame()) {
1811 /* precision check ensures pulses and frames align.*/
1812 if (t->frame() != prev_t->frame_at_pulse (t->pulse(), _frame_rate)) {
1813 if (!t->locked_to_meter()) {
1821 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1822 if (prev_m && m->position_lock_style() == AudioTime) {
1823 TempoSection* t = const_cast<TempoSection*>(&tempo_section_at_locked (metrics, m->frame() - 1));
1824 const double nascent_m_pulse = ((m->beat() - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse();
1825 const framepos_t nascent_m_frame = t->frame_at_pulse (nascent_m_pulse, _frame_rate);
1827 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
1841 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
1843 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1845 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1846 if (!t->movable()) {
1847 t->set_active (true);
1850 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
1851 t->set_active (false);
1853 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
1854 t->set_active (true);
1855 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
1864 TempoMap::solve_map_frame (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
1866 TempoSection* prev_t = 0;
1867 TempoSection* section_prev = 0;
1868 framepos_t first_m_frame = 0;
1870 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1872 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1873 if (!m->movable()) {
1874 first_m_frame = m->frame();
1879 if (section->movable() && frame <= first_m_frame) {
1883 section->set_active (true);
1884 section->set_frame (frame);
1886 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1888 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1895 section_prev = prev_t;
1898 if (t->position_lock_style() == MusicTime) {
1899 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1900 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1902 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1903 if (!t->locked_to_meter()) {
1904 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1913 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
1914 if (!section->locked_to_meter()) {
1915 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
1919 if (section->position_lock_style() == MusicTime) {
1920 /* we're setting the frame */
1921 section->set_position_lock_style (AudioTime);
1922 recompute_tempos (imaginary);
1923 section->set_position_lock_style (MusicTime);
1925 recompute_tempos (imaginary);
1928 if (check_solved (imaginary, true)) {
1932 MetricSectionFrameSorter fcmp;
1933 imaginary.sort (fcmp);
1934 if (section->position_lock_style() == MusicTime) {
1935 /* we're setting the frame */
1936 section->set_position_lock_style (AudioTime);
1937 recompute_tempos (imaginary);
1938 section->set_position_lock_style (MusicTime);
1940 recompute_tempos (imaginary);
1943 if (check_solved (imaginary, true)) {
1951 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
1953 TempoSection* prev_t = 0;
1954 TempoSection* section_prev = 0;
1956 section->set_pulse (pulse);
1958 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1960 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1964 if (!t->movable()) {
1971 section_prev = prev_t;
1974 if (t->position_lock_style() == MusicTime) {
1975 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1976 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1978 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1979 if (!t->locked_to_meter()) {
1980 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1988 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
1989 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
1992 if (section->position_lock_style() == AudioTime) {
1993 /* we're setting the pulse */
1994 section->set_position_lock_style (MusicTime);
1995 recompute_tempos (imaginary);
1996 section->set_position_lock_style (AudioTime);
1998 recompute_tempos (imaginary);
2001 if (check_solved (imaginary, false)) {
2005 MetricSectionSorter cmp;
2006 imaginary.sort (cmp);
2007 if (section->position_lock_style() == AudioTime) {
2008 /* we're setting the pulse */
2009 section->set_position_lock_style (MusicTime);
2010 recompute_tempos (imaginary);
2011 section->set_position_lock_style (AudioTime);
2013 recompute_tempos (imaginary);
2016 if (check_solved (imaginary, false)) {
2024 TempoMap::solve_map_frame (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
2026 /* disallow moving first meter past any subsequent one, and any movable meter before the first one */
2027 const MeterSection* other = &meter_section_at_locked (imaginary, frame);
2028 if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->frame() >= frame)) {
2032 if (!section->movable()) {
2033 /* lock the first tempo to our first meter */
2034 if (!set_active_tempos (imaginary, frame)) {
2039 /* it would make sense to bail out if there is no audio-locked meter,
2040 however it may be desirable to move a music-locked meter by frame at some point.
2042 TempoSection* meter_locked_tempo = 0;
2043 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2045 if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
2046 if ((t->locked_to_meter() || !t->movable()) && t->frame() == section->frame()) {
2047 meter_locked_tempo = t;
2053 MeterSection* prev_m = 0;
2055 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2057 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2059 if (prev_m && section->movable()) {
2060 const double beats = (pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor();
2061 if (beats + prev_m->beat() < section->beat()) {
2062 /* disallow position change if it will alter our beat
2063 we allow tempo changes to do this in recompute_meters().
2064 blocking this is an option, but i'm not convinced that
2065 this is what the user would actually want.
2066 here we set the frame/pulse corresponding to its musical position.
2069 if (meter_locked_tempo) {
2071 bool solved = false;
2072 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2073 const double new_pulse = ((section->beat() - prev_m->beat())
2074 / prev_m->note_divisor()) + prev_m->pulse();
2075 const framepos_t smallest_frame = frame_at_pulse_locked (future_map, new_pulse);
2076 if ((solved = solve_map_frame (future_map, tempo_copy, smallest_frame))) {
2077 meter_locked_tempo->set_pulse (new_pulse);
2078 solve_map_frame (imaginary, meter_locked_tempo, smallest_frame);
2079 section->set_frame (smallest_frame);
2080 section->set_pulse (new_pulse);
2085 Metrics::const_iterator d = future_map.begin();
2086 while (d != future_map.end()) {
2097 if (meter_locked_tempo) {
2099 bool solved = false;
2101 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2102 MeterSection* meter_copy = const_cast<MeterSection*> (&meter_section_at_locked (future_map, section->frame()));
2103 meter_copy->set_frame (frame);
2105 if ((solved = solve_map_frame (future_map, tempo_copy, frame))) {
2106 section->set_frame (frame);
2107 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2108 / prev_m->note_divisor()) + prev_m->pulse());
2109 solve_map_frame (imaginary, meter_locked_tempo, frame);
2114 Metrics::const_iterator d = future_map.begin();
2115 while (d != future_map.end()) {
2126 /* not movable (first meter atm) */
2127 if (meter_locked_tempo) {
2129 bool solved = false;
2130 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2132 tempo_copy->set_frame (frame);
2133 tempo_copy->set_pulse (0.0);
2135 if ((solved = solve_map_frame (future_map, tempo_copy, frame))) {
2136 section->set_frame (frame);
2137 meter_locked_tempo->set_frame (frame);
2138 meter_locked_tempo->set_pulse (0.0);
2139 solve_map_frame (imaginary, meter_locked_tempo, frame);
2144 Metrics::const_iterator d = future_map.begin();
2145 while (d != future_map.end()) {
2157 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2158 section->set_beat (b_bbt);
2159 section->set_pulse (0.0);
2169 MetricSectionFrameSorter fcmp;
2170 imaginary.sort (fcmp);
2171 if (section->position_lock_style() == MusicTime) {
2172 /* we're setting the frame */
2173 section->set_position_lock_style (AudioTime);
2174 recompute_meters (imaginary);
2175 section->set_position_lock_style (MusicTime);
2177 recompute_meters (imaginary);
2184 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2186 /* disallow setting section to an existing meter's bbt */
2187 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2189 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2190 if (m != section && m->bbt().bars == when.bars) {
2196 MeterSection* prev_m = 0;
2197 MeterSection* section_prev = 0;
2199 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2201 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2202 pair<double, BBT_Time> b_bbt;
2203 double new_pulse = 0.0;
2205 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2206 section_prev = prev_m;
2207 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2208 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2209 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2211 section->set_beat (b_bbt);
2212 section->set_pulse (pulse);
2213 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2218 if (m->position_lock_style() == AudioTime) {
2219 TempoSection* meter_locked_tempo = 0;
2220 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2222 if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
2223 if ((t->locked_to_meter() || !t->movable()) && t->frame() == m->frame()) {
2224 meter_locked_tempo = t;
2231 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2233 if (beats + prev_m->beat() != m->beat()) {
2234 /* tempo/ meter change caused a change in beat (bar). */
2235 b_bbt = make_pair (beats + prev_m->beat()
2236 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2237 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2238 } else if (m->movable()) {
2239 b_bbt = make_pair (m->beat(), m->bbt());
2240 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2243 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2245 if (meter_locked_tempo) {
2246 meter_locked_tempo->set_pulse (new_pulse);
2247 recompute_tempos (imaginary);
2249 m->set_beat (b_bbt);
2250 m->set_pulse (new_pulse);
2254 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2255 if (beats + prev_m->beat() != m->beat()) {
2256 /* tempo/ meter change caused a change in beat (bar). */
2257 b_bbt = make_pair (beats + prev_m->beat()
2258 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2260 b_bbt = make_pair (beats + prev_m->beat()
2263 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2264 m->set_beat (b_bbt);
2265 m->set_pulse (new_pulse);
2266 m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2273 if (!section_prev) {
2275 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
2276 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2277 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
2279 section->set_beat (b_bbt);
2280 section->set_pulse (pulse);
2281 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2284 MetricSectionSorter cmp;
2285 imaginary.sort (cmp);
2287 if (section->position_lock_style() == AudioTime) {
2288 /* we're setting the pulse */
2289 section->set_position_lock_style (MusicTime);
2290 recompute_meters (imaginary);
2291 section->set_position_lock_style (AudioTime);
2293 recompute_meters (imaginary);
2299 /** places a copy of _metrics into copy and returns a pointer
2300 * to section's equivalent in copy.
2303 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
2305 TempoSection* ret = 0;
2307 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2310 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2312 ret = new TempoSection (*t);
2313 copy.push_back (ret);
2317 TempoSection* cp = new TempoSection (*t);
2318 copy.push_back (cp);
2320 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2321 MeterSection* cp = new MeterSection (*m);
2322 copy.push_back (cp);
2330 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
2332 MeterSection* ret = 0;
2334 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2337 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2338 TempoSection* cp = new TempoSection (*t);
2339 copy.push_back (cp);
2342 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2344 ret = new MeterSection (*m);
2345 copy.push_back (ret);
2348 MeterSection* cp = new MeterSection (*m);
2349 copy.push_back (cp);
2357 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2360 TempoSection* tempo_copy = 0;
2363 Glib::Threads::RWLock::ReaderLock lm (lock);
2364 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
2370 const double beat = bbt_to_beats_locked (copy, bbt);
2371 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_beat_locked (copy, beat));
2373 Metrics::const_iterator d = copy.begin();
2374 while (d != copy.end()) {
2383 * 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,
2384 * taking any possible reordering as a consequence of this into account.
2385 * @param section - the section to be altered
2386 * @param bpm - the new Tempo
2387 * @param bbt - the bbt where the altered tempo will fall
2388 * @return returns - the position in frames where the new tempo section will lie.
2391 TempoMap::predict_tempo_frame (TempoSection* section, const BBT_Time& bbt)
2393 Glib::Threads::RWLock::ReaderLock lm (lock);
2396 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
2400 const double beat = bbt_to_beats_locked (future_map, bbt);
2402 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
2403 ret = tempo_copy->frame();
2405 ret = section->frame();
2408 Metrics::const_iterator d = future_map.begin();
2409 while (d != future_map.end()) {
2417 TempoMap::predict_tempo_pulse (TempoSection* section, const framepos_t& frame)
2419 Glib::Threads::RWLock::ReaderLock lm (lock);
2422 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
2424 if (solve_map_frame (future_map, tempo_copy, frame)) {
2425 ret = tempo_copy->pulse();
2427 ret = section->pulse();
2430 Metrics::const_iterator d = future_map.begin();
2431 while (d != future_map.end()) {
2439 TempoMap::gui_move_tempo_frame (TempoSection* ts, const framepos_t& frame)
2443 Glib::Threads::RWLock::WriterLock lm (lock);
2444 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2445 if (solve_map_frame (future_map, tempo_copy, frame)) {
2446 solve_map_frame (_metrics, ts, frame);
2447 recompute_meters (_metrics);
2451 Metrics::const_iterator d = future_map.begin();
2452 while (d != future_map.end()) {
2457 MetricPositionChanged (); // Emit Signal
2461 TempoMap::gui_move_tempo_beat (TempoSection* ts, const double& beat)
2465 Glib::Threads::RWLock::WriterLock lm (lock);
2466 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2467 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
2468 solve_map_pulse (_metrics, ts, pulse_at_beat_locked (_metrics, beat));
2469 recompute_meters (_metrics);
2473 Metrics::const_iterator d = future_map.begin();
2474 while (d != future_map.end()) {
2479 MetricPositionChanged (); // Emit Signal
2483 TempoMap::gui_move_meter_frame (MeterSection* ms, const framepos_t& frame)
2487 Glib::Threads::RWLock::WriterLock lm (lock);
2488 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2489 if (solve_map_frame (future_map, copy, frame)) {
2490 solve_map_frame (_metrics, ms, frame);
2491 recompute_tempos (_metrics);
2495 Metrics::const_iterator d = future_map.begin();
2496 while (d != future_map.end()) {
2501 MetricPositionChanged (); // Emit Signal
2505 TempoMap::gui_move_meter_bbt (MeterSection* ms, const Timecode::BBT_Time& bbt)
2509 Glib::Threads::RWLock::WriterLock lm (lock);
2510 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2511 if (solve_map_bbt (future_map, copy, bbt)) {
2512 solve_map_bbt (_metrics, ms, bbt);
2513 recompute_tempos (_metrics);
2517 Metrics::const_iterator d = future_map.begin();
2518 while (d != future_map.end()) {
2523 MetricPositionChanged (); // Emit Signal
2527 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2530 bool can_solve = false;
2532 Glib::Threads::RWLock::WriterLock lm (lock);
2533 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2534 tempo_copy->set_beats_per_minute (bpm.beats_per_minute());
2535 recompute_tempos (future_map);
2537 if (check_solved (future_map, true)) {
2538 ts->set_beats_per_minute (bpm.beats_per_minute());
2539 recompute_map (_metrics);
2544 Metrics::const_iterator d = future_map.begin();
2545 while (d != future_map.end()) {
2550 MetricPositionChanged (); // Emit Signal
2556 TempoMap::gui_dilate_tempo (MeterSection* ms, const framepos_t& frame)
2559 TempoSection* ts = 0;
2561 if (frame <= first_meter().frame()) {
2565 if (ms->position_lock_style() == AudioTime) {
2566 /* disabled for now due to faked tempo locked to meter pulse */
2571 Glib::Threads::RWLock::WriterLock lm (lock);
2572 ts = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, ms->frame() - 1));
2576 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
2577 TempoSection* prev_to_prev_t = 0;
2578 const frameoffset_t fr_off = frame - ms->frame();
2579 double new_bpm = 0.0;
2581 if (prev_t && prev_t->pulse() > 0.0) {
2582 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (future_map, prev_t->frame() - 1));
2585 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
2586 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
2588 double contribution = 0.0;
2589 frameoffset_t frame_contribution = 0;
2590 frameoffset_t prev_t_frame_contribution = fr_off;
2592 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2593 /* prev to prev_t's position will remain constant in terms of frame and pulse. lets use frames. */
2594 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (ms->frame() - prev_to_prev_t->frame());
2595 frame_contribution = contribution * (double) fr_off;
2596 prev_t_frame_contribution = fr_off - frame_contribution;
2599 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
2601 if (prev_t->position_lock_style() == MusicTime) {
2602 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2603 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2604 / (double) (ms->frame() + prev_t_frame_contribution - prev_t->frame()));
2607 /* prev to prev is irrelevant */
2608 const double meter_pulse = prev_t->pulse_at_frame (ms->frame(), _frame_rate);
2609 const double frame_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
2611 if (frame_pulse != prev_t->pulse()) {
2612 new_bpm = prev_t->beats_per_minute() * ((meter_pulse - prev_t->pulse()) / (frame_pulse - prev_t->pulse()));
2614 new_bpm = prev_t->beats_per_minute();
2619 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2620 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2621 / (double) (ms->frame() + prev_t_frame_contribution - prev_t->frame()));
2623 /* prev_to_prev_t is irrelevant */
2625 if (frame != prev_t->frame()) {
2626 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame()) / (double) (frame - prev_t->frame()));
2628 new_bpm = prev_t->beats_per_minute();
2632 } else if (prev_t->c_func() < 0.0) {
2633 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2634 new_bpm = prev_t->tempo_at_frame (prev_t->frame() + frame_contribution, _frame_rate) * (double) prev_t->note_type();
2636 /* prev_to_prev_t is irrelevant */
2637 new_bpm = prev_t->tempo_at_frame (prev_t->frame() + fr_off, _frame_rate) * (double) prev_t->note_type();
2640 } else if (prev_t->c_func() > 0.0) {
2641 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2642 new_bpm = prev_t->tempo_at_frame (prev_t->frame() - frame_contribution, _frame_rate) * (double) prev_t->note_type();
2644 /* prev_to_prev_t is irrelevant */
2645 new_bpm = prev_t->tempo_at_frame (prev_t->frame() - fr_off, _frame_rate) * (double) prev_t->note_type();
2649 /* limits - a bit clunky, but meh */
2650 const double diff = (prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type()) - prev_t->beats_per_minute();
2651 if (diff > -1.0 && diff < 1.0) {
2652 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2653 / (double) ((ms->frame() + prev_t_frame_contribution) - prev_t->frame()));
2656 prev_t->set_beats_per_minute (new_bpm);
2657 recompute_tempos (future_map);
2658 recompute_meters (future_map);
2660 if (check_solved (future_map, true)) {
2662 prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, ms->frame() - 1));
2663 prev_t->set_beats_per_minute (new_bpm);
2664 recompute_tempos (_metrics);
2666 if (ms->position_lock_style() == AudioTime) {
2667 ms->set_frame (frame);
2670 recompute_meters (_metrics);
2674 Metrics::const_iterator d = future_map.begin();
2675 while (d != future_map.end()) {
2680 MetricPositionChanged (); // Emit Signal
2684 TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame, const double& pulse)
2687 Ts (future prev_t) Tnext
2690 |----------|----------
2697 Glib::Threads::RWLock::WriterLock lm (lock);
2703 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
2704 TempoSection* prev_to_prev_t = 0;
2705 const frameoffset_t fr_off = end_frame - frame;
2707 if (prev_t && prev_t->pulse() > 0.0) {
2708 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (future_map, prev_t->frame() - 1));
2711 TempoSection* next_t = 0;
2712 for (Metrics::iterator i = future_map.begin(); i != future_map.end(); ++i) {
2713 TempoSection* t = 0;
2714 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2715 if (t->frame() > ts->frame()) {
2722 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
2723 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
2725 double contribution = 0.0;
2726 double start_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
2728 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2729 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
2732 frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
2733 double end_pulse = prev_t->pulse_at_frame (end_frame, _frame_rate);
2736 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
2738 if (prev_t->position_lock_style() == MusicTime) {
2739 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2741 new_bpm = prev_t->beats_per_minute() * ((frame - prev_to_prev_t->frame())
2742 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
2745 /* prev to prev is irrelevant */
2747 if (start_pulse != prev_t->pulse()) {
2748 new_bpm = prev_t->beats_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
2750 new_bpm = prev_t->beats_per_minute();
2755 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2756 new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame())
2757 / (double) ((frame + prev_t_frame_contribution) - prev_t->frame()));
2759 /* prev_to_prev_t is irrelevant */
2761 if (end_frame != prev_t->frame()) {
2762 new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
2764 new_bpm = prev_t->beats_per_minute();
2772 const framepos_t pulse_pos = prev_t->frame_at_pulse (pulse, _frame_rate);
2774 if (prev_to_prev_t) {
2776 frame_ratio = (((pulse_pos - fr_off) - prev_to_prev_t->frame()) / (double) ((pulse_pos) - prev_to_prev_t->frame()));
2777 pulse_ratio = ((start_pulse - prev_to_prev_t->pulse()) / (end_pulse - prev_to_prev_t->pulse()));
2780 frame_ratio = (((pulse_pos - fr_off) - prev_t->frame()) / (double) ((pulse_pos) - prev_t->frame()));
2781 pulse_ratio = (start_pulse / end_pulse);
2783 new_bpm = prev_t->beats_per_minute() * (pulse_ratio * frame_ratio);
2786 prev_t->set_beats_per_minute (new_bpm);
2787 recompute_tempos (future_map);
2788 recompute_meters (future_map);
2790 if (check_solved (future_map, true)) {
2791 ts->set_beats_per_minute (new_bpm);
2792 recompute_tempos (_metrics);
2793 recompute_meters (_metrics);
2797 Metrics::const_iterator d = future_map.begin();
2798 while (d != future_map.end()) {
2803 MetricPositionChanged (); // Emit Signal
2807 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2809 Glib::Threads::RWLock::ReaderLock lm (lock);
2811 const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2812 const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2813 const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2815 return frame_at_beat_locked (_metrics, total_beats);
2819 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2821 return round_to_type (fr, dir, Bar);
2825 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2827 return round_to_type (fr, dir, Beat);
2831 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2833 Glib::Threads::RWLock::ReaderLock lm (lock);
2834 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2835 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2836 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2838 ticks -= beats * BBT_Time::ticks_per_beat;
2841 /* round to next (or same iff dir == RoundUpMaybe) */
2843 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2845 if (mod == 0 && dir == RoundUpMaybe) {
2846 /* right on the subdivision, which is fine, so do nothing */
2848 } else if (mod == 0) {
2849 /* right on the subdivision, so the difference is just the subdivision ticks */
2850 ticks += ticks_one_subdivisions_worth;
2853 /* not on subdivision, compute distance to next subdivision */
2855 ticks += ticks_one_subdivisions_worth - mod;
2858 if (ticks >= BBT_Time::ticks_per_beat) {
2859 ticks -= BBT_Time::ticks_per_beat;
2861 } else if (dir < 0) {
2863 /* round to previous (or same iff dir == RoundDownMaybe) */
2865 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2867 if (difference == 0 && dir == RoundDownAlways) {
2868 /* right on the subdivision, but force-rounding down,
2869 so the difference is just the subdivision ticks */
2870 difference = ticks_one_subdivisions_worth;
2873 if (ticks < difference) {
2874 ticks = BBT_Time::ticks_per_beat - ticks;
2876 ticks -= difference;
2880 /* round to nearest */
2883 /* compute the distance to the previous and next subdivision */
2885 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2887 /* closer to the next subdivision, so shift forward */
2889 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2891 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2893 if (ticks > BBT_Time::ticks_per_beat) {
2895 ticks -= BBT_Time::ticks_per_beat;
2896 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2899 } else if (rem > 0) {
2901 /* closer to previous subdivision, so shift backward */
2905 /* can't go backwards past zero, so ... */
2908 /* step back to previous beat */
2910 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2911 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2913 ticks = lrint (ticks - rem);
2914 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2917 /* on the subdivision, do nothing */
2921 const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2927 TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num, RoundMode dir)
2929 if (sub_num == -1) {
2934 } else if (dir < 0) {
2938 const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2939 if ((double) when.beats > bpb / 2.0) {
2948 } else if (sub_num == 0) {
2949 const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2950 if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
2952 while ((double) when.beats > bpb) {
2954 when.beats -= (uint32_t) floor (bpb);
2962 const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
2965 /* round to next (or same iff dir == RoundUpMaybe) */
2967 uint32_t mod = when.ticks % ticks_one_subdivisions_worth;
2969 if (mod == 0 && dir == RoundUpMaybe) {
2970 /* right on the subdivision, which is fine, so do nothing */
2972 } else if (mod == 0) {
2973 /* right on the subdivision, so the difference is just the subdivision ticks */
2974 when.ticks += ticks_one_subdivisions_worth;
2977 /* not on subdivision, compute distance to next subdivision */
2979 when.ticks += ticks_one_subdivisions_worth - mod;
2982 if (when.ticks >= BBT_Time::ticks_per_beat) {
2983 when.ticks -= BBT_Time::ticks_per_beat;
2986 } else if (dir < 0) {
2987 /* round to previous (or same iff dir == RoundDownMaybe) */
2989 uint32_t difference = when.ticks % ticks_one_subdivisions_worth;
2991 if (difference == 0 && dir == RoundDownAlways) {
2992 /* right on the subdivision, but force-rounding down,
2993 so the difference is just the subdivision ticks */
2994 difference = ticks_one_subdivisions_worth;
2997 if (when.ticks < difference) {
2998 when.ticks = BBT_Time::ticks_per_beat - when.ticks;
3000 when.ticks -= difference;
3004 /* round to nearest */ double rem;
3005 if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
3006 /* closer to the next subdivision, so shift forward */
3008 when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
3010 if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
3012 when.ticks -= Timecode::BBT_Time::ticks_per_beat;
3015 } else if (rem > 0) {
3016 /* closer to previous subdivision, so shift backward */
3018 if (rem > when.ticks) {
3019 if (when.beats == 0) {
3020 /* can't go backwards past zero, so ... */
3022 /* step back to previous beat */
3024 when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
3026 when.ticks = when.ticks - rem;
3033 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
3035 Glib::Threads::RWLock::ReaderLock lm (lock);
3037 const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
3038 BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
3043 /* find bar previous to 'frame' */
3046 return frame_time_locked (_metrics, bbt);
3048 } else if (dir > 0) {
3049 /* find bar following 'frame' */
3053 return frame_time_locked (_metrics, bbt);
3055 /* true rounding: find nearest bar */
3056 framepos_t raw_ft = frame_time_locked (_metrics, bbt);
3059 framepos_t prev_ft = frame_time_locked (_metrics, bbt);
3061 framepos_t next_ft = frame_time_locked (_metrics, bbt);
3063 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
3074 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
3075 } else if (dir > 0) {
3076 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
3078 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
3087 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
3088 framepos_t lower, framepos_t upper)
3090 Glib::Threads::RWLock::ReaderLock lm (lock);
3091 int32_t cnt = ceil (beat_at_frame_locked (_metrics, lower));
3093 /* although the map handles negative beats, bbt doesn't. */
3097 while (pos < upper) {
3098 pos = frame_at_beat_locked (_metrics, cnt);
3099 const TempoSection tempo = tempo_section_at_locked (_metrics, pos);
3100 const MeterSection meter = meter_section_at_locked (_metrics, pos);
3101 const BBT_Time bbt = beats_to_bbt (cnt);
3102 points.push_back (BBTPoint (meter, tempo_at_locked (_metrics, pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
3108 TempoMap::tempo_section_at (framepos_t frame) const
3110 Glib::Threads::RWLock::ReaderLock lm (lock);
3111 return tempo_section_at_locked (_metrics, frame);
3115 TempoMap::tempo_section_at_locked (const Metrics& metrics, framepos_t frame) const
3117 Metrics::const_iterator i;
3118 TempoSection* prev = 0;
3120 for (i = metrics.begin(); i != metrics.end(); ++i) {
3123 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3127 if (prev && t->frame() > frame) {
3137 abort(); /*NOTREACHED*/
3144 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3146 TempoSection* prev_t = 0;
3147 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
3149 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3151 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3152 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
3163 TempoMap::tempo_section_at_pulse_locked (const Metrics& metrics, const double& pulse) const
3165 TempoSection* prev_t = 0;
3167 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3169 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3170 if (prev_t && t->pulse() > pulse) {
3180 /* don't use this to calculate length (the tempo is only correct for this frame).
3181 do that stuff based on the beat_at_frame and frame_at_beat api
3184 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
3186 Glib::Threads::RWLock::ReaderLock lm (lock);
3188 const TempoSection* ts_at = &tempo_section_at_locked (_metrics, frame);
3189 const TempoSection* ts_after = 0;
3190 Metrics::const_iterator i;
3192 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3195 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3199 if ((*i)->frame() > frame) {
3207 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
3209 /* must be treated as constant tempo */
3210 return ts_at->frames_per_beat (_frame_rate);
3214 TempoMap::tempo_at_locked (const Metrics& metrics, const framepos_t& frame) const
3216 TempoSection* prev_t = 0;
3218 Metrics::const_iterator i;
3220 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3222 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3226 if ((prev_t) && t->frame() > frame) {
3227 /* t is the section past frame */
3228 const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
3229 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
3236 const double ret = prev_t->beats_per_minute();
3237 const Tempo ret_tempo (ret, prev_t->note_type ());
3243 TempoMap::tempo_at (const framepos_t& frame) const
3245 Glib::Threads::RWLock::ReaderLock lm (lock);
3246 return tempo_at_locked (_metrics, frame);
3250 TempoMap::meter_section_at_locked (const Metrics& metrics, framepos_t frame) const
3252 Metrics::const_iterator i;
3253 MeterSection* prev = 0;
3255 for (i = metrics.begin(); i != metrics.end(); ++i) {
3258 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
3260 if (prev && (*i)->frame() > frame) {
3270 abort(); /*NOTREACHED*/
3278 TempoMap::meter_section_at (framepos_t frame) const
3280 Glib::Threads::RWLock::ReaderLock lm (lock);
3281 return meter_section_at_locked (_metrics, frame);
3285 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3287 MeterSection* prev_m = 0;
3289 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3291 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
3292 if (prev_m && m->beat() > beat) {
3303 TempoMap::meter_section_at_beat (double beat) const
3305 Glib::Threads::RWLock::ReaderLock lm (lock);
3306 return meter_section_at_beat_locked (_metrics, beat);
3310 TempoMap::meter_at (framepos_t frame) const
3312 TempoMetric m (metric_at (frame));
3317 TempoMap::fix_legacy_session ()
3319 MeterSection* prev_m = 0;
3320 TempoSection* prev_t = 0;
3322 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3326 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3327 if (!m->movable()) {
3328 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3332 m->set_position_lock_style (AudioTime);
3337 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
3338 + (m->bbt().beats - 1)
3339 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
3341 m->set_beat (start);
3342 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
3343 + (m->bbt().beats - 1)
3344 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
3345 m->set_pulse (start_beat / prev_m->note_divisor());
3348 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3354 if (!t->movable()) {
3357 t->set_position_lock_style (AudioTime);
3363 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
3364 + (t->legacy_bbt().beats - 1)
3365 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
3367 t->set_pulse (beat / prev_m->note_divisor());
3369 /* really shouldn't happen but.. */
3370 t->set_pulse (beat / 4.0);
3379 TempoMap::get_state ()
3381 Metrics::const_iterator i;
3382 XMLNode *root = new XMLNode ("TempoMap");
3385 Glib::Threads::RWLock::ReaderLock lm (lock);
3386 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3387 root->add_child_nocopy ((*i)->get_state());
3395 TempoMap::set_state (const XMLNode& node, int /*version*/)
3398 Glib::Threads::RWLock::WriterLock lm (lock);
3401 XMLNodeConstIterator niter;
3402 Metrics old_metrics (_metrics);
3405 nlist = node.children();
3407 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
3408 XMLNode* child = *niter;
3410 if (child->name() == TempoSection::xml_state_node_name) {
3413 TempoSection* ts = new TempoSection (*child);
3414 _metrics.push_back (ts);
3417 catch (failed_constructor& err){
3418 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3419 _metrics = old_metrics;
3423 } else if (child->name() == MeterSection::xml_state_node_name) {
3426 MeterSection* ms = new MeterSection (*child);
3427 _metrics.push_back (ms);
3430 catch (failed_constructor& err) {
3431 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3432 _metrics = old_metrics;
3438 if (niter == nlist.end()) {
3439 MetricSectionSorter cmp;
3440 _metrics.sort (cmp);
3443 /* check for legacy sessions where bbt was the base musical unit for tempo */
3444 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3446 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3447 if (t->legacy_bbt().bars != 0) {
3448 fix_legacy_session();
3455 /* check for multiple tempo/meters at the same location, which
3456 ardour2 somehow allowed.
3459 Metrics::iterator prev = _metrics.end();
3460 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3461 if (prev != _metrics.end()) {
3463 MeterSection* prev_m;
3465 TempoSection* prev_t;
3466 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
3467 if (prev_m->pulse() == ms->pulse()) {
3468 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3469 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3472 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
3473 if (prev_t->pulse() == ts->pulse()) {
3474 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3475 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3483 recompute_map (_metrics);
3486 PropertyChanged (PropertyChange ());
3492 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
3494 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
3495 const MeterSection* m;
3496 const TempoSection* t;
3497 const TempoSection* prev_t = 0;
3499 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3501 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
3502 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
3503 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
3504 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
3506 o << "previous : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
3507 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;
3510 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
3511 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
3512 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
3515 o << "------" << std::endl;
3519 TempoMap::n_tempos() const
3521 Glib::Threads::RWLock::ReaderLock lm (lock);
3524 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3525 if (dynamic_cast<const TempoSection*>(*i) != 0) {
3534 TempoMap::n_meters() const
3536 Glib::Threads::RWLock::ReaderLock lm (lock);
3539 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3540 if (dynamic_cast<const MeterSection*>(*i) != 0) {
3549 TempoMap::insert_time (framepos_t where, framecnt_t amount)
3552 Glib::Threads::RWLock::WriterLock lm (lock);
3553 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3554 if ((*i)->frame() >= where && (*i)->movable ()) {
3555 (*i)->set_frame ((*i)->frame() + amount);
3559 /* now reset the BBT time of all metrics, based on their new
3560 * audio time. This is the only place where we do this reverse
3564 Metrics::iterator i;
3565 const MeterSection* meter;
3566 const TempoSection* tempo;
3570 meter = &first_meter ();
3571 tempo = &first_tempo ();
3576 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
3579 MetricSection* prev = 0;
3581 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3584 //TempoMetric metric (*meter, *tempo);
3585 MeterSection* ms = const_cast<MeterSection*>(meter);
3586 TempoSection* ts = const_cast<TempoSection*>(tempo);
3589 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3593 ts->set_pulse (t->pulse());
3595 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3596 ts->set_pulse (m->pulse());
3598 ts->set_frame (prev->frame());
3602 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3603 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3604 ms->set_beat (start);
3605 ms->set_pulse (m->pulse());
3607 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3611 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3612 pair<double, BBT_Time> start = make_pair (beat, beats_to_bbt_locked (_metrics, beat));
3613 ms->set_beat (start);
3614 ms->set_pulse (t->pulse());
3616 ms->set_frame (prev->frame());
3620 // metric will be at frames=0 bbt=1|1|0 by default
3621 // which is correct for our purpose
3624 // cerr << bbt << endl;
3626 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3630 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3632 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3633 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3634 bbt_time (m->frame(), bbt);
3636 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3642 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3643 /* round up to next beat */
3649 if (bbt.beats != 1) {
3650 /* round up to next bar */
3655 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3656 m->set_beat (start);
3657 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3659 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3661 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3662 abort(); /*NOTREACHED*/
3668 recompute_map (_metrics);
3672 PropertyChanged (PropertyChange ());
3675 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3679 std::list<MetricSection*> metric_kill_list;
3681 TempoSection* last_tempo = NULL;
3682 MeterSection* last_meter = NULL;
3683 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3684 bool meter_after = false; // is there a meter marker likewise?
3686 Glib::Threads::RWLock::WriterLock lm (lock);
3687 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3688 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3689 metric_kill_list.push_back(*i);
3690 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3693 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3697 else if ((*i)->frame() >= where) {
3698 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3699 (*i)->set_frame ((*i)->frame() - amount);
3700 if ((*i)->frame() == where) {
3701 // marker was immediately after end of range
3702 tempo_after = dynamic_cast<TempoSection*> (*i);
3703 meter_after = dynamic_cast<MeterSection*> (*i);
3709 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3710 if (last_tempo && !tempo_after) {
3711 metric_kill_list.remove(last_tempo);
3712 last_tempo->set_frame(where);
3715 if (last_meter && !meter_after) {
3716 metric_kill_list.remove(last_meter);
3717 last_meter->set_frame(where);
3721 //remove all the remaining metrics
3722 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3723 _metrics.remove(*i);
3728 recompute_map (_metrics);
3731 PropertyChanged (PropertyChange ());
3735 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3736 * pos can be -ve, if required.
3739 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3741 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
3744 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3746 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3748 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
3751 /** Add the BBT interval op to pos and return the result */
3753 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3755 Glib::Threads::RWLock::ReaderLock lm (lock);
3757 BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3758 pos_bbt.ticks += op.ticks;
3759 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3761 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3763 pos_bbt.beats += op.beats;
3764 /* the meter in effect will start on the bar */
3765 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();
3766 while (pos_bbt.beats >= divisions_per_bar + 1) {
3768 divisions_per_bar = meter_section_at_beat (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3769 pos_bbt.beats -= divisions_per_bar;
3771 pos_bbt.bars += op.bars;
3773 return frame_time_locked (_metrics, pos_bbt);
3776 /** Count the number of beats that are equivalent to distance when going forward,
3780 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3782 return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
3786 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3792 operator<< (std::ostream& o, const Meter& m) {
3793 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3797 operator<< (std::ostream& o, const Tempo& t) {
3798 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3802 operator<< (std::ostream& o, const MetricSection& section) {
3804 o << "MetricSection @ " << section.frame() << ' ';
3806 const TempoSection* ts;
3807 const MeterSection* ms;
3809 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3810 o << *((const Tempo*) ts);
3811 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3812 o << *((const Meter*) ms);