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 pulse p 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& p, const framecnt_t& frame_rate) const
228 if (_type == Constant || _c_func == 0.0) {
229 return ((p - 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 is the rate of the musical pulse.
590 Meters divide the pulses into measures and beats.
592 Note that Tempo::beats_per_minute() has nothing to do with musical beats.
593 It should rather be thought of as tempo note divisions per minute.
595 TempoSections, which are nice to think of in whole pulses per minute,
596 and MeterSecions which divide tempo pulses into measures (via divisions_per_bar)
597 and beats (via note_divisor) are used to form a tempo map.
598 TempoSections and MeterSections may be locked to either audio or music (position lock style).
599 We construct the tempo map by first using the frame or pulse position (depending on position lock style) of each tempo.
600 We then use this pulse/frame layout to find the beat & pulse or frame position of each meter (again depending on lock style).
602 Having done this, we can now find any one of tempo, beat, frame or pulse if a beat, frame, pulse or tempo is known.
604 With tepo sections potentially being ramped, meters provide a way of mapping beats to whole pulses without
605 referring to the tempo function(s) involved as the distance in whole pulses between a meter and a subsequent beat is
606 sb->beat() - meter->beat() / meter->note_divisor().
607 Because every meter falls on a known pulse, (derived from its bar), the rest is easy as the duration in pulses between
608 two meters is of course
609 (meater_b->bar - meter_a->bar) * meter_a->divisions_per_bar / meter_a->note_divisor.
611 Below, beat calculations are based on meter sections and all pulse and tempo calculations are based on tempo sections.
612 Beat to frame conversion of course requires the use of meter and tempo.
614 Remembering that ramped tempo sections interact, it is important to avoid referring to any other tempos when moving tempo sections,
615 Here, beats (meters) are used to determine the new pulse (see predict_tempo_position())
617 The first tempo and first meter are special. they must move together, and must be locked to audio.
618 Audio locked tempos which lie before the first meter are made inactive.
619 They will be re-activated if the first meter is again placed before them.
621 Both tempos and meters have a pulse position and a frame position.
622 Meters also have a beat position, which is always 0.0 for the first meter.
624 A tempo locked to music is locked to musical pulses.
625 A meter locked to music is locked to beats.
627 Recomputing the tempo map is the process where the 'missing' position
628 (tempo pulse or meter pulse & beat in the case of AudioTime, frame for MusicTime) is calculated.
630 It is important to keep the _metrics in an order that makes sense.
631 Because ramped MusicTime and AudioTime tempos can interact with each other,
632 reordering is frequent. Care must be taken to keep _metrics in a solved state.
633 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
635 struct MetricSectionSorter {
636 bool operator() (const MetricSection* a, const MetricSection* b) {
637 return a->pulse() < b->pulse();
641 struct MetricSectionFrameSorter {
642 bool operator() (const MetricSection* a, const MetricSection* b) {
643 return a->frame() < b->frame();
647 TempoMap::TempoMap (framecnt_t fr)
650 BBT_Time start (1, 1, 0);
652 TempoSection *t = new TempoSection (0.0, 0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Ramp, AudioTime);
653 MeterSection *m = new MeterSection (0.0, 0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime);
655 t->set_movable (false);
656 m->set_movable (false);
658 /* note: frame time is correct (zero) for both of these */
660 _metrics.push_back (t);
661 _metrics.push_back (m);
665 TempoMap::~TempoMap ()
667 Metrics::const_iterator d = _metrics.begin();
668 while (d != _metrics.end()) {
676 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
678 bool removed = false;
681 Glib::Threads::RWLock::WriterLock lm (lock);
682 if ((removed = remove_tempo_locked (tempo))) {
683 if (complete_operation) {
684 recompute_map (_metrics);
689 if (removed && complete_operation) {
690 PropertyChanged (PropertyChange ());
695 TempoMap::remove_tempo_locked (const TempoSection& tempo)
699 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
700 if (dynamic_cast<TempoSection*> (*i) != 0) {
701 if (tempo.frame() == (*i)->frame()) {
702 if ((*i)->movable()) {
715 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
717 bool removed = false;
720 Glib::Threads::RWLock::WriterLock lm (lock);
721 if ((removed = remove_meter_locked (tempo))) {
722 if (complete_operation) {
723 recompute_map (_metrics);
728 if (removed && complete_operation) {
729 PropertyChanged (PropertyChange ());
734 TempoMap::remove_meter_locked (const MeterSection& meter)
738 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
740 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
741 if (meter.frame() == (*i)->frame()) {
742 if (t->locked_to_meter()) {
751 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
752 if (dynamic_cast<MeterSection*> (*i) != 0) {
753 if (meter.frame() == (*i)->frame()) {
754 if ((*i)->movable()) {
767 TempoMap::do_insert (MetricSection* section)
769 bool need_add = true;
770 /* we only allow new meters to be inserted on beat 1 of an existing
774 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
776 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
778 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
779 corrected.second.beats = 1;
780 corrected.second.ticks = 0;
781 corrected.first = beat_at_bbt_locked (_metrics, corrected.second);
782 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
783 m->bbt(), corrected.second) << endmsg;
784 //m->set_pulse (corrected);
788 /* Look for any existing MetricSection that is of the same type and
789 in the same bar as the new one, and remove it before adding
790 the new one. Note that this means that if we find a matching,
791 existing section, we can break out of the loop since we're
792 guaranteed that there is only one such match.
795 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
797 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
798 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
799 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
800 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
802 if (tempo && insert_tempo) {
805 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
806 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
808 if (!tempo->movable()) {
810 /* can't (re)move this section, so overwrite
811 * its data content (but not its properties as
815 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
816 (*i)->set_position_lock_style (AudioTime);
818 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
819 t->set_type (insert_tempo->type());
829 } else if (meter && insert_meter) {
833 bool const ipm = insert_meter->position_lock_style() == MusicTime;
835 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
837 if (!meter->movable()) {
839 /* can't (re)move this section, so overwrite
840 * its data content (but not its properties as
844 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
845 (*i)->set_position_lock_style (AudioTime);
855 /* non-matching types, so we don't care */
859 /* Add the given MetricSection, if we didn't just reset an existing
864 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
865 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
868 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
869 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
872 bool const ipm = insert_meter->position_lock_style() == MusicTime;
873 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
878 } else if (insert_tempo) {
879 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
880 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
883 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
884 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
891 _metrics.insert (i, section);
892 //dump (_metrics, std::cout);
897 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, ARDOUR::TempoSection::Type type, PositionLockStyle pls)
899 TempoSection* ts = 0;
901 Glib::Threads::RWLock::WriterLock lm (lock);
902 ts = add_tempo_locked (tempo, pulse, frame, type, pls, true);
906 PropertyChanged (PropertyChange ());
912 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, TempoSection::Type type, PositionLockStyle pls)
914 const bool locked_to_meter = ts.locked_to_meter();
917 Glib::Threads::RWLock::WriterLock lm (lock);
918 TempoSection& first (first_tempo());
919 if (ts.frame() != first.frame()) {
920 remove_tempo_locked (ts);
921 add_tempo_locked (tempo, pulse, frame, type, pls, true, locked_to_meter);
923 first.set_type (type);
924 first.set_pulse (0.0);
925 first.set_frame (frame);
926 first.set_position_lock_style (AudioTime);
928 /* cannot move the first tempo section */
929 *static_cast<Tempo*>(&first) = tempo;
930 recompute_map (_metrics);
935 PropertyChanged (PropertyChange ());
939 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, framepos_t frame
940 , TempoSection::Type type, PositionLockStyle pls, bool recompute, bool locked_to_meter)
942 TempoSection* t = new TempoSection (pulse, frame, tempo.beats_per_minute(), tempo.note_type(), type, pls);
943 t->set_locked_to_meter (locked_to_meter);
948 if (pls == AudioTime) {
949 solve_map_frame (_metrics, t, t->frame());
951 solve_map_pulse (_metrics, t, t->pulse());
953 recompute_meters (_metrics);
960 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, const framepos_t& frame, PositionLockStyle pls)
964 Glib::Threads::RWLock::WriterLock lm (lock);
965 m = add_meter_locked (meter, beat, where, frame, pls, true);
970 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
971 dump (_metrics, std::cerr);
975 PropertyChanged (PropertyChange ());
980 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, const framepos_t& frame, PositionLockStyle pls)
983 Glib::Threads::RWLock::WriterLock lm (lock);
984 const double beat = beat_at_bbt_locked (_metrics, where);
987 remove_meter_locked (ms);
988 add_meter_locked (meter, beat, where, frame, pls, true);
990 MeterSection& first (first_meter());
991 TempoSection& first_t (first_tempo());
992 /* cannot move the first meter section */
993 *static_cast<Meter*>(&first) = meter;
994 first.set_position_lock_style (AudioTime);
995 first.set_pulse (0.0);
996 first.set_frame (frame);
997 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
998 first.set_beat (beat);
999 first_t.set_frame (first.frame());
1000 first_t.set_pulse (0.0);
1001 first_t.set_position_lock_style (AudioTime);
1002 recompute_map (_metrics);
1006 PropertyChanged (PropertyChange ());
1010 TempoMap::add_meter_locked (const Meter& meter, double beat, const Timecode::BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute)
1012 const MeterSection& prev_m = meter_section_at_frame_locked (_metrics, frame - 1);
1013 const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1015 if (pls == AudioTime) {
1016 /* add meter-locked tempo */
1017 add_tempo_locked (tempo_at_frame_locked (_metrics, frame), pulse, frame, TempoSection::Ramp, AudioTime, true, true);
1020 MeterSection* new_meter = new MeterSection (pulse, frame, beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls);
1022 do_insert (new_meter);
1026 if (pls == AudioTime) {
1027 solve_map_frame (_metrics, new_meter, frame);
1029 solve_map_bbt (_metrics, new_meter, where);
1037 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1039 Tempo newtempo (beats_per_minute, note_type);
1042 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1043 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1048 Glib::Threads::RWLock::WriterLock lm (lock);
1049 *((Tempo*) t) = newtempo;
1050 recompute_map (_metrics);
1052 PropertyChanged (PropertyChange ());
1059 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1061 Tempo newtempo (beats_per_minute, note_type);
1064 TempoSection* first;
1065 Metrics::iterator i;
1067 /* find the TempoSection immediately preceding "where"
1070 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1072 if ((*i)->frame() > where) {
1078 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1091 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1101 Glib::Threads::RWLock::WriterLock lm (lock);
1102 /* cannot move the first tempo section */
1103 *((Tempo*)prev) = newtempo;
1104 recompute_map (_metrics);
1107 PropertyChanged (PropertyChange ());
1111 TempoMap::first_meter () const
1113 const MeterSection *m = 0;
1115 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1116 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1121 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1122 abort(); /*NOTREACHED*/
1127 TempoMap::first_meter ()
1129 MeterSection *m = 0;
1131 /* CALLER MUST HOLD LOCK */
1133 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1134 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1139 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1140 abort(); /*NOTREACHED*/
1145 TempoMap::first_tempo () const
1147 const TempoSection *t = 0;
1149 /* CALLER MUST HOLD LOCK */
1151 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1152 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1156 if (!t->movable()) {
1162 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1163 abort(); /*NOTREACHED*/
1168 TempoMap::first_tempo ()
1170 TempoSection *t = 0;
1172 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1173 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1177 if (!t->movable()) {
1183 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1184 abort(); /*NOTREACHED*/
1188 TempoMap::recompute_tempos (Metrics& metrics)
1190 TempoSection* prev_t = 0;
1192 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1195 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1199 if (!t->movable()) {
1207 if (t->position_lock_style() == AudioTime) {
1208 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1209 if (!t->locked_to_meter()) {
1210 t->set_pulse (prev_t->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
1214 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1215 t->set_frame (prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
1222 prev_t->set_c_func (0.0);
1225 /* tempos must be positioned correctly.
1226 the current approach is to use a meter's bbt time as its base position unit.
1227 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1228 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1231 TempoMap::recompute_meters (Metrics& metrics)
1233 MeterSection* meter = 0;
1234 MeterSection* prev_m = 0;
1236 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1237 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1238 if (meter->position_lock_style() == AudioTime) {
1240 pair<double, BBT_Time> b_bbt;
1241 TempoSection* meter_locked_tempo = 0;
1242 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1244 if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
1245 if ((t->locked_to_meter() || !t->movable()) && t->frame() == meter->frame()) {
1246 meter_locked_tempo = t;
1253 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1254 if (beats + prev_m->beat() != meter->beat()) {
1255 /* reordering caused a bbt change */
1256 b_bbt = make_pair (beats + prev_m->beat()
1257 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1258 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1260 } else if (meter->movable()) {
1261 b_bbt = make_pair (meter->beat(), meter->bbt());
1262 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1265 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1267 if (meter_locked_tempo) {
1268 meter_locked_tempo->set_pulse (pulse);
1270 meter->set_beat (b_bbt);
1271 meter->set_pulse (pulse);
1276 pair<double, BBT_Time> b_bbt;
1278 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1279 if (beats + prev_m->beat() != meter->beat()) {
1280 /* reordering caused a bbt change */
1281 b_bbt = make_pair (beats + prev_m->beat()
1282 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1284 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1286 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1288 /* shouldn't happen - the first is audio-locked */
1289 pulse = pulse_at_beat_locked (metrics, meter->beat());
1290 b_bbt = make_pair (meter->beat(), meter->bbt());
1293 meter->set_beat (b_bbt);
1294 meter->set_pulse (pulse);
1295 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1304 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1306 /* CALLER MUST HOLD WRITE LOCK */
1310 /* we will actually stop once we hit
1317 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1320 /* silly call from Session::process() during startup
1325 recompute_tempos (metrics);
1326 recompute_meters (metrics);
1330 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1332 Glib::Threads::RWLock::ReaderLock lm (lock);
1333 TempoMetric m (first_meter(), first_tempo());
1335 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1336 at something, because we insert the default tempo and meter during
1337 TempoMap construction.
1339 now see if we can find better candidates.
1342 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1344 if ((*i)->frame() > frame) {
1358 /* XX meters only */
1360 TempoMap::metric_at (BBT_Time bbt) const
1362 Glib::Threads::RWLock::ReaderLock lm (lock);
1363 TempoMetric m (first_meter(), first_tempo());
1365 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1366 at something, because we insert the default tempo and meter during
1367 TempoMap construction.
1369 now see if we can find better candidates.
1372 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1374 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1375 BBT_Time section_start (mw->bbt());
1377 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1389 TempoMap::beat_at_frame (const framecnt_t& frame) const
1391 Glib::Threads::RWLock::ReaderLock lm (lock);
1392 return beat_at_frame_locked (_metrics, frame);
1395 /* meter / tempo section based */
1397 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1399 const TempoSection& ts = tempo_section_at_frame_locked (metrics, frame);
1400 MeterSection* prev_m = 0;
1401 MeterSection* next_m = 0;
1403 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1405 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1406 if (prev_m && m->frame() > frame) {
1413 if (frame < prev_m->frame()) {
1416 const double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
1418 if (next_m && next_m->beat() < beat) {
1419 return next_m->beat();
1426 TempoMap::frame_at_beat (const double& beat) const
1428 Glib::Threads::RWLock::ReaderLock lm (lock);
1429 return frame_at_beat_locked (_metrics, beat);
1432 /* meter section based */
1434 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1436 const TempoSection& prev_t = tempo_section_at_beat_locked (metrics, beat);
1437 MeterSection* prev_m = 0;
1439 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1441 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1442 if (prev_m && m->beat() > beat) {
1449 return prev_t.frame_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse(), _frame_rate);
1453 TempoMap::tempo_at_frame (const framepos_t& frame) const
1455 Glib::Threads::RWLock::ReaderLock lm (lock);
1456 return tempo_at_frame_locked (_metrics, frame);
1460 TempoMap::tempo_at_frame_locked (const Metrics& metrics, const framepos_t& frame) const
1462 TempoSection* prev_t = 0;
1464 Metrics::const_iterator i;
1466 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1468 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1472 if ((prev_t) && t->frame() > frame) {
1473 /* t is the section past frame */
1474 const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
1475 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
1482 const double ret = prev_t->beats_per_minute();
1483 const Tempo ret_tempo (ret, prev_t->note_type ());
1488 /** returns the frame at which the supplied tempo occurs, or
1489 * the frame of the last tempo section (search exhausted)
1490 * only the position of the first occurence will be returned
1494 TempoMap::frame_at_tempo (const Tempo& tempo) const
1496 Glib::Threads::RWLock::ReaderLock lm (lock);
1497 return frame_at_tempo_locked (_metrics, tempo);
1502 TempoMap::frame_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1504 TempoSection* prev_t = 0;
1505 const double tempo_ppm = tempo.beats_per_minute() / tempo.note_type();
1507 Metrics::const_iterator i;
1509 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1511 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1517 const double t_ppm = t->beats_per_minute() / t->note_type();
1519 if (t_ppm == tempo_ppm) {
1524 const double prev_t_ppm = prev_t->beats_per_minute() / prev_t->note_type();
1526 if ((t_ppm > tempo_ppm && prev_t_ppm < tempo_ppm) || (t_ppm < tempo_ppm && prev_t_ppm > tempo_ppm)) {
1527 const framepos_t ret_frame = prev_t->frame_at_tempo (tempo_ppm, prev_t->pulse(), _frame_rate);
1535 return prev_t->frame();
1539 TempoMap::tempo_at_beat (const double& beat) const
1541 Glib::Threads::RWLock::ReaderLock lm (lock);
1542 const MeterSection* prev_m = &meter_section_at_beat_locked (_metrics, beat);
1543 const TempoSection* prev_t = &tempo_section_at_beat_locked (_metrics, beat);
1544 const double note_type = prev_t->note_type();
1546 return Tempo (prev_t->tempo_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse()) * note_type, note_type);
1550 TempoMap::pulse_at_beat (const double& beat) const
1552 Glib::Threads::RWLock::ReaderLock lm (lock);
1553 return pulse_at_beat_locked (_metrics, beat);
1557 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1559 MeterSection* prev_m = 0;
1561 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1563 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1564 if (prev_m && m->beat() > beat) {
1571 double const ret = prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1576 TempoMap::beat_at_pulse (const double& pulse) const
1578 Glib::Threads::RWLock::ReaderLock lm (lock);
1579 return beat_at_pulse_locked (_metrics, pulse);
1583 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1585 MeterSection* prev_m = 0;
1587 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1589 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1590 if (prev_m && m->pulse() > pulse) {
1591 if (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > m->beat()) {
1599 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1604 TempoMap::pulse_at_frame (const framecnt_t& frame) const
1606 Glib::Threads::RWLock::ReaderLock lm (lock);
1607 return pulse_at_frame_locked (_metrics, frame);
1610 /* tempo section based */
1612 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1614 /* HOLD (at least) THE READER LOCK */
1615 TempoSection* prev_t = 0;
1617 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1619 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1623 if (prev_t && t->frame() > frame) {
1624 /*the previous ts is the one containing the frame */
1625 const double ret = prev_t->pulse_at_frame (frame, _frame_rate);
1632 /* treated as constant for this ts */
1633 const double pulses_in_section = (frame - prev_t->frame()) / prev_t->frames_per_pulse (_frame_rate);
1635 return pulses_in_section + prev_t->pulse();
1639 TempoMap::frame_at_pulse (const double& pulse) const
1641 Glib::Threads::RWLock::ReaderLock lm (lock);
1642 return frame_at_pulse_locked (_metrics, pulse);
1645 /* tempo section based */
1647 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1649 /* HOLD THE READER LOCK */
1651 const TempoSection* prev_t = 0;
1653 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1656 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1660 if (prev_t && t->pulse() > pulse) {
1661 return prev_t->frame_at_pulse (pulse, _frame_rate);
1667 /* must be treated as constant, irrespective of _type */
1668 double const pulses_in_section = pulse - prev_t->pulse();
1669 double const dtime = pulses_in_section * prev_t->frames_per_pulse (_frame_rate);
1671 framecnt_t const ret = (framecnt_t) floor (dtime) + prev_t->frame();
1677 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
1679 Glib::Threads::RWLock::ReaderLock lm (lock);
1680 return beat_at_bbt_locked (_metrics, bbt);
1685 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1687 /* CALLER HOLDS READ LOCK */
1689 MeterSection* prev_m = 0;
1691 /* because audio-locked meters have 'fake' integral beats,
1692 there is no pulse offset here.
1694 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1696 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1698 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1699 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1707 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1708 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1709 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1715 TempoMap::bbt_at_beat (const double& beats)
1717 Glib::Threads::RWLock::ReaderLock lm (lock);
1718 return bbt_at_beat_locked (_metrics, beats);
1722 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
1724 /* CALLER HOLDS READ LOCK */
1725 MeterSection* prev_m = 0;
1726 const double beats = max (0.0, b);
1728 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1729 MeterSection* m = 0;
1731 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1733 if (m->beat() > beats) {
1734 /* this is the meter after the one our beat is on*/
1743 const double beats_in_ms = beats - prev_m->beat();
1744 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1745 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1746 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1747 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1751 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1752 ret.beats = (uint32_t) floor (remaining_beats);
1753 ret.bars = total_bars;
1755 /* 0 0 0 to 1 1 0 - based mapping*/
1759 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1761 ret.ticks -= BBT_Time::ticks_per_beat;
1764 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1773 TempoMap::pulse_at_bbt (const Timecode::BBT_Time& bbt)
1775 Glib::Threads::RWLock::ReaderLock lm (lock);
1777 return pulse_at_bbt_locked (_metrics, bbt);
1781 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1783 /* CALLER HOLDS READ LOCK */
1785 MeterSection* prev_m = 0;
1787 /* because audio-locked meters have 'fake' integral beats,
1788 there is no pulse offset here.
1790 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1792 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1794 if (m->bbt().bars > bbt.bars) {
1802 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1803 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
1804 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
1810 TempoMap::bbt_at_pulse (const double& pulse)
1812 Glib::Threads::RWLock::ReaderLock lm (lock);
1814 return bbt_at_pulse_locked (_metrics, pulse);
1818 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1820 MeterSection* prev_m = 0;
1822 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1823 MeterSection* m = 0;
1825 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1828 double const pulses_to_m = m->pulse() - prev_m->pulse();
1829 if (prev_m->pulse() + pulses_to_m > pulse) {
1830 /* this is the meter after the one our beat is on*/
1839 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1840 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1841 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1842 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1843 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1847 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1848 ret.beats = (uint32_t) floor (remaining_beats);
1849 ret.bars = total_bars;
1851 /* 0 0 0 to 1 1 0 mapping*/
1855 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1857 ret.ticks -= BBT_Time::ticks_per_beat;
1860 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1869 TempoMap::bbt_at_frame (framepos_t frame)
1876 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1879 Glib::Threads::RWLock::ReaderLock lm (lock);
1881 return bbt_at_frame_locked (_metrics, frame);
1885 TempoMap::bbt_at_frame_locked (const Metrics& metrics, const framepos_t& frame) const
1892 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1895 const double beat = beat_at_frame_locked (metrics, frame);
1897 return bbt_at_beat_locked (metrics, beat);
1901 TempoMap::frame_at_bbt (const BBT_Time& bbt)
1904 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1908 if (bbt.beats < 1) {
1909 throw std::logic_error ("beats are counted from one");
1911 Glib::Threads::RWLock::ReaderLock lm (lock);
1913 return frame_at_bbt_locked (_metrics, bbt);
1916 /* meter & tempo section based */
1918 TempoMap::frame_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
1920 /* HOLD THE READER LOCK */
1922 const framepos_t ret = frame_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
1927 TempoMap::check_solved (const Metrics& metrics) const
1929 TempoSection* prev_t = 0;
1930 MeterSection* prev_m = 0;
1932 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1935 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1940 /* check ordering */
1941 if ((t->frame() <= prev_t->frame()) || (t->pulse() <= prev_t->pulse())) {
1945 /* precision check ensures tempo and frames align.*/
1946 if (t->frame() != prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate)) {
1947 if (!t->locked_to_meter()) {
1952 /* gradient limit - who knows what it should be?
1953 things are also ok (if a little chaotic) without this
1955 if (fabs (prev_t->c_func()) > 1000.0) {
1956 //std::cout << "c : " << prev_t->c_func() << std::endl;
1963 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1964 if (prev_m && m->position_lock_style() == AudioTime) {
1965 TempoSection* t = const_cast<TempoSection*>(&tempo_section_at_frame_locked (metrics, m->frame() - 1));
1966 const double nascent_m_pulse = ((m->beat() - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse();
1967 const framepos_t nascent_m_frame = t->frame_at_pulse (nascent_m_pulse, _frame_rate);
1969 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
1983 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
1985 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1987 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1988 if (!t->movable()) {
1989 t->set_active (true);
1992 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
1993 t->set_active (false);
1995 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
1996 t->set_active (true);
1997 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
2006 TempoMap::solve_map_frame (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
2008 TempoSection* prev_t = 0;
2009 TempoSection* section_prev = 0;
2010 framepos_t first_m_frame = 0;
2012 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2014 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2015 if (!m->movable()) {
2016 first_m_frame = m->frame();
2021 if (section->movable() && frame <= first_m_frame) {
2025 section->set_active (true);
2026 section->set_frame (frame);
2028 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2030 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2037 section_prev = prev_t;
2040 if (t->position_lock_style() == MusicTime) {
2041 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
2042 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
2044 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
2045 if (!t->locked_to_meter()) {
2046 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
2055 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
2056 if (!section->locked_to_meter()) {
2057 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
2061 recompute_tempos (imaginary);
2063 if (check_solved (imaginary)) {
2067 MetricSectionFrameSorter fcmp;
2068 imaginary.sort (fcmp);
2070 recompute_tempos (imaginary);
2072 if (check_solved (imaginary)) {
2080 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2082 TempoSection* prev_t = 0;
2083 TempoSection* section_prev = 0;
2085 section->set_pulse (pulse);
2087 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2089 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2093 if (!t->movable()) {
2100 section_prev = prev_t;
2103 if (t->position_lock_style() == MusicTime) {
2104 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
2105 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
2107 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
2108 if (!t->locked_to_meter()) {
2109 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
2118 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
2119 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
2122 recompute_tempos (imaginary);
2124 if (check_solved (imaginary)) {
2128 MetricSectionSorter cmp;
2129 imaginary.sort (cmp);
2131 recompute_tempos (imaginary);
2133 * XX need a restriction here, but only for this case,
2134 * as audio locked tempos don't interact in the same way.
2136 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2138 * |50 bpm |250 bpm |60 bpm
2139 * drag 250 to the pulse after 60->
2140 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2142 if (check_solved (imaginary)) {
2150 TempoMap::solve_map_frame (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
2152 /* disallow moving first meter past any subsequent one, and any movable meter before the first one */
2153 const MeterSection* other = &meter_section_at_frame_locked (imaginary, frame);
2154 if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->frame() >= frame)) {
2158 if (!section->movable()) {
2159 /* lock the first tempo to our first meter */
2160 if (!set_active_tempos (imaginary, frame)) {
2165 /* it would make sense to bail out if there is no audio-locked meter,
2166 however it may be desirable to move a music-locked meter by frame at some point.
2168 TempoSection* meter_locked_tempo = 0;
2169 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2171 if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
2172 if ((t->locked_to_meter() || !t->movable()) && t->frame() == section->frame()) {
2173 meter_locked_tempo = t;
2179 if (!meter_locked_tempo) {
2183 MeterSection* prev_m = 0;
2185 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2186 bool solved = false;
2188 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2190 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2192 if (prev_m && section->movable()) {
2193 const double beats = (pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor();
2194 if (beats + prev_m->beat() < section->beat()) {
2195 /* set the frame/pulse corresponding to its musical position,
2196 * as an earlier time than this has been requested.
2198 const double new_pulse = ((section->beat() - prev_m->beat())
2199 / prev_m->note_divisor()) + prev_m->pulse();
2201 const framepos_t smallest_frame = frame_at_pulse_locked (future_map, new_pulse);
2203 if ((solved = solve_map_frame (future_map, tempo_copy, smallest_frame))) {
2204 meter_locked_tempo->set_pulse (new_pulse);
2205 solve_map_frame (imaginary, meter_locked_tempo, smallest_frame);
2206 section->set_frame (smallest_frame);
2207 section->set_pulse (new_pulse);
2212 Metrics::const_iterator d = future_map.begin();
2213 while (d != future_map.end()) {
2222 /* all is ok. set section's tempo */
2223 MeterSection* meter_copy = const_cast<MeterSection*> (&meter_section_at_frame_locked (future_map, section->frame()));
2224 meter_copy->set_frame (frame);
2226 if ((solved = solve_map_frame (future_map, tempo_copy, frame))) {
2227 section->set_frame (frame);
2228 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2229 / prev_m->note_divisor()) + prev_m->pulse());
2230 solve_map_frame (imaginary, meter_locked_tempo, frame);
2235 Metrics::const_iterator d = future_map.begin();
2236 while (d != future_map.end()) {
2246 /* not movable (first meter atm) */
2248 tempo_copy->set_frame (frame);
2249 tempo_copy->set_pulse (0.0);
2251 if ((solved = solve_map_frame (future_map, tempo_copy, frame))) {
2252 section->set_frame (frame);
2253 meter_locked_tempo->set_frame (frame);
2254 meter_locked_tempo->set_pulse (0.0);
2255 solve_map_frame (imaginary, meter_locked_tempo, frame);
2260 Metrics::const_iterator d = future_map.begin();
2261 while (d != future_map.end()) {
2270 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2271 section->set_beat (b_bbt);
2272 section->set_pulse (0.0);
2282 MetricSectionFrameSorter fcmp;
2283 imaginary.sort (fcmp);
2285 recompute_meters (imaginary);
2291 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2293 /* disallow setting section to an existing meter's bbt */
2294 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2296 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2297 if (m != section && m->bbt().bars == when.bars) {
2303 MeterSection* prev_m = 0;
2304 MeterSection* section_prev = 0;
2306 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2308 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2309 pair<double, BBT_Time> b_bbt;
2310 double new_pulse = 0.0;
2312 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2313 section_prev = prev_m;
2314 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2315 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2316 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2318 section->set_beat (b_bbt);
2319 section->set_pulse (pulse);
2320 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2325 if (m->position_lock_style() == AudioTime) {
2326 TempoSection* meter_locked_tempo = 0;
2328 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2330 if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
2331 if ((t->locked_to_meter() || !t->movable()) && t->frame() == m->frame()) {
2332 meter_locked_tempo = t;
2338 if (!meter_locked_tempo) {
2343 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2345 if (beats + prev_m->beat() != m->beat()) {
2346 /* tempo/ meter change caused a change in beat (bar). */
2347 b_bbt = make_pair (beats + prev_m->beat()
2348 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2349 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2350 } else if (m->movable()) {
2351 b_bbt = make_pair (m->beat(), m->bbt());
2352 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2355 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2358 meter_locked_tempo->set_pulse (new_pulse);
2359 m->set_beat (b_bbt);
2360 m->set_pulse (new_pulse);
2364 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2365 if (beats + prev_m->beat() != m->beat()) {
2366 /* tempo/ meter change caused a change in beat (bar). */
2367 b_bbt = make_pair (beats + prev_m->beat()
2368 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2370 b_bbt = make_pair (beats + prev_m->beat()
2373 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2374 m->set_beat (b_bbt);
2375 m->set_pulse (new_pulse);
2376 m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2383 if (!section_prev) {
2385 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
2386 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2387 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
2389 section->set_beat (b_bbt);
2390 section->set_pulse (pulse);
2391 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2394 MetricSectionSorter cmp;
2395 imaginary.sort (cmp);
2397 recompute_meters (imaginary);
2402 /** places a copy of _metrics into copy and returns a pointer
2403 * to section's equivalent in copy.
2406 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
2408 TempoSection* ret = 0;
2410 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2413 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2415 ret = new TempoSection (*t);
2416 copy.push_back (ret);
2420 TempoSection* cp = new TempoSection (*t);
2421 copy.push_back (cp);
2423 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2424 MeterSection* cp = new MeterSection (*m);
2425 copy.push_back (cp);
2433 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
2435 MeterSection* ret = 0;
2437 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2440 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2441 TempoSection* cp = new TempoSection (*t);
2442 copy.push_back (cp);
2445 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2447 ret = new MeterSection (*m);
2448 copy.push_back (ret);
2451 MeterSection* cp = new MeterSection (*m);
2452 copy.push_back (cp);
2459 /** answers the question "is this a valid beat position for this tempo section?".
2460 * it returns true if the tempo section can be moved to the requested bbt position,
2461 * leaving the tempo map in a solved state.
2462 * @param section the tempo section to be moved
2463 * @param bbt the requested new position for the tempo section
2464 * @return true if the tempo section can be moved to the position, otherwise false.
2467 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2470 TempoSection* tempo_copy = 0;
2473 Glib::Threads::RWLock::ReaderLock lm (lock);
2474 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
2480 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
2482 Metrics::const_iterator d = copy.begin();
2483 while (d != copy.end()) {
2492 * This is for a gui that needs to know the pulse or frame of a tempo section if it were to be moved to some bbt time,
2493 * taking any possible reordering as a consequence of this into account.
2494 * @param section - the section to be altered
2495 * @param bbt - the bbt where the altered tempo will fall
2496 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
2498 pair<double, framepos_t>
2499 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
2502 pair<double, framepos_t> ret = make_pair (0.0, 0);
2504 Glib::Threads::RWLock::ReaderLock lm (lock);
2506 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
2508 const double beat = beat_at_bbt_locked (future_map, bbt);
2510 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
2511 ret.first = tempo_copy->pulse();
2512 ret.second = tempo_copy->frame();
2514 ret.first = section->pulse();
2515 ret.second = section->frame();
2518 Metrics::const_iterator d = future_map.begin();
2519 while (d != future_map.end()) {
2527 TempoMap::gui_move_tempo (TempoSection* ts, const framepos_t& frame)
2531 if (ts->position_lock_style() == MusicTime) {
2533 Glib::Threads::RWLock::WriterLock lm (lock);
2534 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2535 const double pulse = pulse_at_frame_locked (future_map, frame);
2536 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
2537 solve_map_pulse (_metrics, ts, pulse);
2538 recompute_meters (_metrics);
2545 Glib::Threads::RWLock::WriterLock lm (lock);
2546 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2547 if (solve_map_frame (future_map, tempo_copy, frame)) {
2548 solve_map_frame (_metrics, ts, frame);
2549 recompute_meters (_metrics);
2554 Metrics::const_iterator d = future_map.begin();
2555 while (d != future_map.end()) {
2560 MetricPositionChanged (); // Emit Signal
2564 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
2568 if (ms->position_lock_style() == AudioTime) {
2571 Glib::Threads::RWLock::WriterLock lm (lock);
2572 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2574 if (solve_map_frame (future_map, copy, frame)) {
2575 solve_map_frame (_metrics, ms, frame);
2576 recompute_tempos (_metrics);
2581 Glib::Threads::RWLock::WriterLock lm (lock);
2582 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2584 const double beat = beat_at_frame_locked (_metrics, frame);
2585 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
2587 if (solve_map_bbt (future_map, copy, bbt)) {
2588 solve_map_bbt (_metrics, ms, bbt);
2589 recompute_tempos (_metrics);
2594 Metrics::const_iterator d = future_map.begin();
2595 while (d != future_map.end()) {
2600 MetricPositionChanged (); // Emit Signal
2604 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2607 bool can_solve = false;
2609 Glib::Threads::RWLock::WriterLock lm (lock);
2610 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2611 tempo_copy->set_beats_per_minute (bpm.beats_per_minute());
2612 recompute_tempos (future_map);
2614 if (check_solved (future_map)) {
2615 ts->set_beats_per_minute (bpm.beats_per_minute());
2616 recompute_map (_metrics);
2621 Metrics::const_iterator d = future_map.begin();
2622 while (d != future_map.end()) {
2627 MetricPositionChanged (); // Emit Signal
2633 TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame, const double& pulse)
2636 Ts (future prev_t) Tnext
2639 |----------|----------
2646 Glib::Threads::RWLock::WriterLock lm (lock);
2652 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
2653 TempoSection* prev_to_prev_t = 0;
2654 const frameoffset_t fr_off = end_frame - frame;
2656 if (prev_t && prev_t->pulse() > 0.0) {
2657 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_frame_locked (future_map, prev_t->frame() - 1));
2660 TempoSection* next_t = 0;
2661 for (Metrics::iterator i = future_map.begin(); i != future_map.end(); ++i) {
2662 TempoSection* t = 0;
2663 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2664 if (t->frame() > ts->frame()) {
2671 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
2672 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
2674 double contribution = 0.0;
2675 double start_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
2677 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2678 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
2681 frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
2682 double end_pulse = prev_t->pulse_at_frame (end_frame, _frame_rate);
2685 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
2687 if (prev_t->position_lock_style() == MusicTime) {
2688 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2690 new_bpm = prev_t->beats_per_minute() * ((frame - prev_to_prev_t->frame())
2691 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
2694 /* prev to prev is irrelevant */
2696 if (start_pulse != prev_t->pulse()) {
2697 new_bpm = prev_t->beats_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
2699 new_bpm = prev_t->beats_per_minute();
2704 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2705 new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame())
2706 / (double) ((frame + prev_t_frame_contribution) - prev_t->frame()));
2708 /* prev_to_prev_t is irrelevant */
2710 if (end_frame != prev_t->frame()) {
2711 new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
2713 new_bpm = prev_t->beats_per_minute();
2721 const framepos_t pulse_pos = prev_t->frame_at_pulse (pulse, _frame_rate);
2723 if (prev_to_prev_t) {
2725 frame_ratio = (((pulse_pos - fr_off) - prev_to_prev_t->frame()) / (double) ((pulse_pos) - prev_to_prev_t->frame()));
2726 pulse_ratio = ((start_pulse - prev_to_prev_t->pulse()) / (end_pulse - prev_to_prev_t->pulse()));
2729 frame_ratio = (((pulse_pos - fr_off) - prev_t->frame()) / (double) ((pulse_pos) - prev_t->frame()));
2730 pulse_ratio = (start_pulse / end_pulse);
2732 new_bpm = prev_t->beats_per_minute() * (pulse_ratio * frame_ratio);
2735 prev_t->set_beats_per_minute (new_bpm);
2736 recompute_tempos (future_map);
2737 recompute_meters (future_map);
2739 if (check_solved (future_map)) {
2740 ts->set_beats_per_minute (new_bpm);
2741 recompute_tempos (_metrics);
2742 recompute_meters (_metrics);
2746 Metrics::const_iterator d = future_map.begin();
2747 while (d != future_map.end()) {
2752 MetricPositionChanged (); // Emit Signal
2756 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2758 Glib::Threads::RWLock::ReaderLock lm (lock);
2760 const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2761 const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2762 const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2764 return frame_at_beat_locked (_metrics, total_beats);
2768 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2770 return round_to_type (fr, dir, Bar);
2774 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2776 return round_to_type (fr, dir, Beat);
2780 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2782 Glib::Threads::RWLock::ReaderLock lm (lock);
2783 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2784 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2785 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2787 ticks -= beats * BBT_Time::ticks_per_beat;
2790 /* round to next (or same iff dir == RoundUpMaybe) */
2792 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2794 if (mod == 0 && dir == RoundUpMaybe) {
2795 /* right on the subdivision, which is fine, so do nothing */
2797 } else if (mod == 0) {
2798 /* right on the subdivision, so the difference is just the subdivision ticks */
2799 ticks += ticks_one_subdivisions_worth;
2802 /* not on subdivision, compute distance to next subdivision */
2804 ticks += ticks_one_subdivisions_worth - mod;
2807 if (ticks >= BBT_Time::ticks_per_beat) {
2808 ticks -= BBT_Time::ticks_per_beat;
2810 } else if (dir < 0) {
2812 /* round to previous (or same iff dir == RoundDownMaybe) */
2814 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2816 if (difference == 0 && dir == RoundDownAlways) {
2817 /* right on the subdivision, but force-rounding down,
2818 so the difference is just the subdivision ticks */
2819 difference = ticks_one_subdivisions_worth;
2822 if (ticks < difference) {
2823 ticks = BBT_Time::ticks_per_beat - ticks;
2825 ticks -= difference;
2829 /* round to nearest */
2832 /* compute the distance to the previous and next subdivision */
2834 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2836 /* closer to the next subdivision, so shift forward */
2838 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2840 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2842 if (ticks > BBT_Time::ticks_per_beat) {
2844 ticks -= BBT_Time::ticks_per_beat;
2845 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2848 } else if (rem > 0) {
2850 /* closer to previous subdivision, so shift backward */
2854 /* can't go backwards past zero, so ... */
2857 /* step back to previous beat */
2859 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2860 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2862 ticks = lrint (ticks - rem);
2863 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2866 /* on the subdivision, do nothing */
2870 const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2876 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
2878 Glib::Threads::RWLock::ReaderLock lm (lock);
2880 const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
2881 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
2886 /* find bar previous to 'frame' */
2889 return frame_at_bbt_locked (_metrics, bbt);
2891 } else if (dir > 0) {
2892 /* find bar following 'frame' */
2896 return frame_at_bbt_locked (_metrics, bbt);
2898 /* true rounding: find nearest bar */
2899 framepos_t raw_ft = frame_at_bbt_locked (_metrics, bbt);
2902 framepos_t prev_ft = frame_at_bbt_locked (_metrics, bbt);
2904 framepos_t next_ft = frame_at_bbt_locked (_metrics, bbt);
2906 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
2917 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
2918 } else if (dir > 0) {
2919 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
2921 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
2930 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2931 framepos_t lower, framepos_t upper)
2933 Glib::Threads::RWLock::ReaderLock lm (lock);
2934 int32_t cnt = ceil (beat_at_frame_locked (_metrics, lower));
2936 /* although the map handles negative beats, bbt doesn't. */
2940 while (pos < upper) {
2941 pos = frame_at_beat_locked (_metrics, cnt);
2942 const TempoSection tempo = tempo_section_at_frame_locked (_metrics, pos);
2943 const MeterSection meter = meter_section_at_frame_locked (_metrics, pos);
2944 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
2945 points.push_back (BBTPoint (meter, tempo_at_frame_locked (_metrics, pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
2951 TempoMap::tempo_section_at_frame (framepos_t frame) const
2953 Glib::Threads::RWLock::ReaderLock lm (lock);
2954 return tempo_section_at_frame_locked (_metrics, frame);
2958 TempoMap::tempo_section_at_frame_locked (const Metrics& metrics, framepos_t frame) const
2960 Metrics::const_iterator i;
2961 TempoSection* prev = 0;
2963 for (i = metrics.begin(); i != metrics.end(); ++i) {
2966 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2970 if (prev && t->frame() > frame) {
2980 abort(); /*NOTREACHED*/
2987 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
2989 TempoSection* prev_t = 0;
2990 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
2992 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2994 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2995 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
3005 /* don't use this to calculate length (the tempo is only correct for this frame).
3006 do that stuff based on the beat_at_frame and frame_at_beat api
3009 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
3011 Glib::Threads::RWLock::ReaderLock lm (lock);
3013 const TempoSection* ts_at = &tempo_section_at_frame_locked (_metrics, frame);
3014 const TempoSection* ts_after = 0;
3015 Metrics::const_iterator i;
3017 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3020 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3024 if ((*i)->frame() > frame) {
3032 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
3034 /* must be treated as constant tempo */
3035 return ts_at->frames_per_beat (_frame_rate);
3039 TempoMap::meter_section_at_frame_locked (const Metrics& metrics, framepos_t frame) const
3041 Metrics::const_iterator i;
3042 MeterSection* prev = 0;
3044 for (i = metrics.begin(); i != metrics.end(); ++i) {
3047 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
3049 if (prev && (*i)->frame() > frame) {
3059 abort(); /*NOTREACHED*/
3067 TempoMap::meter_section_at_frame (framepos_t frame) const
3069 Glib::Threads::RWLock::ReaderLock lm (lock);
3070 return meter_section_at_frame_locked (_metrics, frame);
3074 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3076 MeterSection* prev_m = 0;
3078 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3080 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
3081 if (prev_m && m->beat() > beat) {
3092 TempoMap::meter_section_at_beat (double beat) const
3094 Glib::Threads::RWLock::ReaderLock lm (lock);
3095 return meter_section_at_beat_locked (_metrics, beat);
3099 TempoMap::meter_at_frame (framepos_t frame) const
3101 TempoMetric m (metric_at (frame));
3106 TempoMap::fix_legacy_session ()
3108 MeterSection* prev_m = 0;
3109 TempoSection* prev_t = 0;
3111 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3115 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3116 if (!m->movable()) {
3117 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3121 m->set_position_lock_style (AudioTime);
3126 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
3127 + (m->bbt().beats - 1)
3128 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
3130 m->set_beat (start);
3131 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
3132 + (m->bbt().beats - 1)
3133 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
3134 m->set_pulse (start_beat / prev_m->note_divisor());
3137 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3143 if (!t->movable()) {
3146 t->set_position_lock_style (AudioTime);
3152 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
3153 + (t->legacy_bbt().beats - 1)
3154 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
3156 t->set_pulse (beat / prev_m->note_divisor());
3158 /* really shouldn't happen but.. */
3159 t->set_pulse (beat / 4.0);
3168 TempoMap::get_state ()
3170 Metrics::const_iterator i;
3171 XMLNode *root = new XMLNode ("TempoMap");
3174 Glib::Threads::RWLock::ReaderLock lm (lock);
3175 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3176 root->add_child_nocopy ((*i)->get_state());
3184 TempoMap::set_state (const XMLNode& node, int /*version*/)
3187 Glib::Threads::RWLock::WriterLock lm (lock);
3190 XMLNodeConstIterator niter;
3191 Metrics old_metrics (_metrics);
3194 nlist = node.children();
3196 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
3197 XMLNode* child = *niter;
3199 if (child->name() == TempoSection::xml_state_node_name) {
3202 TempoSection* ts = new TempoSection (*child);
3203 _metrics.push_back (ts);
3206 catch (failed_constructor& err){
3207 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3208 _metrics = old_metrics;
3212 } else if (child->name() == MeterSection::xml_state_node_name) {
3215 MeterSection* ms = new MeterSection (*child);
3216 _metrics.push_back (ms);
3219 catch (failed_constructor& err) {
3220 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3221 _metrics = old_metrics;
3227 if (niter == nlist.end()) {
3228 MetricSectionSorter cmp;
3229 _metrics.sort (cmp);
3232 /* check for legacy sessions where bbt was the base musical unit for tempo */
3233 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3235 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3236 if (t->legacy_bbt().bars != 0) {
3237 fix_legacy_session();
3244 /* check for multiple tempo/meters at the same location, which
3245 ardour2 somehow allowed.
3248 Metrics::iterator prev = _metrics.end();
3249 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3250 if (prev != _metrics.end()) {
3252 MeterSection* prev_m;
3254 TempoSection* prev_t;
3255 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
3256 if (prev_m->pulse() == ms->pulse()) {
3257 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3258 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3261 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
3262 if (prev_t->pulse() == ts->pulse()) {
3263 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3264 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3272 recompute_map (_metrics);
3275 PropertyChanged (PropertyChange ());
3281 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
3283 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
3284 const MeterSection* m;
3285 const TempoSection* t;
3286 const TempoSection* prev_t = 0;
3288 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3290 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
3291 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
3292 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
3293 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
3295 o << "previous : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
3296 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;
3299 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
3300 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
3301 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
3304 o << "------" << std::endl;
3308 TempoMap::n_tempos() const
3310 Glib::Threads::RWLock::ReaderLock lm (lock);
3313 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3314 if (dynamic_cast<const TempoSection*>(*i) != 0) {
3323 TempoMap::n_meters() const
3325 Glib::Threads::RWLock::ReaderLock lm (lock);
3328 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3329 if (dynamic_cast<const MeterSection*>(*i) != 0) {
3338 TempoMap::insert_time (framepos_t where, framecnt_t amount)
3341 Glib::Threads::RWLock::WriterLock lm (lock);
3342 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3343 if ((*i)->frame() >= where && (*i)->movable ()) {
3344 (*i)->set_frame ((*i)->frame() + amount);
3348 /* now reset the BBT time of all metrics, based on their new
3349 * audio time. This is the only place where we do this reverse
3353 Metrics::iterator i;
3354 const MeterSection* meter;
3355 const TempoSection* tempo;
3359 meter = &first_meter ();
3360 tempo = &first_tempo ();
3365 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
3368 MetricSection* prev = 0;
3370 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3373 //TempoMetric metric (*meter, *tempo);
3374 MeterSection* ms = const_cast<MeterSection*>(meter);
3375 TempoSection* ts = const_cast<TempoSection*>(tempo);
3378 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3382 ts->set_pulse (t->pulse());
3384 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3385 ts->set_pulse (m->pulse());
3387 ts->set_frame (prev->frame());
3391 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3392 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3393 ms->set_beat (start);
3394 ms->set_pulse (m->pulse());
3396 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3400 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3401 pair<double, BBT_Time> start = make_pair (beat, bbt_at_beat_locked (_metrics, beat));
3402 ms->set_beat (start);
3403 ms->set_pulse (t->pulse());
3405 ms->set_frame (prev->frame());
3409 // metric will be at frames=0 bbt=1|1|0 by default
3410 // which is correct for our purpose
3413 // cerr << bbt << endl;
3415 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3419 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3421 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3422 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3423 bbt = bbt_at_frame_locked (_metrics, m->frame());
3425 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3431 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3432 /* round up to next beat */
3438 if (bbt.beats != 1) {
3439 /* round up to next bar */
3444 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3445 m->set_beat (start);
3446 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3448 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3450 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3451 abort(); /*NOTREACHED*/
3457 recompute_map (_metrics);
3461 PropertyChanged (PropertyChange ());
3464 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3468 std::list<MetricSection*> metric_kill_list;
3470 TempoSection* last_tempo = NULL;
3471 MeterSection* last_meter = NULL;
3472 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3473 bool meter_after = false; // is there a meter marker likewise?
3475 Glib::Threads::RWLock::WriterLock lm (lock);
3476 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3477 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3478 metric_kill_list.push_back(*i);
3479 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3482 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3486 else if ((*i)->frame() >= where) {
3487 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3488 (*i)->set_frame ((*i)->frame() - amount);
3489 if ((*i)->frame() == where) {
3490 // marker was immediately after end of range
3491 tempo_after = dynamic_cast<TempoSection*> (*i);
3492 meter_after = dynamic_cast<MeterSection*> (*i);
3498 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3499 if (last_tempo && !tempo_after) {
3500 metric_kill_list.remove(last_tempo);
3501 last_tempo->set_frame(where);
3504 if (last_meter && !meter_after) {
3505 metric_kill_list.remove(last_meter);
3506 last_meter->set_frame(where);
3510 //remove all the remaining metrics
3511 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3512 _metrics.remove(*i);
3517 recompute_map (_metrics);
3520 PropertyChanged (PropertyChange ());
3524 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3525 * pos can be -ve, if required.
3528 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3530 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
3533 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3535 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3537 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
3540 /** Add the BBT interval op to pos and return the result */
3542 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3544 Glib::Threads::RWLock::ReaderLock lm (lock);
3546 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3547 pos_bbt.ticks += op.ticks;
3548 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3550 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3552 pos_bbt.beats += op.beats;
3553 /* the meter in effect will start on the bar */
3554 double divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3555 while (pos_bbt.beats >= divisions_per_bar + 1) {
3557 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3558 pos_bbt.beats -= divisions_per_bar;
3560 pos_bbt.bars += op.bars;
3562 return frame_at_bbt_locked (_metrics, pos_bbt);
3565 /** Count the number of beats that are equivalent to distance when going forward,
3569 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3571 return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
3575 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3581 operator<< (std::ostream& o, const Meter& m) {
3582 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3586 operator<< (std::ostream& o, const Tempo& t) {
3587 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3591 operator<< (std::ostream& o, const MetricSection& section) {
3593 o << "MetricSection @ " << section.frame() << ' ';
3595 const TempoSection* ts;
3596 const MeterSection* ms;
3598 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3599 o << *((const Tempo*) ts);
3600 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3601 o << *((const Meter*) ms);