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 /* audio locked meters fake their beat */
1419 if (next_m && next_m->beat() < beat) {
1420 return next_m->beat();
1427 TempoMap::frame_at_beat (const double& beat) const
1429 Glib::Threads::RWLock::ReaderLock lm (lock);
1430 return frame_at_beat_locked (_metrics, beat);
1433 /* meter section based */
1435 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1437 const TempoSection* prev_t = &tempo_section_at_beat_locked (metrics, beat);
1438 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1440 return prev_t->frame_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse(), _frame_rate);
1444 TempoMap::tempo_at_frame (const framepos_t& frame) const
1446 Glib::Threads::RWLock::ReaderLock lm (lock);
1447 return tempo_at_frame_locked (_metrics, frame);
1451 TempoMap::tempo_at_frame_locked (const Metrics& metrics, const framepos_t& frame) const
1453 TempoSection* prev_t = 0;
1455 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1457 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1461 if ((prev_t) && t->frame() > frame) {
1462 /* t is the section past frame */
1463 const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
1464 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
1471 const double ret = prev_t->beats_per_minute();
1472 const Tempo ret_tempo (ret, prev_t->note_type ());
1477 /** returns the frame at which the supplied tempo occurs, or
1478 * the frame of the last tempo section (search exhausted)
1479 * only the position of the first occurence will be returned
1483 TempoMap::frame_at_tempo (const Tempo& tempo) const
1485 Glib::Threads::RWLock::ReaderLock lm (lock);
1486 return frame_at_tempo_locked (_metrics, tempo);
1491 TempoMap::frame_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1493 TempoSection* prev_t = 0;
1494 const double tempo_ppm = tempo.beats_per_minute() / tempo.note_type();
1496 Metrics::const_iterator i;
1498 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1500 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1506 const double t_ppm = t->beats_per_minute() / t->note_type();
1508 if (t_ppm == tempo_ppm) {
1513 const double prev_t_ppm = prev_t->beats_per_minute() / prev_t->note_type();
1515 if ((t_ppm > tempo_ppm && prev_t_ppm < tempo_ppm) || (t_ppm < tempo_ppm && prev_t_ppm > tempo_ppm)) {
1516 const framepos_t ret_frame = prev_t->frame_at_tempo (tempo_ppm, prev_t->pulse(), _frame_rate);
1524 return prev_t->frame();
1528 TempoMap::tempo_at_beat (const double& beat) const
1530 Glib::Threads::RWLock::ReaderLock lm (lock);
1531 const MeterSection* prev_m = &meter_section_at_beat_locked (_metrics, beat);
1532 const TempoSection* prev_t = &tempo_section_at_beat_locked (_metrics, beat);
1533 const double note_type = prev_t->note_type();
1535 return Tempo (prev_t->tempo_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse()) * note_type, note_type);
1539 TempoMap::pulse_at_beat (const double& beat) const
1541 Glib::Threads::RWLock::ReaderLock lm (lock);
1542 return pulse_at_beat_locked (_metrics, beat);
1546 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1548 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1550 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1554 TempoMap::beat_at_pulse (const double& pulse) const
1556 Glib::Threads::RWLock::ReaderLock lm (lock);
1557 return beat_at_pulse_locked (_metrics, pulse);
1561 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1563 MeterSection* prev_m = 0;
1565 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1567 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1568 if (prev_m && m->pulse() > pulse) {
1569 if (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > m->beat()) {
1577 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1582 TempoMap::pulse_at_frame (const framecnt_t& frame) const
1584 Glib::Threads::RWLock::ReaderLock lm (lock);
1585 return pulse_at_frame_locked (_metrics, frame);
1588 /* tempo section based */
1590 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1592 /* HOLD (at least) THE READER LOCK */
1593 TempoSection* prev_t = 0;
1595 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1597 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1601 if (prev_t && t->frame() > frame) {
1602 /*the previous ts is the one containing the frame */
1603 const double ret = prev_t->pulse_at_frame (frame, _frame_rate);
1610 /* treated as constant for this ts */
1611 const double pulses_in_section = (frame - prev_t->frame()) / prev_t->frames_per_pulse (_frame_rate);
1613 return pulses_in_section + prev_t->pulse();
1617 TempoMap::frame_at_pulse (const double& pulse) const
1619 Glib::Threads::RWLock::ReaderLock lm (lock);
1620 return frame_at_pulse_locked (_metrics, pulse);
1623 /* tempo section based */
1625 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1627 /* HOLD THE READER LOCK */
1629 const TempoSection* prev_t = 0;
1631 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1634 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1638 if (prev_t && t->pulse() > pulse) {
1639 return prev_t->frame_at_pulse (pulse, _frame_rate);
1645 /* must be treated as constant, irrespective of _type */
1646 double const pulses_in_section = pulse - prev_t->pulse();
1647 double const dtime = pulses_in_section * prev_t->frames_per_pulse (_frame_rate);
1649 framecnt_t const ret = (framecnt_t) floor (dtime) + prev_t->frame();
1655 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
1657 Glib::Threads::RWLock::ReaderLock lm (lock);
1658 return beat_at_bbt_locked (_metrics, bbt);
1663 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1665 /* CALLER HOLDS READ LOCK */
1667 MeterSection* prev_m = 0;
1669 /* because audio-locked meters have 'fake' integral beats,
1670 there is no pulse offset here.
1672 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1674 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1676 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1677 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1685 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1686 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1687 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1693 TempoMap::bbt_at_beat (const double& beats)
1695 Glib::Threads::RWLock::ReaderLock lm (lock);
1696 return bbt_at_beat_locked (_metrics, beats);
1700 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
1702 /* CALLER HOLDS READ LOCK */
1703 MeterSection* prev_m = 0;
1704 const double beats = max (0.0, b);
1706 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1707 MeterSection* m = 0;
1709 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1711 if (m->beat() > beats) {
1712 /* this is the meter after the one our beat is on*/
1721 const double beats_in_ms = beats - prev_m->beat();
1722 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1723 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1724 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1725 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1729 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1730 ret.beats = (uint32_t) floor (remaining_beats);
1731 ret.bars = total_bars;
1733 /* 0 0 0 to 1 1 0 - based mapping*/
1737 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1739 ret.ticks -= BBT_Time::ticks_per_beat;
1742 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1751 TempoMap::pulse_at_bbt (const Timecode::BBT_Time& bbt)
1753 Glib::Threads::RWLock::ReaderLock lm (lock);
1755 return pulse_at_bbt_locked (_metrics, bbt);
1759 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1761 /* CALLER HOLDS READ LOCK */
1763 MeterSection* prev_m = 0;
1765 /* because audio-locked meters have 'fake' integral beats,
1766 there is no pulse offset here.
1768 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1770 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1772 if (m->bbt().bars > bbt.bars) {
1780 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1781 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
1782 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
1788 TempoMap::bbt_at_pulse (const double& pulse)
1790 Glib::Threads::RWLock::ReaderLock lm (lock);
1792 return bbt_at_pulse_locked (_metrics, pulse);
1796 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1798 MeterSection* prev_m = 0;
1800 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1801 MeterSection* m = 0;
1803 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1806 double const pulses_to_m = m->pulse() - prev_m->pulse();
1807 if (prev_m->pulse() + pulses_to_m > pulse) {
1808 /* this is the meter after the one our beat is on*/
1817 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1818 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1819 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1820 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1821 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1825 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1826 ret.beats = (uint32_t) floor (remaining_beats);
1827 ret.bars = total_bars;
1829 /* 0 0 0 to 1 1 0 mapping*/
1833 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1835 ret.ticks -= BBT_Time::ticks_per_beat;
1838 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1847 TempoMap::bbt_at_frame (framepos_t frame)
1854 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1857 Glib::Threads::RWLock::ReaderLock lm (lock);
1859 return bbt_at_frame_locked (_metrics, frame);
1863 TempoMap::bbt_at_frame_locked (const Metrics& metrics, const framepos_t& frame) const
1870 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1873 const double beat = beat_at_frame_locked (metrics, frame);
1875 return bbt_at_beat_locked (metrics, beat);
1879 TempoMap::frame_at_bbt (const BBT_Time& bbt)
1882 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
1886 if (bbt.beats < 1) {
1887 throw std::logic_error ("beats are counted from one");
1889 Glib::Threads::RWLock::ReaderLock lm (lock);
1891 return frame_at_bbt_locked (_metrics, bbt);
1894 /* meter & tempo section based */
1896 TempoMap::frame_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
1898 /* HOLD THE READER LOCK */
1900 const framepos_t ret = frame_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
1905 TempoMap::check_solved (const Metrics& metrics) const
1907 TempoSection* prev_t = 0;
1908 MeterSection* prev_m = 0;
1910 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1913 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1918 /* check ordering */
1919 if ((t->frame() <= prev_t->frame()) || (t->pulse() <= prev_t->pulse())) {
1923 /* precision check ensures tempo and frames align.*/
1924 if (t->frame() != prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate)) {
1925 if (!t->locked_to_meter()) {
1930 /* gradient limit - who knows what it should be?
1931 things are also ok (if a little chaotic) without this
1933 if (fabs (prev_t->c_func()) > 1000.0) {
1934 //std::cout << "c : " << prev_t->c_func() << std::endl;
1941 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1942 if (prev_m && m->position_lock_style() == AudioTime) {
1943 TempoSection* t = const_cast<TempoSection*>(&tempo_section_at_frame_locked (metrics, m->frame() - 1));
1944 const double nascent_m_pulse = ((m->beat() - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse();
1945 const framepos_t nascent_m_frame = t->frame_at_pulse (nascent_m_pulse, _frame_rate);
1947 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
1961 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
1963 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1965 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1966 if (!t->movable()) {
1967 t->set_active (true);
1970 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
1971 t->set_active (false);
1973 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
1974 t->set_active (true);
1975 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
1984 TempoMap::solve_map_frame (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
1986 TempoSection* prev_t = 0;
1987 TempoSection* section_prev = 0;
1988 framepos_t first_m_frame = 0;
1990 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
1992 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1993 if (!m->movable()) {
1994 first_m_frame = m->frame();
1999 if (section->movable() && frame <= first_m_frame) {
2003 section->set_active (true);
2004 section->set_frame (frame);
2006 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2008 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2015 section_prev = prev_t;
2018 if (t->position_lock_style() == MusicTime) {
2019 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
2020 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
2022 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
2023 if (!t->locked_to_meter()) {
2024 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
2033 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
2034 if (!section->locked_to_meter()) {
2035 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
2039 recompute_tempos (imaginary);
2041 if (check_solved (imaginary)) {
2045 MetricSectionFrameSorter fcmp;
2046 imaginary.sort (fcmp);
2048 recompute_tempos (imaginary);
2050 if (check_solved (imaginary)) {
2058 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2060 TempoSection* prev_t = 0;
2061 TempoSection* section_prev = 0;
2063 section->set_pulse (pulse);
2065 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2067 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2071 if (!t->movable()) {
2078 section_prev = prev_t;
2081 if (t->position_lock_style() == MusicTime) {
2082 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
2083 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
2085 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
2086 if (!t->locked_to_meter()) {
2087 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
2096 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
2097 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
2100 recompute_tempos (imaginary);
2102 if (check_solved (imaginary)) {
2106 MetricSectionSorter cmp;
2107 imaginary.sort (cmp);
2109 recompute_tempos (imaginary);
2111 * XX need a restriction here, but only for this case,
2112 * as audio locked tempos don't interact in the same way.
2114 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2116 * |50 bpm |250 bpm |60 bpm
2117 * drag 250 to the pulse after 60->
2118 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2120 if (check_solved (imaginary)) {
2128 TempoMap::solve_map_frame (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
2130 /* disallow moving first meter past any subsequent one, and any movable meter before the first one */
2131 const MeterSection* other = &meter_section_at_frame_locked (imaginary, frame);
2132 if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->frame() >= frame)) {
2136 if (!section->movable()) {
2137 /* lock the first tempo to our first meter */
2138 if (!set_active_tempos (imaginary, frame)) {
2143 /* it would make sense to bail out if there is no audio-locked meter,
2144 however it may be desirable to move a music-locked meter by frame at some point.
2146 TempoSection* meter_locked_tempo = 0;
2147 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2149 if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
2150 if ((t->locked_to_meter() || !t->movable()) && t->frame() == section->frame()) {
2151 meter_locked_tempo = t;
2157 if (!meter_locked_tempo) {
2161 MeterSection* prev_m = 0;
2163 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2164 bool solved = false;
2166 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2168 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2170 if (prev_m && section->movable()) {
2171 const double beats = (pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor();
2172 if (beats + prev_m->beat() < section->beat()) {
2173 /* set the frame/pulse corresponding to its musical position,
2174 * as an earlier time than this has been requested.
2176 const double new_pulse = ((section->beat() - prev_m->beat())
2177 / prev_m->note_divisor()) + prev_m->pulse();
2179 const framepos_t smallest_frame = frame_at_pulse_locked (future_map, new_pulse);
2181 if ((solved = solve_map_frame (future_map, tempo_copy, smallest_frame))) {
2182 meter_locked_tempo->set_pulse (new_pulse);
2183 solve_map_frame (imaginary, meter_locked_tempo, smallest_frame);
2184 section->set_frame (smallest_frame);
2185 section->set_pulse (new_pulse);
2190 Metrics::const_iterator d = future_map.begin();
2191 while (d != future_map.end()) {
2200 /* all is ok. set section's tempo */
2201 MeterSection* meter_copy = const_cast<MeterSection*> (&meter_section_at_frame_locked (future_map, section->frame()));
2202 meter_copy->set_frame (frame);
2204 if ((solved = solve_map_frame (future_map, tempo_copy, frame))) {
2205 section->set_frame (frame);
2206 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2207 / prev_m->note_divisor()) + prev_m->pulse());
2208 solve_map_frame (imaginary, meter_locked_tempo, frame);
2213 Metrics::const_iterator d = future_map.begin();
2214 while (d != future_map.end()) {
2224 /* not movable (first meter atm) */
2226 tempo_copy->set_frame (frame);
2227 tempo_copy->set_pulse (0.0);
2229 if ((solved = solve_map_frame (future_map, tempo_copy, frame))) {
2230 section->set_frame (frame);
2231 meter_locked_tempo->set_frame (frame);
2232 meter_locked_tempo->set_pulse (0.0);
2233 solve_map_frame (imaginary, meter_locked_tempo, frame);
2238 Metrics::const_iterator d = future_map.begin();
2239 while (d != future_map.end()) {
2248 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2249 section->set_beat (b_bbt);
2250 section->set_pulse (0.0);
2260 MetricSectionFrameSorter fcmp;
2261 imaginary.sort (fcmp);
2263 recompute_meters (imaginary);
2269 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2271 /* disallow setting section to an existing meter's bbt */
2272 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2274 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2275 if (m != section && m->bbt().bars == when.bars) {
2281 MeterSection* prev_m = 0;
2282 MeterSection* section_prev = 0;
2284 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2286 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2287 pair<double, BBT_Time> b_bbt;
2288 double new_pulse = 0.0;
2290 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2291 section_prev = prev_m;
2292 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2293 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2294 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2296 section->set_beat (b_bbt);
2297 section->set_pulse (pulse);
2298 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2303 if (m->position_lock_style() == AudioTime) {
2304 TempoSection* meter_locked_tempo = 0;
2306 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2308 if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
2309 if ((t->locked_to_meter() || !t->movable()) && t->frame() == m->frame()) {
2310 meter_locked_tempo = t;
2316 if (!meter_locked_tempo) {
2321 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2323 if (beats + prev_m->beat() != m->beat()) {
2324 /* tempo/ meter change caused a change in beat (bar). */
2325 b_bbt = make_pair (beats + prev_m->beat()
2326 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2327 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2328 } else if (m->movable()) {
2329 b_bbt = make_pair (m->beat(), m->bbt());
2330 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2333 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2336 meter_locked_tempo->set_pulse (new_pulse);
2337 m->set_beat (b_bbt);
2338 m->set_pulse (new_pulse);
2342 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2343 if (beats + prev_m->beat() != m->beat()) {
2344 /* tempo/ meter change caused a change in beat (bar). */
2345 b_bbt = make_pair (beats + prev_m->beat()
2346 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2348 b_bbt = make_pair (beats + prev_m->beat()
2351 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2352 m->set_beat (b_bbt);
2353 m->set_pulse (new_pulse);
2354 m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2361 if (!section_prev) {
2363 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
2364 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2365 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
2367 section->set_beat (b_bbt);
2368 section->set_pulse (pulse);
2369 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2372 MetricSectionSorter cmp;
2373 imaginary.sort (cmp);
2375 recompute_meters (imaginary);
2380 /** places a copy of _metrics into copy and returns a pointer
2381 * to section's equivalent in copy.
2384 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
2386 TempoSection* ret = 0;
2388 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2391 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2393 ret = new TempoSection (*t);
2394 copy.push_back (ret);
2398 TempoSection* cp = new TempoSection (*t);
2399 copy.push_back (cp);
2401 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2402 MeterSection* cp = new MeterSection (*m);
2403 copy.push_back (cp);
2411 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
2413 MeterSection* ret = 0;
2415 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2418 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2419 TempoSection* cp = new TempoSection (*t);
2420 copy.push_back (cp);
2423 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2425 ret = new MeterSection (*m);
2426 copy.push_back (ret);
2429 MeterSection* cp = new MeterSection (*m);
2430 copy.push_back (cp);
2437 /** answers the question "is this a valid beat position for this tempo section?".
2438 * it returns true if the tempo section can be moved to the requested bbt position,
2439 * leaving the tempo map in a solved state.
2440 * @param section the tempo section to be moved
2441 * @param bbt the requested new position for the tempo section
2442 * @return true if the tempo section can be moved to the position, otherwise false.
2445 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2448 TempoSection* tempo_copy = 0;
2451 Glib::Threads::RWLock::ReaderLock lm (lock);
2452 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
2458 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
2460 Metrics::const_iterator d = copy.begin();
2461 while (d != copy.end()) {
2470 * 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,
2471 * taking any possible reordering as a consequence of this into account.
2472 * @param section - the section to be altered
2473 * @param bbt - the bbt where the altered tempo will fall
2474 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
2476 pair<double, framepos_t>
2477 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
2480 pair<double, framepos_t> ret = make_pair (0.0, 0);
2482 Glib::Threads::RWLock::ReaderLock lm (lock);
2484 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
2486 const double beat = beat_at_bbt_locked (future_map, bbt);
2488 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
2489 ret.first = tempo_copy->pulse();
2490 ret.second = tempo_copy->frame();
2492 ret.first = section->pulse();
2493 ret.second = section->frame();
2496 Metrics::const_iterator d = future_map.begin();
2497 while (d != future_map.end()) {
2505 TempoMap::gui_move_tempo (TempoSection* ts, const framepos_t& frame)
2509 if (ts->position_lock_style() == MusicTime) {
2511 Glib::Threads::RWLock::WriterLock lm (lock);
2512 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2513 const double pulse = pulse_at_frame_locked (future_map, frame);
2514 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
2515 solve_map_pulse (_metrics, ts, pulse);
2516 recompute_meters (_metrics);
2523 Glib::Threads::RWLock::WriterLock lm (lock);
2524 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2525 if (solve_map_frame (future_map, tempo_copy, frame)) {
2526 solve_map_frame (_metrics, ts, frame);
2527 recompute_meters (_metrics);
2532 Metrics::const_iterator d = future_map.begin();
2533 while (d != future_map.end()) {
2538 MetricPositionChanged (); // Emit Signal
2542 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
2546 if (ms->position_lock_style() == AudioTime) {
2549 Glib::Threads::RWLock::WriterLock lm (lock);
2550 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2552 if (solve_map_frame (future_map, copy, frame)) {
2553 solve_map_frame (_metrics, ms, frame);
2554 recompute_tempos (_metrics);
2559 Glib::Threads::RWLock::WriterLock lm (lock);
2560 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2562 const double beat = beat_at_frame_locked (_metrics, frame);
2563 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
2565 if (solve_map_bbt (future_map, copy, bbt)) {
2566 solve_map_bbt (_metrics, ms, bbt);
2567 recompute_tempos (_metrics);
2572 Metrics::const_iterator d = future_map.begin();
2573 while (d != future_map.end()) {
2578 MetricPositionChanged (); // Emit Signal
2582 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2585 bool can_solve = false;
2587 Glib::Threads::RWLock::WriterLock lm (lock);
2588 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2589 tempo_copy->set_beats_per_minute (bpm.beats_per_minute());
2590 recompute_tempos (future_map);
2592 if (check_solved (future_map)) {
2593 ts->set_beats_per_minute (bpm.beats_per_minute());
2594 recompute_map (_metrics);
2599 Metrics::const_iterator d = future_map.begin();
2600 while (d != future_map.end()) {
2605 MetricPositionChanged (); // Emit Signal
2611 TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame, const double& pulse)
2614 Ts (future prev_t) Tnext
2617 |----------|----------
2624 Glib::Threads::RWLock::WriterLock lm (lock);
2630 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
2631 TempoSection* prev_to_prev_t = 0;
2632 const frameoffset_t fr_off = end_frame - frame;
2634 if (prev_t && prev_t->pulse() > 0.0) {
2635 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_frame_locked (future_map, prev_t->frame() - 1));
2638 TempoSection* next_t = 0;
2639 for (Metrics::iterator i = future_map.begin(); i != future_map.end(); ++i) {
2640 TempoSection* t = 0;
2641 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2642 if (t->frame() > ts->frame()) {
2649 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
2650 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
2652 double contribution = 0.0;
2653 double start_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
2655 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2656 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
2659 frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
2660 double end_pulse = prev_t->pulse_at_frame (end_frame, _frame_rate);
2663 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
2665 if (prev_t->position_lock_style() == MusicTime) {
2666 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2668 new_bpm = prev_t->beats_per_minute() * ((frame - prev_to_prev_t->frame())
2669 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
2672 /* prev to prev is irrelevant */
2674 if (start_pulse != prev_t->pulse()) {
2675 new_bpm = prev_t->beats_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
2677 new_bpm = prev_t->beats_per_minute();
2682 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2683 new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame())
2684 / (double) ((frame + prev_t_frame_contribution) - prev_t->frame()));
2686 /* prev_to_prev_t is irrelevant */
2688 if (end_frame != prev_t->frame()) {
2689 new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
2691 new_bpm = prev_t->beats_per_minute();
2699 const framepos_t pulse_pos = prev_t->frame_at_pulse (pulse, _frame_rate);
2701 if (prev_to_prev_t) {
2703 frame_ratio = (((pulse_pos - fr_off) - prev_to_prev_t->frame()) / (double) ((pulse_pos) - prev_to_prev_t->frame()));
2704 pulse_ratio = ((start_pulse - prev_to_prev_t->pulse()) / (end_pulse - prev_to_prev_t->pulse()));
2707 frame_ratio = (((pulse_pos - fr_off) - prev_t->frame()) / (double) ((pulse_pos) - prev_t->frame()));
2708 pulse_ratio = (start_pulse / end_pulse);
2710 new_bpm = prev_t->beats_per_minute() * (pulse_ratio * frame_ratio);
2713 prev_t->set_beats_per_minute (new_bpm);
2714 recompute_tempos (future_map);
2715 recompute_meters (future_map);
2717 if (check_solved (future_map)) {
2718 ts->set_beats_per_minute (new_bpm);
2719 recompute_tempos (_metrics);
2720 recompute_meters (_metrics);
2724 Metrics::const_iterator d = future_map.begin();
2725 while (d != future_map.end()) {
2730 MetricPositionChanged (); // Emit Signal
2734 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2736 Glib::Threads::RWLock::ReaderLock lm (lock);
2738 const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2739 const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2740 const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2742 return frame_at_beat_locked (_metrics, total_beats);
2746 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2748 return round_to_type (fr, dir, Bar);
2752 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2754 return round_to_type (fr, dir, Beat);
2758 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2760 Glib::Threads::RWLock::ReaderLock lm (lock);
2761 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2762 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2763 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2765 ticks -= beats * BBT_Time::ticks_per_beat;
2768 /* round to next (or same iff dir == RoundUpMaybe) */
2770 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2772 if (mod == 0 && dir == RoundUpMaybe) {
2773 /* right on the subdivision, which is fine, so do nothing */
2775 } else if (mod == 0) {
2776 /* right on the subdivision, so the difference is just the subdivision ticks */
2777 ticks += ticks_one_subdivisions_worth;
2780 /* not on subdivision, compute distance to next subdivision */
2782 ticks += ticks_one_subdivisions_worth - mod;
2785 if (ticks >= BBT_Time::ticks_per_beat) {
2786 ticks -= BBT_Time::ticks_per_beat;
2788 } else if (dir < 0) {
2790 /* round to previous (or same iff dir == RoundDownMaybe) */
2792 uint32_t difference = ticks % ticks_one_subdivisions_worth;
2794 if (difference == 0 && dir == RoundDownAlways) {
2795 /* right on the subdivision, but force-rounding down,
2796 so the difference is just the subdivision ticks */
2797 difference = ticks_one_subdivisions_worth;
2800 if (ticks < difference) {
2801 ticks = BBT_Time::ticks_per_beat - ticks;
2803 ticks -= difference;
2807 /* round to nearest */
2810 /* compute the distance to the previous and next subdivision */
2812 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
2814 /* closer to the next subdivision, so shift forward */
2816 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
2818 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
2820 if (ticks > BBT_Time::ticks_per_beat) {
2822 ticks -= BBT_Time::ticks_per_beat;
2823 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
2826 } else if (rem > 0) {
2828 /* closer to previous subdivision, so shift backward */
2832 /* can't go backwards past zero, so ... */
2835 /* step back to previous beat */
2837 ticks = lrint (BBT_Time::ticks_per_beat - rem);
2838 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
2840 ticks = lrint (ticks - rem);
2841 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
2844 /* on the subdivision, do nothing */
2848 const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
2854 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
2856 Glib::Threads::RWLock::ReaderLock lm (lock);
2858 const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
2859 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
2864 /* find bar previous to 'frame' */
2867 return frame_at_bbt_locked (_metrics, bbt);
2869 } else if (dir > 0) {
2870 /* find bar following 'frame' */
2874 return frame_at_bbt_locked (_metrics, bbt);
2876 /* true rounding: find nearest bar */
2877 framepos_t raw_ft = frame_at_bbt_locked (_metrics, bbt);
2880 framepos_t prev_ft = frame_at_bbt_locked (_metrics, bbt);
2882 framepos_t next_ft = frame_at_bbt_locked (_metrics, bbt);
2884 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
2895 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
2896 } else if (dir > 0) {
2897 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
2899 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
2908 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
2909 framepos_t lower, framepos_t upper)
2911 Glib::Threads::RWLock::ReaderLock lm (lock);
2912 int32_t cnt = ceil (beat_at_frame_locked (_metrics, lower));
2914 /* although the map handles negative beats, bbt doesn't. */
2918 while (pos < upper) {
2919 pos = frame_at_beat_locked (_metrics, cnt);
2920 const TempoSection tempo = tempo_section_at_frame_locked (_metrics, pos);
2921 const MeterSection meter = meter_section_at_frame_locked (_metrics, pos);
2922 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
2923 points.push_back (BBTPoint (meter, tempo_at_frame_locked (_metrics, pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
2929 TempoMap::tempo_section_at_frame (framepos_t frame) const
2931 Glib::Threads::RWLock::ReaderLock lm (lock);
2932 return tempo_section_at_frame_locked (_metrics, frame);
2936 TempoMap::tempo_section_at_frame_locked (const Metrics& metrics, framepos_t frame) const
2938 Metrics::const_iterator i;
2939 TempoSection* prev = 0;
2941 for (i = metrics.begin(); i != metrics.end(); ++i) {
2944 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2948 if (prev && t->frame() > frame) {
2958 abort(); /*NOTREACHED*/
2965 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
2967 TempoSection* prev_t = 0;
2968 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
2970 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2972 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2973 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
2983 /* don't use this to calculate length (the tempo is only correct for this frame).
2984 do that stuff based on the beat_at_frame and frame_at_beat api
2987 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
2989 Glib::Threads::RWLock::ReaderLock lm (lock);
2991 const TempoSection* ts_at = &tempo_section_at_frame_locked (_metrics, frame);
2992 const TempoSection* ts_after = 0;
2993 Metrics::const_iterator i;
2995 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
2998 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3002 if ((*i)->frame() > frame) {
3010 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate));
3012 /* must be treated as constant tempo */
3013 return ts_at->frames_per_beat (_frame_rate);
3017 TempoMap::meter_section_at_frame_locked (const Metrics& metrics, framepos_t frame) const
3019 Metrics::const_iterator i;
3020 MeterSection* prev = 0;
3022 for (i = metrics.begin(); i != metrics.end(); ++i) {
3025 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
3027 if (prev && (*i)->frame() > frame) {
3037 abort(); /*NOTREACHED*/
3045 TempoMap::meter_section_at_frame (framepos_t frame) const
3047 Glib::Threads::RWLock::ReaderLock lm (lock);
3048 return meter_section_at_frame_locked (_metrics, frame);
3052 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3054 MeterSection* prev_m = 0;
3056 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3058 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
3059 if (prev_m && m->beat() > beat) {
3070 TempoMap::meter_section_at_beat (double beat) const
3072 Glib::Threads::RWLock::ReaderLock lm (lock);
3073 return meter_section_at_beat_locked (_metrics, beat);
3077 TempoMap::meter_at_frame (framepos_t frame) const
3079 TempoMetric m (metric_at (frame));
3084 TempoMap::fix_legacy_session ()
3086 MeterSection* prev_m = 0;
3087 TempoSection* prev_t = 0;
3089 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3093 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3094 if (!m->movable()) {
3095 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3099 m->set_position_lock_style (AudioTime);
3104 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
3105 + (m->bbt().beats - 1)
3106 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
3108 m->set_beat (start);
3109 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
3110 + (m->bbt().beats - 1)
3111 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
3112 m->set_pulse (start_beat / prev_m->note_divisor());
3115 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3121 if (!t->movable()) {
3124 t->set_position_lock_style (AudioTime);
3130 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
3131 + (t->legacy_bbt().beats - 1)
3132 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
3134 t->set_pulse (beat / prev_m->note_divisor());
3136 /* really shouldn't happen but.. */
3137 t->set_pulse (beat / 4.0);
3146 TempoMap::get_state ()
3148 Metrics::const_iterator i;
3149 XMLNode *root = new XMLNode ("TempoMap");
3152 Glib::Threads::RWLock::ReaderLock lm (lock);
3153 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3154 root->add_child_nocopy ((*i)->get_state());
3162 TempoMap::set_state (const XMLNode& node, int /*version*/)
3165 Glib::Threads::RWLock::WriterLock lm (lock);
3168 XMLNodeConstIterator niter;
3169 Metrics old_metrics (_metrics);
3172 nlist = node.children();
3174 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
3175 XMLNode* child = *niter;
3177 if (child->name() == TempoSection::xml_state_node_name) {
3180 TempoSection* ts = new TempoSection (*child);
3181 _metrics.push_back (ts);
3184 catch (failed_constructor& err){
3185 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3186 _metrics = old_metrics;
3190 } else if (child->name() == MeterSection::xml_state_node_name) {
3193 MeterSection* ms = new MeterSection (*child);
3194 _metrics.push_back (ms);
3197 catch (failed_constructor& err) {
3198 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3199 _metrics = old_metrics;
3205 if (niter == nlist.end()) {
3206 MetricSectionSorter cmp;
3207 _metrics.sort (cmp);
3210 /* check for legacy sessions where bbt was the base musical unit for tempo */
3211 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3213 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3214 if (t->legacy_bbt().bars != 0) {
3215 fix_legacy_session();
3222 /* check for multiple tempo/meters at the same location, which
3223 ardour2 somehow allowed.
3226 Metrics::iterator prev = _metrics.end();
3227 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3228 if (prev != _metrics.end()) {
3230 MeterSection* prev_m;
3232 TempoSection* prev_t;
3233 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
3234 if (prev_m->pulse() == ms->pulse()) {
3235 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3236 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3239 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
3240 if (prev_t->pulse() == ts->pulse()) {
3241 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3242 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3250 recompute_map (_metrics);
3253 PropertyChanged (PropertyChange ());
3259 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
3261 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
3262 const MeterSection* m;
3263 const TempoSection* t;
3264 const TempoSection* prev_t = 0;
3266 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3268 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
3269 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
3270 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
3271 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
3273 o << "previous : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
3274 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;
3277 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
3278 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
3279 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
3282 o << "------" << std::endl;
3286 TempoMap::n_tempos() const
3288 Glib::Threads::RWLock::ReaderLock lm (lock);
3291 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3292 if (dynamic_cast<const TempoSection*>(*i) != 0) {
3301 TempoMap::n_meters() const
3303 Glib::Threads::RWLock::ReaderLock lm (lock);
3306 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3307 if (dynamic_cast<const MeterSection*>(*i) != 0) {
3316 TempoMap::insert_time (framepos_t where, framecnt_t amount)
3319 Glib::Threads::RWLock::WriterLock lm (lock);
3320 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3321 if ((*i)->frame() >= where && (*i)->movable ()) {
3322 (*i)->set_frame ((*i)->frame() + amount);
3326 /* now reset the BBT time of all metrics, based on their new
3327 * audio time. This is the only place where we do this reverse
3331 Metrics::iterator i;
3332 const MeterSection* meter;
3333 const TempoSection* tempo;
3337 meter = &first_meter ();
3338 tempo = &first_tempo ();
3343 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
3346 MetricSection* prev = 0;
3348 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3351 //TempoMetric metric (*meter, *tempo);
3352 MeterSection* ms = const_cast<MeterSection*>(meter);
3353 TempoSection* ts = const_cast<TempoSection*>(tempo);
3356 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3360 ts->set_pulse (t->pulse());
3362 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3363 ts->set_pulse (m->pulse());
3365 ts->set_frame (prev->frame());
3369 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3370 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3371 ms->set_beat (start);
3372 ms->set_pulse (m->pulse());
3374 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3378 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3379 pair<double, BBT_Time> start = make_pair (beat, bbt_at_beat_locked (_metrics, beat));
3380 ms->set_beat (start);
3381 ms->set_pulse (t->pulse());
3383 ms->set_frame (prev->frame());
3387 // metric will be at frames=0 bbt=1|1|0 by default
3388 // which is correct for our purpose
3391 // cerr << bbt << endl;
3393 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3397 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3399 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3400 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3401 bbt = bbt_at_frame_locked (_metrics, m->frame());
3403 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3409 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3410 /* round up to next beat */
3416 if (bbt.beats != 1) {
3417 /* round up to next bar */
3422 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3423 m->set_beat (start);
3424 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3426 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3428 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3429 abort(); /*NOTREACHED*/
3435 recompute_map (_metrics);
3439 PropertyChanged (PropertyChange ());
3442 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3446 std::list<MetricSection*> metric_kill_list;
3448 TempoSection* last_tempo = NULL;
3449 MeterSection* last_meter = NULL;
3450 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3451 bool meter_after = false; // is there a meter marker likewise?
3453 Glib::Threads::RWLock::WriterLock lm (lock);
3454 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3455 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3456 metric_kill_list.push_back(*i);
3457 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3460 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3464 else if ((*i)->frame() >= where) {
3465 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3466 (*i)->set_frame ((*i)->frame() - amount);
3467 if ((*i)->frame() == where) {
3468 // marker was immediately after end of range
3469 tempo_after = dynamic_cast<TempoSection*> (*i);
3470 meter_after = dynamic_cast<MeterSection*> (*i);
3476 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3477 if (last_tempo && !tempo_after) {
3478 metric_kill_list.remove(last_tempo);
3479 last_tempo->set_frame(where);
3482 if (last_meter && !meter_after) {
3483 metric_kill_list.remove(last_meter);
3484 last_meter->set_frame(where);
3488 //remove all the remaining metrics
3489 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3490 _metrics.remove(*i);
3495 recompute_map (_metrics);
3498 PropertyChanged (PropertyChange ());
3502 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3503 * pos can be -ve, if required.
3506 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3508 return frame_at_beat (beat_at_frame (pos) + beats.to_double());
3511 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3513 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3515 return frame_at_beat (beat_at_frame (pos) - beats.to_double());
3518 /** Add the BBT interval op to pos and return the result */
3520 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3522 Glib::Threads::RWLock::ReaderLock lm (lock);
3524 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3525 pos_bbt.ticks += op.ticks;
3526 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3528 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3530 pos_bbt.beats += op.beats;
3531 /* the meter in effect will start on the bar */
3532 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();
3533 while (pos_bbt.beats >= divisions_per_bar + 1) {
3535 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3536 pos_bbt.beats -= divisions_per_bar;
3538 pos_bbt.bars += op.bars;
3540 return frame_at_bbt_locked (_metrics, pos_bbt);
3543 /** Count the number of beats that are equivalent to distance when going forward,
3547 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3549 return Evoral::Beats (beat_at_frame (pos + distance) - beat_at_frame (pos));
3553 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3559 operator<< (std::ostream& o, const Meter& m) {
3560 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3564 operator<< (std::ostream& o, const Tempo& t) {
3565 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3569 operator<< (std::ostream& o, const MetricSection& section) {
3571 o << "MetricSection @ " << section.frame() << ' ';
3573 const TempoSection* ts;
3574 const MeterSection* ms;
3576 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3577 o << *((const Tempo*) ts);
3578 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3579 o << *((const Meter*) ms);