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::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, ARDOUR::TempoSection::Type type, PositionLockStyle pls)
887 TempoSection* ts = 0;
889 Glib::Threads::RWLock::WriterLock lm (lock);
890 ts = add_tempo_locked (tempo, pulse, frame, type, pls, true);
894 PropertyChanged (PropertyChange ());
900 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, TempoSection::Type type, PositionLockStyle pls)
902 const bool locked_to_meter = ts.locked_to_meter();
905 Glib::Threads::RWLock::WriterLock lm (lock);
906 TempoSection& first (first_tempo());
907 if (ts.frame() != first.frame()) {
908 remove_tempo_locked (ts);
909 add_tempo_locked (tempo, pulse, frame, type, pls, true, locked_to_meter);
911 first.set_type (type);
912 first.set_pulse (0.0);
913 first.set_frame (frame);
914 first.set_position_lock_style (AudioTime);
916 /* cannot move the first tempo section */
917 *static_cast<Tempo*>(&first) = tempo;
918 recompute_map (_metrics);
923 PropertyChanged (PropertyChange ());
927 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, framepos_t frame
928 , TempoSection::Type type, PositionLockStyle pls, bool recompute, bool locked_to_meter)
930 TempoSection* t = new TempoSection (pulse, frame, tempo.beats_per_minute(), tempo.note_type(), type, pls);
931 t->set_locked_to_meter (locked_to_meter);
936 if (pls == AudioTime) {
937 solve_map_frame (_metrics, t, t->frame());
939 solve_map_pulse (_metrics, t, t->pulse());
941 recompute_meters (_metrics);
948 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, const framepos_t& frame, PositionLockStyle pls)
952 Glib::Threads::RWLock::WriterLock lm (lock);
953 m = add_meter_locked (meter, beat, where, frame, pls, true);
958 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
959 dump (_metrics, std::cerr);
963 PropertyChanged (PropertyChange ());
968 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, const framepos_t& frame, PositionLockStyle pls)
971 Glib::Threads::RWLock::WriterLock lm (lock);
972 const double beat = bbt_to_beats_locked (_metrics, where);
975 remove_meter_locked (ms);
976 add_meter_locked (meter, beat, where, frame, pls, true);
978 MeterSection& first (first_meter());
979 TempoSection& first_t (first_tempo());
980 /* cannot move the first meter section */
981 *static_cast<Meter*>(&first) = meter;
982 first.set_position_lock_style (AudioTime);
983 first.set_pulse (0.0);
984 first.set_frame (frame);
985 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
986 first.set_beat (beat);
987 first_t.set_frame (first.frame());
988 first_t.set_pulse (0.0);
989 first_t.set_position_lock_style (AudioTime);
991 recompute_map (_metrics);
993 PropertyChanged (PropertyChange ());
997 TempoMap::add_meter_locked (const Meter& meter, double beat, const Timecode::BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute)
999 const MeterSection& prev_m = meter_section_at_locked (_metrics, frame - 1);
1000 const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1002 if (pls == AudioTime) {
1003 /* add meter-locked tempo */
1004 add_tempo_locked (tempo_at_locked (_metrics, frame), pulse, frame, TempoSection::Ramp, AudioTime, true, true);
1007 MeterSection* new_meter = new MeterSection (pulse, frame, beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls);
1009 do_insert (new_meter);
1013 if (pls == AudioTime) {
1014 solve_map_frame (_metrics, new_meter, frame);
1016 solve_map_bbt (_metrics, new_meter, where);
1024 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1026 Tempo newtempo (beats_per_minute, note_type);
1029 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1030 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1035 Glib::Threads::RWLock::WriterLock lm (lock);
1036 *((Tempo*) t) = newtempo;
1037 recompute_map (_metrics);
1039 PropertyChanged (PropertyChange ());
1046 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1048 Tempo newtempo (beats_per_minute, note_type);
1051 TempoSection* first;
1052 Metrics::iterator i;
1054 /* find the TempoSection immediately preceding "where"
1057 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1059 if ((*i)->frame() > where) {
1065 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1078 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1088 Glib::Threads::RWLock::WriterLock lm (lock);
1089 /* cannot move the first tempo section */
1090 *((Tempo*)prev) = newtempo;
1091 recompute_map (_metrics);
1094 PropertyChanged (PropertyChange ());
1098 TempoMap::first_meter () const
1100 const MeterSection *m = 0;
1102 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1103 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1108 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1109 abort(); /*NOTREACHED*/
1114 TempoMap::first_meter ()
1116 MeterSection *m = 0;
1118 /* CALLER MUST HOLD LOCK */
1120 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1121 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1126 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1127 abort(); /*NOTREACHED*/
1132 TempoMap::first_tempo () const
1134 const TempoSection *t = 0;
1136 /* CALLER MUST HOLD LOCK */
1138 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1139 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1143 if (!t->movable()) {
1149 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1150 abort(); /*NOTREACHED*/
1155 TempoMap::first_tempo ()
1157 TempoSection *t = 0;
1159 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1160 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1164 if (!t->movable()) {
1170 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1171 abort(); /*NOTREACHED*/
1175 TempoMap::recompute_tempos (Metrics& metrics)
1177 TempoSection* prev_t = 0;
1179 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1182 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1186 if (!t->movable()) {
1194 if (t->position_lock_style() == AudioTime) {
1195 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1196 if (!t->locked_to_meter()) {
1197 t->set_pulse (prev_t->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
1201 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1202 t->set_frame (prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
1209 prev_t->set_c_func (0.0);
1212 /* tempos must be positioned correctly.
1213 the current approach is to use a meter's bbt time as its base position unit.
1214 this means that a meter's beat may change, but its bbt may not.
1215 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1216 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1219 TempoMap::recompute_meters (Metrics& metrics)
1221 MeterSection* meter = 0;
1222 MeterSection* prev_m = 0;
1224 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1225 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1226 if (meter->position_lock_style() == AudioTime) {
1228 pair<double, BBT_Time> b_bbt;
1229 TempoSection* meter_locked_tempo = 0;
1230 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1232 if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
1233 if ((t->locked_to_meter() || !t->movable()) && t->frame() == meter->frame()) {
1234 meter_locked_tempo = t;
1241 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1242 if (beats + prev_m->beat() != meter->beat()) {
1243 /* reordering caused a bbt change */
1244 b_bbt = make_pair (beats + prev_m->beat()
1245 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1246 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1248 } else if (meter->movable()) {
1249 b_bbt = make_pair (meter->beat(), meter->bbt());
1250 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1253 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1255 if (meter_locked_tempo) {
1256 meter_locked_tempo->set_pulse (pulse);
1258 meter->set_beat (b_bbt);
1259 meter->set_pulse (pulse);
1264 pair<double, BBT_Time> new_beat;
1266 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1267 if (beats + prev_m->beat() != meter->beat()) {
1268 /* reordering caused a bbt change */
1269 new_beat = make_pair (beats + prev_m->beat()
1270 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1272 new_beat = make_pair (beats + prev_m->beat(), meter->bbt());
1274 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1276 /* shouldn't happen - the first is audio-locked */
1277 pulse = pulse_at_beat_locked (metrics, meter->beat());
1278 new_beat = make_pair (meter->beat(), meter->bbt());
1281 meter->set_beat (new_beat);
1282 meter->set_pulse (pulse);
1283 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1292 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1294 /* CALLER MUST HOLD WRITE LOCK */
1298 /* we will actually stop once we hit
1305 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1308 /* silly call from Session::process() during startup
1313 recompute_tempos (metrics);
1314 recompute_meters (metrics);
1318 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1320 Glib::Threads::RWLock::ReaderLock lm (lock);
1321 TempoMetric m (first_meter(), first_tempo());
1323 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1324 at something, because we insert the default tempo and meter during
1325 TempoMap construction.
1327 now see if we can find better candidates.
1330 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1332 if ((*i)->frame() > frame) {
1346 /* XX meters only */
1348 TempoMap::metric_at (BBT_Time bbt) const
1350 Glib::Threads::RWLock::ReaderLock lm (lock);
1351 TempoMetric m (first_meter(), first_tempo());
1353 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1354 at something, because we insert the default tempo and meter during
1355 TempoMap construction.
1357 now see if we can find better candidates.
1360 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1362 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1363 BBT_Time section_start (mw->bbt());
1365 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1377 TempoMap::pulse_at_beat (const double& beat) const
1379 Glib::Threads::RWLock::ReaderLock lm (lock);
1380 return pulse_at_beat_locked (_metrics, beat);
1384 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1386 MeterSection* prev_m = 0;
1388 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1390 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1391 if (prev_m && m->beat() > beat) {
1398 double const ret = prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1403 TempoMap::beat_at_pulse (const double& pulse) const
1405 Glib::Threads::RWLock::ReaderLock lm (lock);
1406 return beat_at_pulse_locked (_metrics, pulse);
1410 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1412 MeterSection* prev_m = 0;
1414 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1416 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1417 if (prev_m && m->pulse() > pulse) {
1418 if (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > m->beat()) {
1426 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1431 TempoMap::pulse_at_frame (const framecnt_t& frame) const
1433 Glib::Threads::RWLock::ReaderLock lm (lock);
1434 return pulse_at_frame_locked (_metrics, frame);
1437 /* tempo section based */
1439 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1441 /* HOLD (at least) THE READER LOCK */
1442 TempoSection* prev_t = 0;
1444 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1446 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1450 if (prev_t && t->frame() > frame) {
1451 /*the previous ts is the one containing the frame */
1452 const double ret = prev_t->pulse_at_frame (frame, _frame_rate);
1459 /* treated as constant for this ts */
1460 const double pulses_in_section = (frame - prev_t->frame()) / prev_t->frames_per_pulse (_frame_rate);
1462 return pulses_in_section + prev_t->pulse();
1466 TempoMap::frame_at_pulse (const double& pulse) const
1468 Glib::Threads::RWLock::ReaderLock lm (lock);
1469 return frame_at_pulse_locked (_metrics, pulse);
1472 /* tempo section based */
1474 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1476 /* HOLD THE READER LOCK */
1478 const TempoSection* prev_t = 0;
1480 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1483 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1487 if (prev_t && t->pulse() > pulse) {
1488 return prev_t->frame_at_pulse (pulse, _frame_rate);
1494 /* must be treated as constant, irrespective of _type */
1495 double const pulses_in_section = pulse - prev_t->pulse();
1496 double const dtime = pulses_in_section * prev_t->frames_per_pulse (_frame_rate);
1498 framecnt_t const ret = (framecnt_t) floor (dtime) + prev_t->frame();
1504 TempoMap::beat_at_frame (const framecnt_t& frame) const
1506 Glib::Threads::RWLock::ReaderLock lm (lock);
1507 return beat_at_frame_locked (_metrics, frame);
1510 /* meter section based */
1512 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1514 const TempoSection& ts = tempo_section_at_locked (metrics, frame);
1515 MeterSection* prev_m = 0;
1516 MeterSection* next_m = 0;
1518 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1520 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1521 if (prev_m && m->frame() > frame) {
1528 if (frame < prev_m->frame()) {
1531 const double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
1533 if (next_m && next_m->beat() < beat) {
1534 return next_m->beat();
1541 TempoMap::frame_at_beat (const double& beat) const
1543 Glib::Threads::RWLock::ReaderLock lm (lock);
1544 return frame_at_beat_locked (_metrics, beat);
1547 /* meter section based */
1549 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1551 const TempoSection& prev_t = tempo_section_at_beat_locked (metrics, beat);
1552 MeterSection* prev_m = 0;
1554 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1556 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1557 if (prev_m && m->beat() > beat) {
1564 return prev_t.frame_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse(), _frame_rate);
1568 TempoMap::bbt_to_beats (const Timecode::BBT_Time& bbt)
1570 Glib::Threads::RWLock::ReaderLock lm (lock);
1571 return bbt_to_beats_locked (_metrics, bbt);
1576 TempoMap::bbt_to_beats_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1578 /* CALLER HOLDS READ LOCK */
1580 MeterSection* prev_m = 0;
1582 /* because audio-locked meters have 'fake' integral beats,
1583 there is no pulse offset here.
1585 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1587 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1589 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1590 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1598 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1599 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1600 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1606 TempoMap::beats_to_bbt (const double& beats)
1608 Glib::Threads::RWLock::ReaderLock lm (lock);
1609 return beats_to_bbt_locked (_metrics, beats);
1613 TempoMap::beats_to_bbt_locked (const Metrics& metrics, const double& b) const
1615 /* CALLER HOLDS READ LOCK */
1616 MeterSection* prev_m = 0;
1617 const double beats = max (0.0, b);
1619 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1620 MeterSection* m = 0;
1622 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1624 if (m->beat() > beats) {
1625 /* this is the meter after the one our beat is on*/
1634 const double beats_in_ms = beats - prev_m->beat();
1635 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1636 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1637 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1638 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1642 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1643 ret.beats = (uint32_t) floor (remaining_beats);
1644 ret.bars = total_bars;
1646 /* 0 0 0 to 1 1 0 - based mapping*/
1650 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1652 ret.ticks -= BBT_Time::ticks_per_beat;
1655 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1664 TempoMap::pulse_to_bbt (const double& pulse)
1666 Glib::Threads::RWLock::ReaderLock lm (lock);
1667 MeterSection* prev_m = 0;
1669 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1670 MeterSection* m = 0;
1672 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1675 double const pulses_to_m = m->pulse() - prev_m->pulse();
1676 if (prev_m->pulse() + pulses_to_m > pulse) {
1677 /* this is the meter after the one our beat is on*/
1686 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1687 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1688 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1689 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1690 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1694 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1695 ret.beats = (uint32_t) floor (remaining_beats);
1696 ret.bars = total_bars;
1698 /* 0 0 0 to 1 1 0 mapping*/
1702 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1704 ret.ticks -= BBT_Time::ticks_per_beat;
1707 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1716 TempoMap::bbt_time (framepos_t frame, BBT_Time& bbt)
1723 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1726 Glib::Threads::RWLock::ReaderLock lm (lock);
1727 const double beat = beat_at_frame_locked (_metrics, frame);
1729 bbt = beats_to_bbt_locked (_metrics, beat);
1733 TempoMap::frame_time (const BBT_Time& bbt)
1736 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1740 if (bbt.beats < 1) {
1741 throw std::logic_error ("beats are counted from one");
1743 Glib::Threads::RWLock::ReaderLock lm (lock);
1745 return frame_time_locked (_metrics, bbt);
1748 /* meter section based */
1750 TempoMap::frame_time_locked (const Metrics& metrics, const BBT_Time& bbt) const
1752 /* HOLD THE READER LOCK */
1754 const framepos_t ret = frame_at_beat_locked (metrics, bbt_to_beats_locked (metrics, bbt));
1759 TempoMap::check_solved (const Metrics& metrics) const
1761 TempoSection* prev_t = 0;
1762 MeterSection* prev_m = 0;
1764 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1767 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1772 if ((t->frame() <= prev_t->frame()) || (t->pulse() <= prev_t->pulse())) {
1776 /* precision check ensures pulses and frames align.*/
1777 if (t->frame() != prev_t->frame_at_pulse (t->pulse(), _frame_rate)) {
1778 if (!t->locked_to_meter()) {
1786 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1787 if (prev_m && m->position_lock_style() == AudioTime) {
1788 TempoSection* t = const_cast<TempoSection*>(&tempo_section_at_locked (metrics, m->frame() - 1));
1789 const double nascent_m_pulse = ((m->beat() - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse();
1790 const framepos_t nascent_m_frame = t->frame_at_pulse (nascent_m_pulse, _frame_rate);
1792 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
1806 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
1808 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1810 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1811 if (!t->movable()) {
1812 t->set_active (true);
1815 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
1816 t->set_active (false);
1818 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
1819 t->set_active (true);
1820 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
1829 TempoMap::solve_map_frame (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
1831 TempoSection* prev_t = 0;
1832 TempoSection* section_prev = 0;
1833 framepos_t first_m_frame = 0;
1835 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1837 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1838 if (!m->movable()) {
1839 first_m_frame = m->frame();
1844 if (section->movable() && frame <= first_m_frame) {
1848 section->set_active (true);
1849 section->set_frame (frame);
1851 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1853 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1860 section_prev = prev_t;
1863 if (t->position_lock_style() == MusicTime) {
1864 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1865 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1867 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1868 if (!t->locked_to_meter()) {
1869 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1878 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
1879 if (!section->locked_to_meter()) {
1880 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
1884 recompute_tempos (imaginary);
1886 if (check_solved (imaginary)) {
1890 MetricSectionFrameSorter fcmp;
1891 imaginary.sort (fcmp);
1893 recompute_tempos (imaginary);
1895 if (check_solved (imaginary)) {
1903 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
1905 TempoSection* prev_t = 0;
1906 TempoSection* section_prev = 0;
1908 section->set_pulse (pulse);
1910 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1912 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1916 if (!t->movable()) {
1923 section_prev = prev_t;
1926 if (t->position_lock_style() == MusicTime) {
1927 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1928 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
1930 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1931 if (!t->locked_to_meter()) {
1932 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
1941 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
1942 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
1945 recompute_tempos (imaginary);
1947 if (check_solved (imaginary)) {
1951 MetricSectionSorter cmp;
1952 imaginary.sort (cmp);
1954 recompute_tempos (imaginary);
1956 * XX need a restriction here, but only for this case,
1957 * as audio locked tempos don't interact in the same way.
1959 * With music-locked tempos, the solution to cross-dragging can fly off the screen
1961 * |50 bpm |250 bpm |60 bpm
1962 * drag 250 to the pulse after 60->
1963 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
1965 if (check_solved (imaginary)) {
1973 TempoMap::solve_map_frame (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
1975 /* disallow moving first meter past any subsequent one, and any movable meter before the first one */
1976 const MeterSection* other = &meter_section_at_locked (imaginary, frame);
1977 if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->frame() >= frame)) {
1981 if (!section->movable()) {
1982 /* lock the first tempo to our first meter */
1983 if (!set_active_tempos (imaginary, frame)) {
1988 /* it would make sense to bail out if there is no audio-locked meter,
1989 however it may be desirable to move a music-locked meter by frame at some point.
1991 TempoSection* meter_locked_tempo = 0;
1992 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
1994 if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
1995 if ((t->locked_to_meter() || !t->movable()) && t->frame() == section->frame()) {
1996 meter_locked_tempo = t;
2002 if (!meter_locked_tempo) {
2006 MeterSection* prev_m = 0;
2008 bool solved = false;
2010 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2012 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2014 if (prev_m && section->movable()) {
2015 const double beats = (pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor();
2016 if (beats + prev_m->beat() < section->beat()) {
2017 /* set the frame/pulse corresponding to its musical position,
2018 * as an earlier time than this has been requested.
2021 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2022 const double new_pulse = ((section->beat() - prev_m->beat())
2023 / prev_m->note_divisor()) + prev_m->pulse();
2024 const framepos_t smallest_frame = frame_at_pulse_locked (future_map, new_pulse);
2025 if ((solved = solve_map_frame (future_map, tempo_copy, smallest_frame))) {
2026 meter_locked_tempo->set_pulse (new_pulse);
2027 solve_map_frame (imaginary, meter_locked_tempo, smallest_frame);
2028 section->set_frame (smallest_frame);
2029 section->set_pulse (new_pulse);
2034 Metrics::const_iterator d = future_map.begin();
2035 while (d != future_map.end()) {
2045 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2046 MeterSection* meter_copy = const_cast<MeterSection*> (&meter_section_at_locked (future_map, section->frame()));
2047 meter_copy->set_frame (frame);
2049 if ((solved = solve_map_frame (future_map, tempo_copy, frame))) {
2050 section->set_frame (frame);
2051 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2052 / prev_m->note_divisor()) + prev_m->pulse());
2053 solve_map_frame (imaginary, meter_locked_tempo, frame);
2058 Metrics::const_iterator d = future_map.begin();
2059 while (d != future_map.end()) {
2069 /* not movable (first meter atm) */
2071 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2073 tempo_copy->set_frame (frame);
2074 tempo_copy->set_pulse (0.0);
2076 if ((solved = solve_map_frame (future_map, tempo_copy, frame))) {
2077 section->set_frame (frame);
2078 meter_locked_tempo->set_frame (frame);
2079 meter_locked_tempo->set_pulse (0.0);
2080 solve_map_frame (imaginary, meter_locked_tempo, frame);
2085 Metrics::const_iterator d = future_map.begin();
2086 while (d != future_map.end()) {
2095 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2096 section->set_beat (b_bbt);
2097 section->set_pulse (0.0);
2107 MetricSectionFrameSorter fcmp;
2108 imaginary.sort (fcmp);
2110 recompute_meters (imaginary);
2116 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2118 /* disallow setting section to an existing meter's bbt */
2119 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2121 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2122 if (m != section && m->bbt().bars == when.bars) {
2128 MeterSection* prev_m = 0;
2129 MeterSection* section_prev = 0;
2131 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2133 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2134 pair<double, BBT_Time> b_bbt;
2135 double new_pulse = 0.0;
2137 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2138 section_prev = prev_m;
2139 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2140 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2141 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2143 section->set_beat (b_bbt);
2144 section->set_pulse (pulse);
2145 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2150 if (m->position_lock_style() == AudioTime) {
2151 TempoSection* meter_locked_tempo = 0;
2152 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2154 if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
2155 if ((t->locked_to_meter() || !t->movable()) && t->frame() == m->frame()) {
2156 meter_locked_tempo = t;
2163 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2165 if (beats + prev_m->beat() != m->beat()) {
2166 /* tempo/ meter change caused a change in beat (bar). */
2167 b_bbt = make_pair (beats + prev_m->beat()
2168 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2169 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2170 } else if (m->movable()) {
2171 b_bbt = make_pair (m->beat(), m->bbt());
2172 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2175 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2177 if (meter_locked_tempo) {
2178 meter_locked_tempo->set_pulse (new_pulse);
2179 recompute_tempos (imaginary);
2181 m->set_beat (b_bbt);
2182 m->set_pulse (new_pulse);
2186 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2187 if (beats + prev_m->beat() != m->beat()) {
2188 /* tempo/ meter change caused a change in beat (bar). */
2189 b_bbt = make_pair (beats + prev_m->beat()
2190 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2192 b_bbt = make_pair (beats + prev_m->beat()
2195 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2196 m->set_beat (b_bbt);
2197 m->set_pulse (new_pulse);
2198 m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2205 if (!section_prev) {
2207 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
2208 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2209 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
2211 section->set_beat (b_bbt);
2212 section->set_pulse (pulse);
2213 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2216 MetricSectionSorter cmp;
2217 imaginary.sort (cmp);
2219 recompute_meters (imaginary);
2224 /** places a copy of _metrics into copy and returns a pointer
2225 * to section's equivalent in copy.
2228 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
2230 TempoSection* ret = 0;
2232 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2235 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2237 ret = new TempoSection (*t);
2238 copy.push_back (ret);
2242 TempoSection* cp = new TempoSection (*t);
2243 copy.push_back (cp);
2245 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2246 MeterSection* cp = new MeterSection (*m);
2247 copy.push_back (cp);
2255 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
2257 MeterSection* ret = 0;
2259 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2262 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2263 TempoSection* cp = new TempoSection (*t);
2264 copy.push_back (cp);
2267 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2269 ret = new MeterSection (*m);
2270 copy.push_back (ret);
2273 MeterSection* cp = new MeterSection (*m);
2274 copy.push_back (cp);
2282 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2285 TempoSection* tempo_copy = 0;
2288 Glib::Threads::RWLock::ReaderLock lm (lock);
2289 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
2295 const double beat = bbt_to_beats_locked (copy, bbt);
2296 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_beat_locked (copy, beat));
2298 Metrics::const_iterator d = copy.begin();
2299 while (d != copy.end()) {
2308 * 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,
2309 * taking any possible reordering as a consequence of this into account.
2310 * @param section - the section to be altered
2311 * @param bpm - the new Tempo
2312 * @param bbt - the bbt where the altered tempo will fall
2313 * @return returns - the position in frames where the new tempo section will lie.
2316 TempoMap::predict_tempo_frame (TempoSection* section, const BBT_Time& bbt)
2318 Glib::Threads::RWLock::ReaderLock lm (lock);
2321 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
2325 const double beat = bbt_to_beats_locked (future_map, bbt);
2327 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
2328 ret = tempo_copy->frame();
2330 ret = section->frame();
2333 Metrics::const_iterator d = future_map.begin();
2334 while (d != future_map.end()) {
2342 TempoMap::predict_tempo_pulse (TempoSection* section, const framepos_t& frame)
2344 Glib::Threads::RWLock::ReaderLock lm (lock);
2347 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
2349 if (solve_map_frame (future_map, tempo_copy, frame)) {
2350 ret = tempo_copy->pulse();
2352 ret = section->pulse();
2355 Metrics::const_iterator d = future_map.begin();
2356 while (d != future_map.end()) {
2364 TempoMap::gui_move_tempo_frame (TempoSection* ts, const framepos_t& frame)
2368 Glib::Threads::RWLock::WriterLock lm (lock);
2369 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2370 if (solve_map_frame (future_map, tempo_copy, frame)) {
2371 solve_map_frame (_metrics, ts, frame);
2372 recompute_meters (_metrics);
2376 Metrics::const_iterator d = future_map.begin();
2377 while (d != future_map.end()) {
2382 MetricPositionChanged (); // Emit Signal
2386 TempoMap::gui_move_tempo_beat (TempoSection* ts, const double& beat)
2390 Glib::Threads::RWLock::WriterLock lm (lock);
2391 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2392 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
2393 solve_map_pulse (_metrics, ts, pulse_at_beat_locked (_metrics, beat));
2394 recompute_meters (_metrics);
2398 Metrics::const_iterator d = future_map.begin();
2399 while (d != future_map.end()) {
2404 MetricPositionChanged (); // Emit Signal
2408 TempoMap::gui_move_meter_frame (MeterSection* ms, const framepos_t& frame)
2412 Glib::Threads::RWLock::WriterLock lm (lock);
2413 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2414 if (solve_map_frame (future_map, copy, frame)) {
2415 solve_map_frame (_metrics, ms, frame);
2416 recompute_tempos (_metrics);
2420 Metrics::const_iterator d = future_map.begin();
2421 while (d != future_map.end()) {
2426 MetricPositionChanged (); // Emit Signal
2430 TempoMap::gui_move_meter_bbt (MeterSection* ms, const Timecode::BBT_Time& bbt)
2434 Glib::Threads::RWLock::WriterLock lm (lock);
2435 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2436 if (solve_map_bbt (future_map, copy, bbt)) {
2437 solve_map_bbt (_metrics, ms, bbt);
2438 recompute_tempos (_metrics);
2442 Metrics::const_iterator d = future_map.begin();
2443 while (d != future_map.end()) {
2448 MetricPositionChanged (); // Emit Signal
2452 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2455 bool can_solve = false;
2457 Glib::Threads::RWLock::WriterLock lm (lock);
2458 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2459 tempo_copy->set_beats_per_minute (bpm.beats_per_minute());
2460 recompute_tempos (future_map);
2462 if (check_solved (future_map)) {
2463 ts->set_beats_per_minute (bpm.beats_per_minute());
2464 recompute_map (_metrics);
2469 Metrics::const_iterator d = future_map.begin();
2470 while (d != future_map.end()) {
2475 MetricPositionChanged (); // Emit Signal
2481 TempoMap::gui_dilate_tempo (MeterSection* ms, const framepos_t& frame)
2484 TempoSection* ts = 0;
2486 if (frame <= first_meter().frame()) {
2490 if (ms->position_lock_style() == AudioTime) {
2491 /* disabled for now due to faked tempo locked to meter pulse */
2496 Glib::Threads::RWLock::WriterLock lm (lock);
2497 ts = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, ms->frame() - 1));
2501 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
2502 TempoSection* prev_to_prev_t = 0;
2503 const frameoffset_t fr_off = frame - ms->frame();
2504 double new_bpm = 0.0;
2506 if (prev_t && prev_t->pulse() > 0.0) {
2507 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (future_map, prev_t->frame() - 1));
2510 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
2511 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
2513 double contribution = 0.0;
2514 frameoffset_t frame_contribution = 0;
2515 frameoffset_t prev_t_frame_contribution = fr_off;
2517 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2518 /* prev to prev_t's position will remain constant in terms of frame and pulse. lets use frames. */
2519 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (ms->frame() - prev_to_prev_t->frame());
2520 frame_contribution = contribution * (double) fr_off;
2521 prev_t_frame_contribution = fr_off - frame_contribution;
2524 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
2526 if (prev_t->position_lock_style() == MusicTime) {
2527 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2528 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2529 / (double) (ms->frame() + prev_t_frame_contribution - prev_t->frame()));
2532 /* prev to prev is irrelevant */
2533 const double meter_pulse = prev_t->pulse_at_frame (ms->frame(), _frame_rate);
2534 const double frame_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
2536 if (frame_pulse != prev_t->pulse()) {
2537 new_bpm = prev_t->beats_per_minute() * ((meter_pulse - prev_t->pulse()) / (frame_pulse - prev_t->pulse()));
2539 new_bpm = prev_t->beats_per_minute();
2544 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2545 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2546 / (double) (ms->frame() + prev_t_frame_contribution - prev_t->frame()));
2548 /* prev_to_prev_t is irrelevant */
2550 if (frame != prev_t->frame()) {
2551 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame()) / (double) (frame - prev_t->frame()));
2553 new_bpm = prev_t->beats_per_minute();
2557 } else if (prev_t->c_func() < 0.0) {
2558 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2559 new_bpm = prev_t->tempo_at_frame (prev_t->frame() + frame_contribution, _frame_rate) * (double) prev_t->note_type();
2561 /* prev_to_prev_t is irrelevant */
2562 new_bpm = prev_t->tempo_at_frame (prev_t->frame() + fr_off, _frame_rate) * (double) prev_t->note_type();
2565 } else if (prev_t->c_func() > 0.0) {
2566 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2567 new_bpm = prev_t->tempo_at_frame (prev_t->frame() - frame_contribution, _frame_rate) * (double) prev_t->note_type();
2569 /* prev_to_prev_t is irrelevant */
2570 new_bpm = prev_t->tempo_at_frame (prev_t->frame() - fr_off, _frame_rate) * (double) prev_t->note_type();
2574 /* limits - a bit clunky, but meh */
2575 const double diff = (prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type()) - prev_t->beats_per_minute();
2576 if (diff > -1.0 && diff < 1.0) {
2577 new_bpm = prev_t->beats_per_minute() * ((ms->frame() - prev_t->frame())
2578 / (double) ((ms->frame() + prev_t_frame_contribution) - prev_t->frame()));
2581 prev_t->set_beats_per_minute (new_bpm);
2582 recompute_tempos (future_map);
2583 recompute_meters (future_map);
2585 if (check_solved (future_map)) {
2587 prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (_metrics, ms->frame() - 1));
2588 prev_t->set_beats_per_minute (new_bpm);
2589 recompute_tempos (_metrics);
2591 if (ms->position_lock_style() == AudioTime) {
2592 ms->set_frame (frame);
2595 recompute_meters (_metrics);
2599 Metrics::const_iterator d = future_map.begin();
2600 while (d != future_map.end()) {
2605 MetricPositionChanged (); // Emit Signal
2609 TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame, const double& pulse)
2612 Ts (future prev_t) Tnext
2615 |----------|----------
2622 Glib::Threads::RWLock::WriterLock lm (lock);
2628 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
2629 TempoSection* prev_to_prev_t = 0;
2630 const frameoffset_t fr_off = end_frame - frame;
2632 if (prev_t && prev_t->pulse() > 0.0) {
2633 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_locked (future_map, prev_t->frame() - 1));
2636 TempoSection* next_t = 0;
2637 for (Metrics::iterator i = future_map.begin(); i != future_map.end(); ++i) {
2638 TempoSection* t = 0;
2639 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2640 if (t->frame() > ts->frame()) {
2647 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
2648 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
2650 double contribution = 0.0;
2651 double start_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
2653 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2654 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
2657 frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
2658 double end_pulse = prev_t->pulse_at_frame (end_frame, _frame_rate);
2661 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
2663 if (prev_t->position_lock_style() == MusicTime) {
2664 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2666 new_bpm = prev_t->beats_per_minute() * ((frame - prev_to_prev_t->frame())
2667 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
2670 /* prev to prev is irrelevant */
2672 if (start_pulse != prev_t->pulse()) {
2673 new_bpm = prev_t->beats_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
2675 new_bpm = prev_t->beats_per_minute();
2680 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2681 new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame())
2682 / (double) ((frame + prev_t_frame_contribution) - prev_t->frame()));
2684 /* prev_to_prev_t is irrelevant */
2686 if (end_frame != prev_t->frame()) {
2687 new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
2689 new_bpm = prev_t->beats_per_minute();
2697 const framepos_t pulse_pos = prev_t->frame_at_pulse (pulse, _frame_rate);
2699 if (prev_to_prev_t) {
2701 frame_ratio = (((pulse_pos - fr_off) - prev_to_prev_t->frame()) / (double) ((pulse_pos) - prev_to_prev_t->frame()));
2702 pulse_ratio = ((start_pulse - prev_to_prev_t->pulse()) / (end_pulse - prev_to_prev_t->pulse()));
2705 frame_ratio = (((pulse_pos - fr_off) - prev_t->frame()) / (double) ((pulse_pos) - prev_t->frame()));
2706 pulse_ratio = (start_pulse / end_pulse);
2708 new_bpm = prev_t->beats_per_minute() * (pulse_ratio * frame_ratio);
2711 prev_t->set_beats_per_minute (new_bpm);
2712 recompute_tempos (future_map);
2713 recompute_meters (future_map);
2715 if (check_solved (future_map)) {
2716 ts->set_beats_per_minute (new_bpm);
2717 recompute_tempos (_metrics);
2718 recompute_meters (_metrics);
2722 Metrics::const_iterator d = future_map.begin();
2723 while (d != future_map.end()) {
2728 MetricPositionChanged (); // Emit Signal
2732 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2734 Glib::Threads::RWLock::ReaderLock lm (lock);
2736 const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2737 const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2738 const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2740 return frame_at_beat_locked (_metrics, total_beats);
2744 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2746 return round_to_type (fr, dir, Bar);
2750 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2752 return round_to_type (fr, dir, Beat);
2756 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2758 Glib::Threads::RWLock::ReaderLock lm (lock);
2759 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2760 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2761 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2763 ticks -= beats * BBT_Time::ticks_per_beat;
2766 /* round to next (or same iff dir == RoundUpMaybe) */
2768 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2770 if (mod == 0 && dir == RoundUpMaybe) {
2771 /* right on the subdivision, which is fine, so do nothing */
2773 } else if (mod == 0) {
2774 /* right on the subdivision, so the difference is just the subdivision ticks */
2775 ticks += ticks_one_subdivisions_worth;
2778 /* not on subdivision, compute distance to next subdivision */
2780 ticks += ticks_one_subdivisions_worth - mod;
2783 if (ticks >= BBT_Time::ticks_per_beat) {
2784 ticks -= BBT_Time::ticks_per_beat;
2786 } else if (dir < 0) {
2788 /* round to previous (or same iff dir == RoundDownMaybe) */
2790 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2792 if (difference == 0 && dir == RoundDownAlways) {
2793 /* right on the subdivision, but force-rounding down,
2794 so the difference is just the subdivision ticks */
2795 difference = ticks_one_subdivisions_worth;
2798 if (ticks < difference) {
2799 ticks = BBT_Time::ticks_per_beat - ticks;
2801 ticks -= difference;
2805 /* round to nearest */
2808 /* compute the distance to the previous and next subdivision */
2810 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2812 /* closer to the next subdivision, so shift forward */
2814 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2816 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2818 if (ticks > BBT_Time::ticks_per_beat) {
2820 ticks -= BBT_Time::ticks_per_beat;
2821 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2824 } else if (rem > 0) {
2826 /* closer to previous subdivision, so shift backward */
2830 /* can't go backwards past zero, so ... */
2833 /* step back to previous beat */
2835 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2836 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2838 ticks = lrint (ticks - rem);
2839 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2842 /* on the subdivision, do nothing */
2846 const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2852 TempoMap::round_bbt (BBT_Time& when, const int32_t& sub_num, RoundMode dir)
2854 if (sub_num == -1) {
2859 } else if (dir < 0) {
2863 const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2864 if ((double) when.beats > bpb / 2.0) {
2873 } else if (sub_num == 0) {
2874 const double bpb = meter_section_at_beat (bbt_to_beats_locked (_metrics, when)).divisions_per_bar();
2875 if ((double) when.ticks > BBT_Time::ticks_per_beat / 2.0) {
2877 while ((double) when.beats > bpb) {
2879 when.beats -= (uint32_t) floor (bpb);
2887 const uint32_t ticks_one_subdivisions_worth = BBT_Time::ticks_per_beat / sub_num;
2890 /* round to next (or same iff dir == RoundUpMaybe) */
2892 uint32_t mod = when.ticks % ticks_one_subdivisions_worth;
2894 if (mod == 0 && dir == RoundUpMaybe) {
2895 /* right on the subdivision, which is fine, so do nothing */
2897 } else if (mod == 0) {
2898 /* right on the subdivision, so the difference is just the subdivision ticks */
2899 when.ticks += ticks_one_subdivisions_worth;
2902 /* not on subdivision, compute distance to next subdivision */
2904 when.ticks += ticks_one_subdivisions_worth - mod;
2907 if (when.ticks >= BBT_Time::ticks_per_beat) {
2908 when.ticks -= BBT_Time::ticks_per_beat;
2911 } else if (dir < 0) {
2912 /* round to previous (or same iff dir == RoundDownMaybe) */
2914 uint32_t difference = when.ticks % ticks_one_subdivisions_worth;
2916 if (difference == 0 && dir == RoundDownAlways) {
2917 /* right on the subdivision, but force-rounding down,
2918 so the difference is just the subdivision ticks */
2919 difference = ticks_one_subdivisions_worth;
2922 if (when.ticks < difference) {
2923 when.ticks = BBT_Time::ticks_per_beat - when.ticks;
2925 when.ticks -= difference;
2929 /* round to nearest */ double rem;
2930 if ((rem = fmod ((double) when.ticks, (double) ticks_one_subdivisions_worth)) > (ticks_one_subdivisions_worth / 2.0)) {
2931 /* closer to the next subdivision, so shift forward */
2933 when.ticks = when.ticks + (ticks_one_subdivisions_worth - rem);
2935 if (when.ticks > Timecode::BBT_Time::ticks_per_beat) {
2937 when.ticks -= Timecode::BBT_Time::ticks_per_beat;
2940 } else if (rem > 0) {
2941 /* closer to previous subdivision, so shift backward */
2943 if (rem > when.ticks) {
2944 if (when.beats == 0) {
2945 /* can't go backwards past zero, so ... */
2947 /* step back to previous beat */
2949 when.ticks = Timecode::BBT_Time::ticks_per_beat - rem;
2951 when.ticks = when.ticks - rem;
2958 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
2960 Glib::Threads::RWLock::ReaderLock lm (lock);
2962 const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
2963 BBT_Time bbt (beats_to_bbt_locked (_metrics, beat_at_framepos));
2968 /* find bar previous to 'frame' */
2971 return frame_time_locked (_metrics, bbt);
2973 } else if (dir > 0) {
2974 /* find bar following 'frame' */
2978 return frame_time_locked (_metrics, bbt);
2980 /* true rounding: find nearest bar */
2981 framepos_t raw_ft = frame_time_locked (_metrics, bbt);
2984 framepos_t prev_ft = frame_time_locked (_metrics, bbt);
2986 framepos_t next_ft = frame_time_locked (_metrics, bbt);
2988 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
2999 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
3000 } else if (dir > 0) {
3001 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
3003 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
3012 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
3013 framepos_t lower, framepos_t upper)
3015 Glib::Threads::RWLock::ReaderLock lm (lock);
3016 int32_t cnt = ceil (beat_at_frame_locked (_metrics, lower));
3018 /* although the map handles negative beats, bbt doesn't. */
3022 while (pos < upper) {
3023 pos = frame_at_beat_locked (_metrics, cnt);
3024 const TempoSection tempo = tempo_section_at_locked (_metrics, pos);
3025 const MeterSection meter = meter_section_at_locked (_metrics, pos);
3026 const BBT_Time bbt = beats_to_bbt (cnt);
3027 points.push_back (BBTPoint (meter, tempo_at_locked (_metrics, pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
3033 TempoMap::tempo_section_at (framepos_t frame) const
3035 Glib::Threads::RWLock::ReaderLock lm (lock);
3036 return tempo_section_at_locked (_metrics, frame);
3040 TempoMap::tempo_section_at_locked (const Metrics& metrics, framepos_t frame) const
3042 Metrics::const_iterator i;
3043 TempoSection* prev = 0;
3045 for (i = metrics.begin(); i != metrics.end(); ++i) {
3048 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3052 if (prev && t->frame() > frame) {
3062 abort(); /*NOTREACHED*/
3069 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3071 TempoSection* prev_t = 0;
3072 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
3074 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3076 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3077 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
3088 TempoMap::tempo_section_at_pulse_locked (const Metrics& metrics, const double& pulse) const
3090 TempoSection* prev_t = 0;
3092 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3094 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3095 if (prev_t && t->pulse() > pulse) {
3105 /* don't use this to calculate length (the tempo is only correct for this frame).
3106 do that stuff based on the beat_at_frame and frame_at_beat api
3109 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
3111 Glib::Threads::RWLock::ReaderLock lm (lock);
3113 const TempoSection* ts_at = &tempo_section_at_locked (_metrics, frame);
3114 const TempoSection* ts_after = 0;
3115 Metrics::const_iterator i;
3117 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3120 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3124 if ((*i)->frame() > frame) {
3132 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
3134 /* must be treated as constant tempo */
3135 return ts_at->frames_per_beat (_frame_rate);
3139 TempoMap::tempo_at_locked (const Metrics& metrics, const framepos_t& frame) const
3141 TempoSection* prev_t = 0;
3143 Metrics::const_iterator i;
3145 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3147 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3151 if ((prev_t) && t->frame() > frame) {
3152 /* t is the section past frame */
3153 const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
3154 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
3161 const double ret = prev_t->beats_per_minute();
3162 const Tempo ret_tempo (ret, prev_t->note_type ());
3168 TempoMap::tempo_at (const framepos_t& frame) const
3170 Glib::Threads::RWLock::ReaderLock lm (lock);
3171 return tempo_at_locked (_metrics, frame);
3175 TempoMap::meter_section_at_locked (const Metrics& metrics, framepos_t frame) const
3177 Metrics::const_iterator i;
3178 MeterSection* prev = 0;
3180 for (i = metrics.begin(); i != metrics.end(); ++i) {
3183 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
3185 if (prev && (*i)->frame() > frame) {
3195 abort(); /*NOTREACHED*/
3203 TempoMap::meter_section_at (framepos_t frame) const
3205 Glib::Threads::RWLock::ReaderLock lm (lock);
3206 return meter_section_at_locked (_metrics, frame);
3210 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3212 MeterSection* prev_m = 0;
3214 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3216 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
3217 if (prev_m && m->beat() > beat) {
3228 TempoMap::meter_section_at_beat (double beat) const
3230 Glib::Threads::RWLock::ReaderLock lm (lock);
3231 return meter_section_at_beat_locked (_metrics, beat);
3235 TempoMap::meter_at (framepos_t frame) const
3237 TempoMetric m (metric_at (frame));
3242 TempoMap::fix_legacy_session ()
3244 MeterSection* prev_m = 0;
3245 TempoSection* prev_t = 0;
3247 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3251 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3252 if (!m->movable()) {
3253 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3257 m->set_position_lock_style (AudioTime);
3262 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
3263 + (m->bbt().beats - 1)
3264 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
3266 m->set_beat (start);
3267 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
3268 + (m->bbt().beats - 1)
3269 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
3270 m->set_pulse (start_beat / prev_m->note_divisor());
3273 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3279 if (!t->movable()) {
3282 t->set_position_lock_style (AudioTime);
3288 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
3289 + (t->legacy_bbt().beats - 1)
3290 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
3292 t->set_pulse (beat / prev_m->note_divisor());
3294 /* really shouldn't happen but.. */
3295 t->set_pulse (beat / 4.0);
3304 TempoMap::get_state ()
3306 Metrics::const_iterator i;
3307 XMLNode *root = new XMLNode ("TempoMap");
3310 Glib::Threads::RWLock::ReaderLock lm (lock);
3311 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3312 root->add_child_nocopy ((*i)->get_state());
3320 TempoMap::set_state (const XMLNode& node, int /*version*/)
3323 Glib::Threads::RWLock::WriterLock lm (lock);
3326 XMLNodeConstIterator niter;
3327 Metrics old_metrics (_metrics);
3330 nlist = node.children();
3332 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
3333 XMLNode* child = *niter;
3335 if (child->name() == TempoSection::xml_state_node_name) {
3338 TempoSection* ts = new TempoSection (*child);
3339 _metrics.push_back (ts);
3342 catch (failed_constructor& err){
3343 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3344 _metrics = old_metrics;
3348 } else if (child->name() == MeterSection::xml_state_node_name) {
3351 MeterSection* ms = new MeterSection (*child);
3352 _metrics.push_back (ms);
3355 catch (failed_constructor& err) {
3356 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3357 _metrics = old_metrics;
3363 if (niter == nlist.end()) {
3364 MetricSectionSorter cmp;
3365 _metrics.sort (cmp);
3368 /* check for legacy sessions where bbt was the base musical unit for tempo */
3369 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3371 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3372 if (t->legacy_bbt().bars != 0) {
3373 fix_legacy_session();
3380 /* check for multiple tempo/meters at the same location, which
3381 ardour2 somehow allowed.
3384 Metrics::iterator prev = _metrics.end();
3385 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3386 if (prev != _metrics.end()) {
3388 MeterSection* prev_m;
3390 TempoSection* prev_t;
3391 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
3392 if (prev_m->pulse() == ms->pulse()) {
3393 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3394 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3397 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
3398 if (prev_t->pulse() == ts->pulse()) {
3399 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3400 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3408 recompute_map (_metrics);
3411 PropertyChanged (PropertyChange ());
3417 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
3419 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
3420 const MeterSection* m;
3421 const TempoSection* t;
3422 const TempoSection* prev_t = 0;
3424 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3426 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
3427 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
3428 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
3429 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
3431 o << "previous : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
3432 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;
3435 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
3436 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
3437 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
3440 o << "------" << std::endl;
3444 TempoMap::n_tempos() const
3446 Glib::Threads::RWLock::ReaderLock lm (lock);
3449 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3450 if (dynamic_cast<const TempoSection*>(*i) != 0) {
3459 TempoMap::n_meters() const
3461 Glib::Threads::RWLock::ReaderLock lm (lock);
3464 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3465 if (dynamic_cast<const MeterSection*>(*i) != 0) {
3474 TempoMap::insert_time (framepos_t where, framecnt_t amount)
3477 Glib::Threads::RWLock::WriterLock lm (lock);
3478 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3479 if ((*i)->frame() >= where && (*i)->movable ()) {
3480 (*i)->set_frame ((*i)->frame() + amount);
3484 /* now reset the BBT time of all metrics, based on their new
3485 * audio time. This is the only place where we do this reverse
3489 Metrics::iterator i;
3490 const MeterSection* meter;
3491 const TempoSection* tempo;
3495 meter = &first_meter ();
3496 tempo = &first_tempo ();
3501 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
3504 MetricSection* prev = 0;
3506 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3509 //TempoMetric metric (*meter, *tempo);
3510 MeterSection* ms = const_cast<MeterSection*>(meter);
3511 TempoSection* ts = const_cast<TempoSection*>(tempo);
3514 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3518 ts->set_pulse (t->pulse());
3520 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3521 ts->set_pulse (m->pulse());
3523 ts->set_frame (prev->frame());
3527 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3528 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3529 ms->set_beat (start);
3530 ms->set_pulse (m->pulse());
3532 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3536 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3537 pair<double, BBT_Time> start = make_pair (beat, beats_to_bbt_locked (_metrics, beat));
3538 ms->set_beat (start);
3539 ms->set_pulse (t->pulse());
3541 ms->set_frame (prev->frame());
3545 // metric will be at frames=0 bbt=1|1|0 by default
3546 // which is correct for our purpose
3549 // cerr << bbt << endl;
3551 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3555 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3557 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3558 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3559 bbt_time (m->frame(), bbt);
3561 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3567 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3568 /* round up to next beat */
3574 if (bbt.beats != 1) {
3575 /* round up to next bar */
3580 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3581 m->set_beat (start);
3582 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3584 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3586 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3587 abort(); /*NOTREACHED*/
3593 recompute_map (_metrics);
3597 PropertyChanged (PropertyChange ());
3600 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3604 std::list<MetricSection*> metric_kill_list;
3606 TempoSection* last_tempo = NULL;
3607 MeterSection* last_meter = NULL;
3608 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3609 bool meter_after = false; // is there a meter marker likewise?
3611 Glib::Threads::RWLock::WriterLock lm (lock);
3612 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3613 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3614 metric_kill_list.push_back(*i);
3615 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3618 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3622 else if ((*i)->frame() >= where) {
3623 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3624 (*i)->set_frame ((*i)->frame() - amount);
3625 if ((*i)->frame() == where) {
3626 // marker was immediately after end of range
3627 tempo_after = dynamic_cast<TempoSection*> (*i);
3628 meter_after = dynamic_cast<MeterSection*> (*i);
3634 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3635 if (last_tempo && !tempo_after) {
3636 metric_kill_list.remove(last_tempo);
3637 last_tempo->set_frame(where);
3640 if (last_meter && !meter_after) {
3641 metric_kill_list.remove(last_meter);
3642 last_meter->set_frame(where);
3646 //remove all the remaining metrics
3647 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3648 _metrics.remove(*i);
3653 recompute_map (_metrics);
3656 PropertyChanged (PropertyChange ());
3660 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3661 * pos can be -ve, if required.
3664 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3666 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
3669 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3671 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3673 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
3676 /** Add the BBT interval op to pos and return the result */
3678 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3680 Glib::Threads::RWLock::ReaderLock lm (lock);
3682 BBT_Time pos_bbt = beats_to_bbt_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3683 pos_bbt.ticks += op.ticks;
3684 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3686 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3688 pos_bbt.beats += op.beats;
3689 /* the meter in effect will start on the bar */
3690 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();
3691 while (pos_bbt.beats >= divisions_per_bar + 1) {
3693 divisions_per_bar = meter_section_at_beat (bbt_to_beats_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3694 pos_bbt.beats -= divisions_per_bar;
3696 pos_bbt.bars += op.bars;
3698 return frame_time_locked (_metrics, pos_bbt);
3701 /** Count the number of beats that are equivalent to distance when going forward,
3705 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3707 return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
3711 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3717 operator<< (std::ostream& o, const Meter& m) {
3718 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3722 operator<< (std::ostream& o, const Tempo& t) {
3723 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3727 operator<< (std::ostream& o, const MetricSection& section) {
3729 o << "MetricSection @ " << section.frame() << ' ';
3731 const TempoSection* ts;
3732 const MeterSection* ms;
3734 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3735 o << *((const Tempo*) ts);
3736 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3737 o << *((const Meter*) ms);