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, true)
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, false), 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 The Shaggs - Things I Wonder
590 https://www.youtube.com/watch?v=9wQK6zMJOoQ
592 Tempo is the rate of the musical pulse.
593 Meters divide the pulses into measures and beats.
595 TempoSections - provide pulses in the form of beats_per_minute() and note_type() where note_type is the division of a whole pulse,
596 and beats_per_minute is the number of note_types in one minute (unlike what its name suggests).
597 Note that Tempo::beats_per_minute() has nothing to do with musical beats. It has been left that way because
598 a shorter one hasn't been found yet (pulse_divisions_per_minute()?).
600 MeterSecions - divide pulses into measures (via divisions_per_bar) and beats (via note_divisor).
602 Both tempos and meters have a pulse position and a frame position.
603 Meters also have a beat position, which is always 0.0 for the first meter.
604 TempoSections and MeterSections may be locked to either audio or music (position lock style).
605 The lock style determines the 'true' position of the section wich is used to calculate the other postion parameters of the section.
607 The first tempo and first meter are special. they must move together, and must be locked to audio.
608 Audio locked tempos which lie before the first meter are made inactive.
609 They will be re-activated if the first meter is again placed before them.
611 With tepo sections potentially being ramped, meters provide a way of mapping beats to whole pulses without
612 referring to the tempo function(s) involved as the distance in whole pulses between a meter and a subsequent beat is
613 sb->beat() - meter->beat() / meter->note_divisor().
614 Because every meter falls on a known pulse, (derived from its bar), the rest is easy as the duration in pulses between
615 two meters is of course
616 (meater_b->bar - meter_a->bar) * meter_a->divisions_per_bar / meter_a->note_divisor.
618 Below, beat calculations are based on meter sections and all pulse and tempo calculations are based on tempo sections.
619 Beat to frame conversion of course requires the use of meter and tempo.
621 Remembering that ramped tempo sections interact, it is important to avoid referring to any other tempos when moving tempo sections,
622 Here, beats (meters) are used to determine the new pulse (see predict_tempo_position())
624 Recomputing the map is the process where the 'missing' position
625 (tempo pulse or meter pulse & beat in the case of AudioTime, frame for MusicTime) is calculated.
626 We construct the tempo map by first using the frame or pulse position (depending on position lock style) of each tempo.
627 We then use this tempo map (really just the tempos) to find the pulse or frame position of each meter (again depending on lock style).
629 Having done this, we can now find any musical duration by selecting the tempo and meter covering the position (or tempo) in question
630 and querying its appropriate meter/tempo.
632 It is important to keep the _metrics in an order that makes sense.
633 Because ramped MusicTime and AudioTime tempos can interact with each other,
634 reordering is frequent. Care must be taken to keep _metrics in a solved state.
635 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
639 Music and audio-locked objects may seem interchangeable on the surface, but when translating
640 between audio samples and beats, keep in mind that a sample is only a quantised approximation
641 of the actual time (in minutes) of a beat.
642 Thus if a gui user points to the frame occupying the start of a music-locked object on 1|3|0, it does not
643 mean that this frame is the actual location in time of 1|3|0.
645 You cannot use a frame measurement to determine beat distance except under special circumstances
646 (e.g. where the user has requested that a beat lie on a SMPTE frame or if the tempo is known to be constant over the duration).
648 This means is that a user operating on a musical grid must supply the desired beat position and/or current beat quantization in order for the
649 sample space the user is operating at to be translated correctly to the object.
651 The current approach is to interpret the supplied frame using the grid division the user has currently selected.
652 If the user has no musical grid set, they are actually operating in sample space (even SMPTE frames are rounded to audio frame), so
653 the supplied audio frame is interpreted as the desired musical location (beat_at_frame()).
655 tldr: Beat, being a function of time, has nothing to do with sample rate, but time quantization can get in the way of precision.
657 When frame_at_beat() is called, the position calculation is performed in pulses and minutes.
658 The result is rounded to audio frames.
659 When beat_at_frame() is called, the frame is converted to minutes, with no rounding performed on the result.
662 frame_at_beat (beat_at_frame (frame)) == frame
664 beat_at_frame (frame_at_beat (beat)) != beat due to the time quantization of frame_at_beat().
666 Doing the second one will result in a beat distance error of up to 0.5 audio samples.
667 So instead work in pulses and/or beats and only use beat position to caclulate frame position (e.g. after tempo change).
668 For audio-locked objects, use frame position to calculate beat position.
670 The above pointless example would then do:
671 beat_at_pulse (pulse_at_beat (beat)) to avoid rounding.
674 struct MetricSectionSorter {
675 bool operator() (const MetricSection* a, const MetricSection* b) {
676 return a->pulse() < b->pulse();
680 struct MetricSectionFrameSorter {
681 bool operator() (const MetricSection* a, const MetricSection* b) {
682 return a->frame() < b->frame();
686 TempoMap::TempoMap (framecnt_t fr)
689 BBT_Time start (1, 1, 0);
691 TempoSection *t = new TempoSection (0.0, 0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Ramp, AudioTime);
692 MeterSection *m = new MeterSection (0.0, 0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime);
694 t->set_movable (false);
695 m->set_movable (false);
697 /* note: frame time is correct (zero) for both of these */
699 _metrics.push_back (t);
700 _metrics.push_back (m);
704 TempoMap::~TempoMap ()
706 Metrics::const_iterator d = _metrics.begin();
707 while (d != _metrics.end()) {
715 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
717 bool removed = false;
720 Glib::Threads::RWLock::WriterLock lm (lock);
721 if ((removed = remove_tempo_locked (tempo))) {
722 if (complete_operation) {
723 recompute_map (_metrics);
728 if (removed && complete_operation) {
729 PropertyChanged (PropertyChange ());
734 TempoMap::remove_tempo_locked (const TempoSection& tempo)
738 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
739 if (dynamic_cast<TempoSection*> (*i) != 0) {
740 if (tempo.frame() == (*i)->frame()) {
741 if ((*i)->movable()) {
754 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
756 bool removed = false;
759 Glib::Threads::RWLock::WriterLock lm (lock);
760 if ((removed = remove_meter_locked (tempo))) {
761 if (complete_operation) {
762 recompute_map (_metrics);
767 if (removed && complete_operation) {
768 PropertyChanged (PropertyChange ());
773 TempoMap::remove_meter_locked (const MeterSection& meter)
777 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
779 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
780 if (meter.frame() == (*i)->frame()) {
781 if (t->locked_to_meter()) {
790 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
791 if (dynamic_cast<MeterSection*> (*i) != 0) {
792 if (meter.frame() == (*i)->frame()) {
793 if ((*i)->movable()) {
806 TempoMap::do_insert (MetricSection* section)
808 bool need_add = true;
809 /* we only allow new meters to be inserted on beat 1 of an existing
813 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
815 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
817 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
818 corrected.second.beats = 1;
819 corrected.second.ticks = 0;
820 corrected.first = beat_at_bbt_locked (_metrics, corrected.second);
821 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
822 m->bbt(), corrected.second) << endmsg;
823 //m->set_pulse (corrected);
827 /* Look for any existing MetricSection that is of the same type and
828 in the same bar as the new one, and remove it before adding
829 the new one. Note that this means that if we find a matching,
830 existing section, we can break out of the loop since we're
831 guaranteed that there is only one such match.
834 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
836 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
837 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
838 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
839 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
841 if (tempo && insert_tempo) {
844 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
845 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
847 if (!tempo->movable()) {
849 /* can't (re)move this section, so overwrite
850 * its data content (but not its properties as
854 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
855 (*i)->set_position_lock_style (AudioTime);
857 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
858 t->set_type (insert_tempo->type());
868 } else if (meter && insert_meter) {
872 bool const ipm = insert_meter->position_lock_style() == MusicTime;
874 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
876 if (!meter->movable()) {
878 /* can't (re)move this section, so overwrite
879 * its data content (but not its properties as
883 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
884 (*i)->set_position_lock_style (AudioTime);
894 /* non-matching types, so we don't care */
898 /* Add the given MetricSection, if we didn't just reset an existing
903 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
904 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
907 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
908 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
911 bool const ipm = insert_meter->position_lock_style() == MusicTime;
912 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
917 } else if (insert_tempo) {
918 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
919 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
922 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
923 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
930 _metrics.insert (i, section);
931 //dump (_metrics, std::cout);
936 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, ARDOUR::TempoSection::Type type, PositionLockStyle pls)
938 TempoSection* ts = 0;
940 Glib::Threads::RWLock::WriterLock lm (lock);
941 ts = add_tempo_locked (tempo, pulse, frame, type, pls, true);
945 PropertyChanged (PropertyChange ());
951 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, TempoSection::Type type, PositionLockStyle pls)
953 const bool locked_to_meter = ts.locked_to_meter();
956 Glib::Threads::RWLock::WriterLock lm (lock);
957 TempoSection& first (first_tempo());
958 if (ts.frame() != first.frame()) {
959 remove_tempo_locked (ts);
960 add_tempo_locked (tempo, pulse, frame, type, pls, true, locked_to_meter);
962 first.set_type (type);
963 first.set_pulse (0.0);
964 first.set_frame (frame);
965 first.set_position_lock_style (AudioTime);
967 /* cannot move the first tempo section */
968 *static_cast<Tempo*>(&first) = tempo;
969 recompute_map (_metrics);
974 PropertyChanged (PropertyChange ());
978 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, framepos_t frame
979 , TempoSection::Type type, PositionLockStyle pls, bool recompute, bool locked_to_meter)
981 TempoSection* t = new TempoSection (pulse, frame, tempo.beats_per_minute(), tempo.note_type(), type, pls);
982 t->set_locked_to_meter (locked_to_meter);
987 if (pls == AudioTime) {
988 solved = solve_map_frame (_metrics, t, t->frame());
990 solved = solve_map_pulse (_metrics, t, t->pulse());
992 recompute_meters (_metrics);
996 remove_tempo_locked (*t);
1004 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, const framepos_t& frame, PositionLockStyle pls)
1006 MeterSection* m = 0;
1008 Glib::Threads::RWLock::WriterLock lm (lock);
1009 m = add_meter_locked (meter, beat, where, frame, pls, true);
1014 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1015 dump (_metrics, std::cerr);
1019 PropertyChanged (PropertyChange ());
1024 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, const framepos_t& frame, PositionLockStyle pls)
1027 Glib::Threads::RWLock::WriterLock lm (lock);
1028 const double beat = beat_at_bbt_locked (_metrics, where);
1031 remove_meter_locked (ms);
1032 add_meter_locked (meter, beat, where, frame, pls, true);
1034 MeterSection& first (first_meter());
1035 TempoSection& first_t (first_tempo());
1036 /* cannot move the first meter section */
1037 *static_cast<Meter*>(&first) = meter;
1038 first.set_position_lock_style (AudioTime);
1039 first.set_pulse (0.0);
1040 first.set_frame (frame);
1041 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1042 first.set_beat (beat);
1043 first_t.set_frame (first.frame());
1044 first_t.set_pulse (0.0);
1045 first_t.set_position_lock_style (AudioTime);
1046 recompute_map (_metrics);
1050 PropertyChanged (PropertyChange ());
1054 TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute)
1056 const MeterSection& prev_m = meter_section_at_frame_locked (_metrics, frame - 1);
1057 const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1058 const BBT_Time bbt (where);
1059 TempoSection* mlt = 0;
1061 if (pls == AudioTime) {
1062 /* add meter-locked tempo */
1063 mlt = add_tempo_locked (tempo_at_frame_locked (_metrics, frame), pulse, frame, TempoSection::Ramp, AudioTime, true, true);
1071 MeterSection* new_meter = new MeterSection (pulse, frame, beat, bbt, meter.divisions_per_bar(), meter.note_divisor(), pls);
1073 do_insert (new_meter);
1077 if (pls == AudioTime) {
1078 solve_map_frame (_metrics, new_meter, frame);
1080 solve_map_bbt (_metrics, new_meter, where);
1088 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1090 Tempo newtempo (beats_per_minute, note_type);
1093 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1094 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1099 Glib::Threads::RWLock::WriterLock lm (lock);
1100 *((Tempo*) t) = newtempo;
1101 recompute_map (_metrics);
1103 PropertyChanged (PropertyChange ());
1110 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1112 Tempo newtempo (beats_per_minute, note_type);
1115 TempoSection* first;
1116 Metrics::iterator i;
1118 /* find the TempoSection immediately preceding "where"
1121 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1123 if ((*i)->frame() > where) {
1129 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1142 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1152 Glib::Threads::RWLock::WriterLock lm (lock);
1153 /* cannot move the first tempo section */
1154 *((Tempo*)prev) = newtempo;
1155 recompute_map (_metrics);
1158 PropertyChanged (PropertyChange ());
1162 TempoMap::first_meter () const
1164 const MeterSection *m = 0;
1166 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1167 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1172 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1173 abort(); /*NOTREACHED*/
1178 TempoMap::first_meter ()
1180 MeterSection *m = 0;
1182 /* CALLER MUST HOLD LOCK */
1184 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1185 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1190 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1191 abort(); /*NOTREACHED*/
1196 TempoMap::first_tempo () const
1198 const TempoSection *t = 0;
1200 /* CALLER MUST HOLD LOCK */
1202 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1203 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1207 if (!t->movable()) {
1213 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1214 abort(); /*NOTREACHED*/
1219 TempoMap::first_tempo ()
1221 TempoSection *t = 0;
1223 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1224 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1228 if (!t->movable()) {
1234 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1235 abort(); /*NOTREACHED*/
1239 TempoMap::recompute_tempos (Metrics& metrics)
1241 TempoSection* prev_t = 0;
1243 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1246 if ((*i)->is_tempo()) {
1247 t = static_cast<TempoSection*> (*i);
1251 if (!t->movable()) {
1259 if (t->position_lock_style() == AudioTime) {
1260 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1261 if (!t->locked_to_meter()) {
1262 t->set_pulse (prev_t->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
1266 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1267 t->set_frame (prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
1274 prev_t->set_c_func (0.0);
1277 /* tempos must be positioned correctly.
1278 the current approach is to use a meter's bbt time as its base position unit.
1279 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1280 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1283 TempoMap::recompute_meters (Metrics& metrics)
1285 MeterSection* meter = 0;
1286 MeterSection* prev_m = 0;
1288 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1289 if (!(*mi)->is_tempo()) {
1290 meter = static_cast<MeterSection*> (*mi);
1291 if (meter->position_lock_style() == AudioTime) {
1293 pair<double, BBT_Time> b_bbt;
1294 TempoSection* meter_locked_tempo = 0;
1295 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1297 if ((*ii)->is_tempo()) {
1298 t = static_cast<TempoSection*> (*ii);
1299 if ((t->locked_to_meter() || !t->movable()) && t->frame() == meter->frame()) {
1300 meter_locked_tempo = t;
1307 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1308 if (beats + prev_m->beat() != meter->beat()) {
1309 /* reordering caused a bbt change */
1310 b_bbt = make_pair (beats + prev_m->beat()
1311 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1312 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1314 } else if (meter->movable()) {
1315 b_bbt = make_pair (meter->beat(), meter->bbt());
1316 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1319 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1321 if (meter_locked_tempo) {
1322 meter_locked_tempo->set_pulse (pulse);
1324 meter->set_beat (b_bbt);
1325 meter->set_pulse (pulse);
1330 pair<double, BBT_Time> b_bbt;
1332 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1333 if (beats + prev_m->beat() != meter->beat()) {
1334 /* reordering caused a bbt change */
1335 b_bbt = make_pair (beats + prev_m->beat()
1336 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1338 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1340 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1342 /* shouldn't happen - the first is audio-locked */
1343 pulse = pulse_at_beat_locked (metrics, meter->beat());
1344 b_bbt = make_pair (meter->beat(), meter->bbt());
1347 meter->set_beat (b_bbt);
1348 meter->set_pulse (pulse);
1349 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1358 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1360 /* CALLER MUST HOLD WRITE LOCK */
1364 /* we will actually stop once we hit
1371 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1374 /* silly call from Session::process() during startup
1379 recompute_tempos (metrics);
1380 recompute_meters (metrics);
1384 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1386 Glib::Threads::RWLock::ReaderLock lm (lock);
1387 TempoMetric m (first_meter(), first_tempo());
1389 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1390 at something, because we insert the default tempo and meter during
1391 TempoMap construction.
1393 now see if we can find better candidates.
1396 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1398 if ((*i)->frame() > frame) {
1412 /* XX meters only */
1414 TempoMap::metric_at (BBT_Time bbt) const
1416 Glib::Threads::RWLock::ReaderLock lm (lock);
1417 TempoMetric m (first_meter(), first_tempo());
1419 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1420 at something, because we insert the default tempo and meter during
1421 TempoMap construction.
1423 now see if we can find better candidates.
1426 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1428 if (!(*i)->is_tempo()) {
1429 mw = static_cast<MeterSection*> (*i);
1430 BBT_Time section_start (mw->bbt());
1432 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1444 TempoMap::beat_at_frame (const framecnt_t& frame) const
1446 Glib::Threads::RWLock::ReaderLock lm (lock);
1447 return beat_at_frame_locked (_metrics, frame);
1450 /* meter / tempo section based */
1452 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1454 const TempoSection& ts = tempo_section_at_frame_locked (metrics, frame);
1455 MeterSection* prev_m = 0;
1456 MeterSection* next_m = 0;
1458 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1459 if (!(*i)->is_tempo()) {
1460 if (prev_m && (*i)->frame() > frame) {
1461 next_m = static_cast<MeterSection*> (*i);
1464 prev_m = static_cast<MeterSection*> (*i);
1467 if (frame < prev_m->frame()) {
1470 const double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
1472 /* audio locked meters fake their beat */
1473 if (next_m && next_m->beat() < beat) {
1474 return next_m->beat();
1481 TempoMap::frame_at_beat (const double& beat) const
1483 Glib::Threads::RWLock::ReaderLock lm (lock);
1484 return frame_at_beat_locked (_metrics, beat);
1487 /* meter & tempo section based */
1489 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1491 MeterSection* prev_m = 0;
1492 TempoSection* prev_t = 0;
1496 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1497 if (!(*i)->is_tempo()) {
1498 m = static_cast<MeterSection*> (*i);
1499 if (prev_m && m->beat() > beat) {
1508 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1509 if ((*i)->is_tempo()) {
1510 t = static_cast<TempoSection*> (*i);
1511 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1519 return prev_t->frame_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse(), _frame_rate);
1523 TempoMap::tempo_at_frame (const framepos_t& frame) const
1525 Glib::Threads::RWLock::ReaderLock lm (lock);
1526 return tempo_at_frame_locked (_metrics, frame);
1530 TempoMap::tempo_at_frame_locked (const Metrics& metrics, const framepos_t& frame) const
1532 TempoSection* prev_t = 0;
1536 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1537 if ((*i)->is_tempo()) {
1538 t = static_cast<TempoSection*> (*i);
1542 if ((prev_t) && t->frame() > frame) {
1543 /* t is the section past frame */
1544 const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
1545 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
1552 const double ret = prev_t->beats_per_minute();
1553 const Tempo ret_tempo (ret, prev_t->note_type ());
1558 /** returns the frame at which the supplied tempo occurs, or
1559 * the frame of the last tempo section (search exhausted)
1560 * only the position of the first occurence will be returned
1564 TempoMap::frame_at_tempo (const Tempo& tempo) const
1566 Glib::Threads::RWLock::ReaderLock lm (lock);
1567 return frame_at_tempo_locked (_metrics, tempo);
1572 TempoMap::frame_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1574 TempoSection* prev_t = 0;
1575 const double tempo_ppm = tempo.beats_per_minute() / tempo.note_type();
1577 Metrics::const_iterator i;
1579 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1581 if ((*i)->is_tempo()) {
1582 t = static_cast<TempoSection*> (*i);
1588 const double t_ppm = t->beats_per_minute() / t->note_type();
1590 if (t_ppm == tempo_ppm) {
1595 const double prev_t_ppm = prev_t->beats_per_minute() / prev_t->note_type();
1597 if ((t_ppm > tempo_ppm && prev_t_ppm < tempo_ppm) || (t_ppm < tempo_ppm && prev_t_ppm > tempo_ppm)) {
1598 return prev_t->frame_at_tempo (tempo_ppm, prev_t->pulse(), _frame_rate);
1605 return prev_t->frame();
1608 /** more precise than doing tempo_at_frame (frame_at_beat (b)),
1609 * as there is no intermediate frame rounding.
1612 TempoMap::tempo_at_beat (const double& beat) const
1614 Glib::Threads::RWLock::ReaderLock lm (lock);
1615 const MeterSection* prev_m = &meter_section_at_beat_locked (_metrics, beat);
1616 const TempoSection* prev_t = &tempo_section_at_beat_locked (_metrics, beat);
1617 const double note_type = prev_t->note_type();
1619 return Tempo (prev_t->tempo_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse()) * note_type, note_type);
1623 TempoMap::pulse_at_beat (const double& beat) const
1625 Glib::Threads::RWLock::ReaderLock lm (lock);
1626 return pulse_at_beat_locked (_metrics, beat);
1630 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1632 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1634 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1638 TempoMap::beat_at_pulse (const double& pulse) const
1640 Glib::Threads::RWLock::ReaderLock lm (lock);
1641 return beat_at_pulse_locked (_metrics, pulse);
1645 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1647 MeterSection* prev_m = 0;
1649 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1651 if (!(*i)->is_tempo()) {
1652 m = static_cast<MeterSection*> (*i);
1653 if (prev_m && m->pulse() > pulse) {
1654 if (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > m->beat()) {
1662 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1667 TempoMap::pulse_at_frame (const framepos_t& frame) const
1669 Glib::Threads::RWLock::ReaderLock lm (lock);
1670 return pulse_at_frame_locked (_metrics, frame);
1673 /* tempo section based */
1675 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framepos_t& frame) const
1677 /* HOLD (at least) THE READER LOCK */
1678 TempoSection* prev_t = 0;
1680 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1682 if ((*i)->is_tempo()) {
1683 t = static_cast<TempoSection*> (*i);
1687 if (prev_t && t->frame() > frame) {
1688 /*the previous ts is the one containing the frame */
1689 const double ret = prev_t->pulse_at_frame (frame, _frame_rate);
1696 /* treated as constant for this ts */
1697 const double pulses_in_section = (frame - prev_t->frame()) / prev_t->frames_per_pulse (_frame_rate);
1699 return pulses_in_section + prev_t->pulse();
1703 TempoMap::frame_at_pulse (const double& pulse) const
1705 Glib::Threads::RWLock::ReaderLock lm (lock);
1706 return frame_at_pulse_locked (_metrics, pulse);
1709 /* tempo section based */
1711 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1713 /* HOLD THE READER LOCK */
1715 const TempoSection* prev_t = 0;
1717 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1720 if ((*i)->is_tempo()) {
1721 t = static_cast<TempoSection*> (*i);
1725 if (prev_t && t->pulse() > pulse) {
1726 return prev_t->frame_at_pulse (pulse, _frame_rate);
1732 /* must be treated as constant, irrespective of _type */
1733 double const dtime = (pulse - prev_t->pulse()) * prev_t->frames_per_pulse (_frame_rate);
1735 return (framecnt_t) floor (dtime) + prev_t->frame();
1739 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
1741 Glib::Threads::RWLock::ReaderLock lm (lock);
1742 return beat_at_bbt_locked (_metrics, bbt);
1747 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1749 /* CALLER HOLDS READ LOCK */
1751 MeterSection* prev_m = 0;
1753 /* because audio-locked meters have 'fake' integral beats,
1754 there is no pulse offset here.
1758 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1759 if (!(*i)->is_tempo()) {
1760 m = static_cast<MeterSection*> (*i);
1762 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1763 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1771 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1772 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1773 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1779 TempoMap::bbt_at_beat (const double& beats)
1781 Glib::Threads::RWLock::ReaderLock lm (lock);
1782 return bbt_at_beat_locked (_metrics, beats);
1786 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
1788 /* CALLER HOLDS READ LOCK */
1789 MeterSection* prev_m = 0;
1790 const double beats = max (0.0, b);
1792 MeterSection* m = 0;
1794 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1795 if (!(*i)->is_tempo()) {
1796 m = static_cast<MeterSection*> (*i);
1798 if (m->beat() > beats) {
1799 /* this is the meter after the one our beat is on*/
1808 const double beats_in_ms = beats - prev_m->beat();
1809 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1810 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1811 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1812 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1816 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1817 ret.beats = (uint32_t) floor (remaining_beats);
1818 ret.bars = total_bars;
1820 /* 0 0 0 to 1 1 0 - based mapping*/
1824 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1826 ret.ticks -= BBT_Time::ticks_per_beat;
1829 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1838 TempoMap::pulse_at_bbt (const Timecode::BBT_Time& bbt)
1840 Glib::Threads::RWLock::ReaderLock lm (lock);
1842 return pulse_at_bbt_locked (_metrics, bbt);
1846 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1848 /* CALLER HOLDS READ LOCK */
1850 MeterSection* prev_m = 0;
1852 /* because audio-locked meters have 'fake' integral beats,
1853 there is no pulse offset here.
1857 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1858 if (!(*i)->is_tempo()) {
1859 m = static_cast<MeterSection*> (*i);
1861 if (m->bbt().bars > bbt.bars) {
1869 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1870 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
1871 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
1877 TempoMap::bbt_at_pulse (const double& pulse)
1879 Glib::Threads::RWLock::ReaderLock lm (lock);
1881 return bbt_at_pulse_locked (_metrics, pulse);
1885 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1887 MeterSection* prev_m = 0;
1889 MeterSection* m = 0;
1891 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1893 if (!(*i)->is_tempo()) {
1894 m = static_cast<MeterSection*> (*i);
1897 double const pulses_to_m = m->pulse() - prev_m->pulse();
1898 if (prev_m->pulse() + pulses_to_m > pulse) {
1899 /* this is the meter after the one our beat is on*/
1908 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1909 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1910 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1911 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1912 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1916 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1917 ret.beats = (uint32_t) floor (remaining_beats);
1918 ret.bars = total_bars;
1920 /* 0 0 0 to 1 1 0 mapping*/
1924 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1926 ret.ticks -= BBT_Time::ticks_per_beat;
1929 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1938 TempoMap::bbt_at_frame (framepos_t frame)
1945 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1948 Glib::Threads::RWLock::ReaderLock lm (lock);
1950 return bbt_at_frame_locked (_metrics, frame);
1954 TempoMap::bbt_at_frame_rt (framepos_t frame)
1956 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
1959 throw std::logic_error ("TempoMap::bbt_time_rt() could not lock tempo map");
1962 return bbt_at_frame_locked (_metrics, frame);
1966 TempoMap::bbt_at_frame_locked (const Metrics& metrics, const framepos_t& frame) const
1973 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1977 const TempoSection& ts = tempo_section_at_frame_locked (metrics, frame);
1978 MeterSection* prev_m = 0;
1979 MeterSection* next_m = 0;
1983 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1984 if (!(*i)->is_tempo()) {
1985 m = static_cast<MeterSection*> (*i);
1986 if (prev_m && m->frame() > frame) {
1994 double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
1996 /* handle frame before first meter */
1997 if (frame < prev_m->frame()) {
2000 /* audio locked meters fake their beat */
2001 if (next_m && next_m->beat() < beat) {
2002 beat = next_m->beat();
2005 beat = max (0.0, beat);
2007 const double beats_in_ms = beat - prev_m->beat();
2008 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2009 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2010 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2011 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2015 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2016 ret.beats = (uint32_t) floor (remaining_beats);
2017 ret.bars = total_bars;
2019 /* 0 0 0 to 1 1 0 - based mapping*/
2023 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2025 ret.ticks -= BBT_Time::ticks_per_beat;
2028 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2037 TempoMap::frame_at_bbt (const BBT_Time& bbt)
2040 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
2044 if (bbt.beats < 1) {
2045 throw std::logic_error ("beats are counted from one");
2047 Glib::Threads::RWLock::ReaderLock lm (lock);
2049 return frame_at_bbt_locked (_metrics, bbt);
2052 /* meter & tempo section based */
2054 TempoMap::frame_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2056 /* HOLD THE READER LOCK */
2058 const framepos_t ret = frame_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2063 TempoMap::check_solved (const Metrics& metrics) const
2065 TempoSection* prev_t = 0;
2066 MeterSection* prev_m = 0;
2068 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2071 if ((*i)->is_tempo()) {
2072 t = static_cast<TempoSection*> (*i);
2077 /* check ordering */
2078 if ((t->frame() <= prev_t->frame()) || (t->pulse() <= prev_t->pulse())) {
2082 /* precision check ensures tempo and frames align.*/
2083 if (t->frame() != prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate)) {
2084 if (!t->locked_to_meter()) {
2089 /* gradient limit - who knows what it should be?
2090 things are also ok (if a little chaotic) without this
2092 if (fabs (prev_t->c_func()) > 1000.0) {
2093 //std::cout << "c : " << prev_t->c_func() << std::endl;
2100 if (!(*i)->is_tempo()) {
2101 m = static_cast<MeterSection*> (*i);
2102 if (prev_m && m->position_lock_style() == AudioTime) {
2103 const TempoSection* t = &tempo_section_at_frame_locked (metrics, m->frame() - 1);
2104 const double nascent_m_pulse = ((m->beat() - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse();
2105 const framepos_t nascent_m_frame = t->frame_at_pulse (nascent_m_pulse, _frame_rate);
2107 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
2121 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
2123 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2125 if ((*i)->is_tempo()) {
2126 t = static_cast<TempoSection*> (*i);
2127 if (!t->movable()) {
2128 t->set_active (true);
2131 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
2132 t->set_active (false);
2134 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
2135 t->set_active (true);
2136 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
2145 TempoMap::solve_map_frame (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
2147 TempoSection* prev_t = 0;
2148 TempoSection* section_prev = 0;
2149 framepos_t first_m_frame = 0;
2151 /* can't move a tempo before the first meter */
2152 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2154 if (!(*i)->is_tempo()) {
2155 m = static_cast<MeterSection*> (*i);
2156 if (!m->movable()) {
2157 first_m_frame = m->frame();
2162 if (section->movable() && frame <= first_m_frame) {
2166 section->set_active (true);
2167 section->set_frame (frame);
2169 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2171 if ((*i)->is_tempo()) {
2172 t = static_cast<TempoSection*> (*i);
2179 section_prev = prev_t;
2180 if (t->locked_to_meter()) {
2185 if (t->position_lock_style() == MusicTime) {
2186 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
2187 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
2189 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
2190 if (!t->locked_to_meter()) {
2191 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
2200 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
2201 if (!section->locked_to_meter()) {
2202 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
2207 recompute_tempos (imaginary);
2209 if (check_solved (imaginary)) {
2212 dunp (imaginary, std::cout);
2216 MetricSectionFrameSorter fcmp;
2217 imaginary.sort (fcmp);
2219 recompute_tempos (imaginary);
2221 if (check_solved (imaginary)) {
2229 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2231 TempoSection* prev_t = 0;
2232 TempoSection* section_prev = 0;
2234 section->set_pulse (pulse);
2236 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2238 if ((*i)->is_tempo()) {
2239 t = static_cast<TempoSection*> (*i);
2243 if (!t->movable()) {
2250 section_prev = prev_t;
2253 if (t->position_lock_style() == MusicTime) {
2254 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
2255 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
2257 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
2258 if (!t->locked_to_meter()) {
2259 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
2268 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
2269 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
2273 recompute_tempos (imaginary);
2275 if (check_solved (imaginary)) {
2278 dunp (imaginary, std::cout);
2282 MetricSectionSorter cmp;
2283 imaginary.sort (cmp);
2285 recompute_tempos (imaginary);
2287 * XX need a restriction here, but only for this case,
2288 * as audio locked tempos don't interact in the same way.
2290 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2292 * |50 bpm |250 bpm |60 bpm
2293 * drag 250 to the pulse after 60->
2294 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2296 if (check_solved (imaginary)) {
2304 TempoMap::solve_map_frame (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
2306 /* disallow moving first meter past any subsequent one, and any movable meter before the first one */
2307 const MeterSection* other = &meter_section_at_frame_locked (imaginary, frame);
2308 if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->frame() >= frame)) {
2312 if (!section->movable()) {
2313 /* lock the first tempo to our first meter */
2314 if (!set_active_tempos (imaginary, frame)) {
2319 TempoSection* meter_locked_tempo = 0;
2321 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2323 if ((*ii)->is_tempo()) {
2324 t = static_cast<TempoSection*> (*ii);
2325 if ((t->locked_to_meter() || !t->movable()) && t->frame() == section->frame()) {
2326 meter_locked_tempo = t;
2332 if (!meter_locked_tempo) {
2336 MeterSection* prev_m = 0;
2338 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2339 bool solved = false;
2341 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2343 if (!(*i)->is_tempo()) {
2344 m = static_cast<MeterSection*> (*i);
2346 if (prev_m && section->movable()) {
2347 const double beats = (pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor();
2348 if (beats + prev_m->beat() < section->beat()) {
2349 /* set the frame/pulse corresponding to its musical position,
2350 * as an earlier time than this has been requested.
2352 const double new_pulse = ((section->beat() - prev_m->beat())
2353 / prev_m->note_divisor()) + prev_m->pulse();
2355 const framepos_t smallest_frame = frame_at_pulse_locked (future_map, new_pulse);
2357 if ((solved = solve_map_frame (future_map, tempo_copy, smallest_frame))) {
2358 meter_locked_tempo->set_pulse (new_pulse);
2359 solve_map_frame (imaginary, meter_locked_tempo, smallest_frame);
2360 section->set_frame (smallest_frame);
2361 section->set_pulse (new_pulse);
2366 Metrics::const_iterator d = future_map.begin();
2367 while (d != future_map.end()) {
2376 /* all is ok. set section's locked tempo if allowed.
2377 possibly disallowed if there is an adjacent audio-locked tempo.
2378 XX this check could possibly go. its never actually happened here.
2380 MeterSection* meter_copy = const_cast<MeterSection*> (&meter_section_at_frame_locked (future_map, section->frame()));
2381 meter_copy->set_frame (frame);
2383 if ((solved = solve_map_frame (future_map, tempo_copy, frame))) {
2384 section->set_frame (frame);
2385 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2386 / prev_m->note_divisor()) + prev_m->pulse());
2387 solve_map_frame (imaginary, meter_locked_tempo, frame);
2392 Metrics::const_iterator d = future_map.begin();
2393 while (d != future_map.end()) {
2403 /* not movable (first meter atm) */
2405 tempo_copy->set_frame (frame);
2406 tempo_copy->set_pulse (0.0);
2408 if ((solved = solve_map_frame (future_map, tempo_copy, frame))) {
2409 section->set_frame (frame);
2410 meter_locked_tempo->set_frame (frame);
2411 meter_locked_tempo->set_pulse (0.0);
2412 solve_map_frame (imaginary, meter_locked_tempo, frame);
2417 Metrics::const_iterator d = future_map.begin();
2418 while (d != future_map.end()) {
2427 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2428 section->set_beat (b_bbt);
2429 section->set_pulse (0.0);
2439 MetricSectionFrameSorter fcmp;
2440 imaginary.sort (fcmp);
2442 recompute_meters (imaginary);
2448 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2450 /* disallow setting section to an existing meter's bbt */
2451 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2453 if (!(*i)->is_tempo()) {
2454 m = static_cast<MeterSection*> (*i);
2455 if (m != section && m->bbt().bars == when.bars) {
2461 MeterSection* prev_m = 0;
2462 MeterSection* section_prev = 0;
2464 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2466 if (!(*i)->is_tempo()) {
2467 m = static_cast<MeterSection*> (*i);
2468 pair<double, BBT_Time> b_bbt;
2469 double new_pulse = 0.0;
2471 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2472 section_prev = prev_m;
2473 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2474 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2475 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2477 section->set_beat (b_bbt);
2478 section->set_pulse (pulse);
2479 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2484 if (m->position_lock_style() == AudioTime) {
2485 TempoSection* meter_locked_tempo = 0;
2487 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2489 if ((*ii)->is_tempo()) {
2490 t = static_cast<TempoSection*> (*ii);
2491 if ((t->locked_to_meter() || !t->movable()) && t->frame() == m->frame()) {
2492 meter_locked_tempo = t;
2498 if (!meter_locked_tempo) {
2503 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2505 if (beats + prev_m->beat() != m->beat()) {
2506 /* tempo/ meter change caused a change in beat (bar). */
2507 b_bbt = make_pair (beats + prev_m->beat()
2508 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2509 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2510 } else if (m->movable()) {
2511 b_bbt = make_pair (m->beat(), m->bbt());
2512 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2515 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2518 meter_locked_tempo->set_pulse (new_pulse);
2519 m->set_beat (b_bbt);
2520 m->set_pulse (new_pulse);
2524 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2525 if (beats + prev_m->beat() != m->beat()) {
2526 /* tempo/ meter change caused a change in beat (bar). */
2527 b_bbt = make_pair (beats + prev_m->beat()
2528 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2530 b_bbt = make_pair (beats + prev_m->beat()
2533 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2534 m->set_beat (b_bbt);
2535 m->set_pulse (new_pulse);
2536 m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2543 if (!section_prev) {
2545 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
2546 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2547 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
2549 section->set_beat (b_bbt);
2550 section->set_pulse (pulse);
2551 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2554 MetricSectionSorter cmp;
2555 imaginary.sort (cmp);
2557 recompute_meters (imaginary);
2562 /** places a copy of _metrics into copy and returns a pointer
2563 * to section's equivalent in copy.
2566 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
2568 TempoSection* ret = 0;
2570 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2573 if ((*i)->is_tempo()) {
2574 t = static_cast<TempoSection*> (*i);
2576 ret = new TempoSection (*t);
2577 copy.push_back (ret);
2581 TempoSection* cp = new TempoSection (*t);
2582 copy.push_back (cp);
2584 if (!(*i)->is_tempo()) {
2585 m = static_cast<MeterSection *> (*i);
2586 MeterSection* cp = new MeterSection (*m);
2587 copy.push_back (cp);
2595 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
2597 MeterSection* ret = 0;
2599 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2602 if ((*i)->is_tempo()) {
2603 t = static_cast<TempoSection*> (*i);
2604 TempoSection* cp = new TempoSection (*t);
2605 copy.push_back (cp);
2608 if (!(*i)->is_tempo()) {
2609 m = static_cast<MeterSection *> (*i);
2611 ret = new MeterSection (*m);
2612 copy.push_back (ret);
2615 MeterSection* cp = new MeterSection (*m);
2616 copy.push_back (cp);
2623 /** answers the question "is this a valid beat position for this tempo section?".
2624 * it returns true if the tempo section can be moved to the requested bbt position,
2625 * leaving the tempo map in a solved state.
2626 * @param section the tempo section to be moved
2627 * @param bbt the requested new position for the tempo section
2628 * @return true if the tempo section can be moved to the position, otherwise false.
2631 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2634 TempoSection* tempo_copy = 0;
2637 Glib::Threads::RWLock::ReaderLock lm (lock);
2638 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
2644 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
2646 Metrics::const_iterator d = copy.begin();
2647 while (d != copy.end()) {
2656 * 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,
2657 * taking any possible reordering as a consequence of this into account.
2658 * @param section - the section to be altered
2659 * @param bbt - the bbt where the altered tempo will fall
2660 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
2662 pair<double, framepos_t>
2663 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
2666 pair<double, framepos_t> ret = make_pair (0.0, 0);
2668 Glib::Threads::RWLock::ReaderLock lm (lock);
2670 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
2672 const double beat = beat_at_bbt_locked (future_map, bbt);
2674 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
2675 ret.first = tempo_copy->pulse();
2676 ret.second = tempo_copy->frame();
2678 ret.first = section->pulse();
2679 ret.second = section->frame();
2682 Metrics::const_iterator d = future_map.begin();
2683 while (d != future_map.end()) {
2691 TempoMap::gui_move_tempo (TempoSection* ts, const framepos_t& frame, const int& sub_num)
2694 bool was_musical = ts->position_lock_style() == MusicTime;
2696 if (sub_num == 0 && was_musical) {
2697 /* if we're not snapping to music,
2698 AudioTime and MusicTime may be treated identically.
2700 ts->set_position_lock_style (AudioTime);
2703 if (ts->position_lock_style() == MusicTime) {
2705 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
2706 Glib::Threads::RWLock::WriterLock lm (lock);
2707 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2708 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
2709 double pulse = pulse_at_beat_locked (future_map, beat);
2711 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
2712 solve_map_pulse (_metrics, ts, pulse);
2713 recompute_meters (_metrics);
2720 Glib::Threads::RWLock::WriterLock lm (lock);
2721 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2722 if (solve_map_frame (future_map, tempo_copy, frame)) {
2723 solve_map_frame (_metrics, ts, frame);
2724 recompute_meters (_metrics);
2729 if (sub_num == 0 && was_musical) {
2730 ts->set_position_lock_style (MusicTime);
2733 Metrics::const_iterator d = future_map.begin();
2734 while (d != future_map.end()) {
2739 MetricPositionChanged (); // Emit Signal
2743 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
2747 if (ms->position_lock_style() == AudioTime) {
2750 Glib::Threads::RWLock::WriterLock lm (lock);
2751 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2753 if (solve_map_frame (future_map, copy, frame)) {
2754 solve_map_frame (_metrics, ms, frame);
2755 recompute_tempos (_metrics);
2760 Glib::Threads::RWLock::WriterLock lm (lock);
2761 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2763 const double beat = beat_at_frame_locked (_metrics, frame);
2764 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
2766 if (solve_map_bbt (future_map, copy, bbt)) {
2767 solve_map_bbt (_metrics, ms, bbt);
2768 recompute_tempos (_metrics);
2773 Metrics::const_iterator d = future_map.begin();
2774 while (d != future_map.end()) {
2779 MetricPositionChanged (); // Emit Signal
2783 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2786 bool can_solve = false;
2788 Glib::Threads::RWLock::WriterLock lm (lock);
2789 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2790 tempo_copy->set_beats_per_minute (bpm.beats_per_minute());
2791 recompute_tempos (future_map);
2793 if (check_solved (future_map)) {
2794 ts->set_beats_per_minute (bpm.beats_per_minute());
2795 recompute_map (_metrics);
2800 Metrics::const_iterator d = future_map.begin();
2801 while (d != future_map.end()) {
2806 MetricPositionChanged (); // Emit Signal
2812 TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame, const double& pulse)
2815 Ts (future prev_t) Tnext
2818 |----------|----------
2825 Glib::Threads::RWLock::WriterLock lm (lock);
2831 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
2832 TempoSection* prev_to_prev_t = 0;
2833 const frameoffset_t fr_off = end_frame - frame;
2835 if (prev_t && prev_t->pulse() > 0.0) {
2836 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_frame_locked (future_map, prev_t->frame() - 1));
2839 TempoSection* next_t = 0;
2840 for (Metrics::iterator i = future_map.begin(); i != future_map.end(); ++i) {
2841 TempoSection* t = 0;
2842 if ((*i)->is_tempo()) {
2843 t = static_cast<TempoSection*> (*i);
2844 if (t->frame() > ts->frame()) {
2850 /* minimum allowed measurement distance in frames */
2851 const framepos_t min_dframe = 2;
2853 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
2854 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
2856 double contribution = 0.0;
2858 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2859 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
2862 const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
2864 const double start_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
2865 const double end_pulse = prev_t->pulse_at_frame (end_frame, _frame_rate);
2869 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
2871 if (prev_t->position_lock_style() == MusicTime) {
2872 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2873 if (frame > prev_to_prev_t->frame() + min_dframe && (frame + prev_t_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
2875 new_bpm = prev_t->beats_per_minute() * ((frame - prev_to_prev_t->frame())
2876 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
2878 new_bpm = prev_t->beats_per_minute();
2881 /* prev to prev is irrelevant */
2883 if (start_pulse > prev_t->pulse() && end_pulse > prev_t->pulse()) {
2884 new_bpm = prev_t->beats_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
2886 new_bpm = prev_t->beats_per_minute();
2891 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2892 if (frame > prev_to_prev_t->frame() + min_dframe && end_frame > prev_to_prev_t->frame() + min_dframe) {
2894 new_bpm = prev_t->beats_per_minute() * ((frame - prev_to_prev_t->frame())
2895 / (double) ((end_frame) - prev_to_prev_t->frame()));
2897 new_bpm = prev_t->beats_per_minute();
2900 /* prev_to_prev_t is irrelevant */
2902 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
2903 new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
2905 new_bpm = prev_t->beats_per_minute();
2911 double frame_ratio = 1.0;
2912 double pulse_ratio = 1.0;
2913 const framepos_t pulse_pos = prev_t->frame_at_pulse (pulse, _frame_rate);
2915 if (prev_to_prev_t) {
2916 if (pulse_pos > prev_to_prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_to_prev_t->frame() + min_dframe) {
2917 frame_ratio = (((pulse_pos - fr_off) - prev_to_prev_t->frame()) / (double) ((pulse_pos) - prev_to_prev_t->frame()));
2919 if (end_pulse > prev_to_prev_t->pulse() && start_pulse > prev_to_prev_t->pulse()) {
2920 pulse_ratio = ((start_pulse - prev_to_prev_t->pulse()) / (end_pulse - prev_to_prev_t->pulse()));
2923 if (pulse_pos > prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_t->frame() + min_dframe) {
2924 frame_ratio = (((pulse_pos - fr_off) - prev_t->frame()) / (double) ((pulse_pos) - prev_t->frame()));
2926 pulse_ratio = (start_pulse / end_pulse);
2928 new_bpm = prev_t->beats_per_minute() * (pulse_ratio * frame_ratio);
2931 /* don't clamp and proceed here.
2932 testing has revealed that this can go negative,
2933 which is an entirely different thing to just being too low.
2935 if (new_bpm < 0.5) {
2938 new_bpm = min (new_bpm, (double) 1000.0);
2939 prev_t->set_beats_per_minute (new_bpm);
2940 recompute_tempos (future_map);
2941 recompute_meters (future_map);
2943 if (check_solved (future_map)) {
2944 ts->set_beats_per_minute (new_bpm);
2945 recompute_tempos (_metrics);
2946 recompute_meters (_metrics);
2950 Metrics::const_iterator d = future_map.begin();
2951 while (d != future_map.end()) {
2956 MetricPositionChanged (); // Emit Signal
2960 TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t& sub_num)
2962 Glib::Threads::RWLock::ReaderLock lm (lock);
2964 return exact_beat_at_frame_locked (_metrics, frame, sub_num);
2968 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t& sub_num)
2970 double beat = beat_at_frame_locked (metrics, frame);
2972 beat = floor (beat) + (floor (((beat - floor (beat)) * (double) sub_num) + 0.5) / sub_num);
2973 } else if (sub_num == 1) {
2975 beat = floor (beat + 0.5);
2976 } else if (sub_num == -1) {
2978 Timecode::BBT_Time bbt = bbt_at_beat_locked (metrics, beat);
2981 beat = beat_at_bbt_locked (metrics, bbt);
2987 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2989 Glib::Threads::RWLock::ReaderLock lm (lock);
2991 const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2992 const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2993 const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2995 return frame_at_beat_locked (_metrics, total_beats);
2999 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
3001 return round_to_type (fr, dir, Bar);
3005 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
3007 return round_to_type (fr, dir, Beat);
3011 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3013 Glib::Threads::RWLock::ReaderLock lm (lock);
3014 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
3015 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3016 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3018 ticks -= beats * BBT_Time::ticks_per_beat;
3021 /* round to next (or same iff dir == RoundUpMaybe) */
3023 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3025 if (mod == 0 && dir == RoundUpMaybe) {
3026 /* right on the subdivision, which is fine, so do nothing */
3028 } else if (mod == 0) {
3029 /* right on the subdivision, so the difference is just the subdivision ticks */
3030 ticks += ticks_one_subdivisions_worth;
3033 /* not on subdivision, compute distance to next subdivision */
3035 ticks += ticks_one_subdivisions_worth - mod;
3038 if (ticks >= BBT_Time::ticks_per_beat) {
3039 ticks -= BBT_Time::ticks_per_beat;
3041 } else if (dir < 0) {
3043 /* round to previous (or same iff dir == RoundDownMaybe) */
3045 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3047 if (difference == 0 && dir == RoundDownAlways) {
3048 /* right on the subdivision, but force-rounding down,
3049 so the difference is just the subdivision ticks */
3050 difference = ticks_one_subdivisions_worth;
3053 if (ticks < difference) {
3054 ticks = BBT_Time::ticks_per_beat - ticks;
3056 ticks -= difference;
3060 /* round to nearest */
3063 /* compute the distance to the previous and next subdivision */
3065 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3067 /* closer to the next subdivision, so shift forward */
3069 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3071 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3073 if (ticks > BBT_Time::ticks_per_beat) {
3075 ticks -= BBT_Time::ticks_per_beat;
3076 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3079 } else if (rem > 0) {
3081 /* closer to previous subdivision, so shift backward */
3085 /* can't go backwards past zero, so ... */
3088 /* step back to previous beat */
3090 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3091 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3093 ticks = lrint (ticks - rem);
3094 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3097 /* on the subdivision, do nothing */
3101 const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
3107 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
3109 Glib::Threads::RWLock::ReaderLock lm (lock);
3111 const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
3112 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
3117 /* find bar previous to 'frame' */
3120 return frame_at_bbt_locked (_metrics, bbt);
3122 } else if (dir > 0) {
3123 /* find bar following 'frame' */
3127 return frame_at_bbt_locked (_metrics, bbt);
3129 /* true rounding: find nearest bar */
3130 framepos_t raw_ft = frame_at_bbt_locked (_metrics, bbt);
3133 framepos_t prev_ft = frame_at_bbt_locked (_metrics, bbt);
3135 framepos_t next_ft = frame_at_bbt_locked (_metrics, bbt);
3137 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
3148 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
3149 } else if (dir > 0) {
3150 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
3152 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
3161 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
3162 framepos_t lower, framepos_t upper)
3164 Glib::Threads::RWLock::ReaderLock lm (lock);
3165 int32_t cnt = ceil (beat_at_frame_locked (_metrics, lower));
3167 /* although the map handles negative beats, bbt doesn't. */
3172 if (frame_at_beat_locked (_metrics, cnt) >= upper) {
3176 while (pos < upper) {
3177 pos = frame_at_beat_locked (_metrics, cnt);
3178 const TempoSection tempo = tempo_section_at_frame_locked (_metrics, pos);
3179 const MeterSection meter = meter_section_at_frame_locked (_metrics, pos);
3180 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
3181 points.push_back (BBTPoint (meter, tempo_at_frame_locked (_metrics, pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
3187 TempoMap::tempo_section_at_frame (framepos_t frame) const
3189 Glib::Threads::RWLock::ReaderLock lm (lock);
3190 return tempo_section_at_frame_locked (_metrics, frame);
3194 TempoMap::tempo_section_at_frame_locked (const Metrics& metrics, framepos_t frame) const
3196 TempoSection* prev = 0;
3200 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3202 if ((*i)->is_tempo()) {
3203 t = static_cast<TempoSection*> (*i);
3207 if (prev && t->frame() > frame) {
3217 abort(); /*NOTREACHED*/
3224 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3226 TempoSection* prev_t = 0;
3227 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
3231 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3232 if ((*i)->is_tempo()) {
3233 t = static_cast<TempoSection*> (*i);
3234 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
3244 /* don't use this to calculate length (the tempo is only correct for this frame).
3245 do that stuff based on the beat_at_frame and frame_at_beat api
3248 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
3250 Glib::Threads::RWLock::ReaderLock lm (lock);
3252 const TempoSection* ts_at = 0;
3253 const TempoSection* ts_after = 0;
3254 Metrics::const_iterator i;
3257 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3259 if ((*i)->is_tempo()) {
3260 t = static_cast<TempoSection*> (*i);
3264 if (ts_at && (*i)->frame() > frame) {
3273 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate) * ts_at->note_type());
3275 /* must be treated as constant tempo */
3276 return ts_at->frames_per_beat (_frame_rate);
3280 TempoMap::meter_section_at_frame_locked (const Metrics& metrics, framepos_t frame) const
3282 Metrics::const_iterator i;
3283 MeterSection* prev = 0;
3287 for (i = metrics.begin(); i != metrics.end(); ++i) {
3289 if (!(*i)->is_tempo()) {
3290 m = static_cast<MeterSection*> (*i);
3292 if (prev && (*i)->frame() > frame) {
3302 abort(); /*NOTREACHED*/
3310 TempoMap::meter_section_at_frame (framepos_t frame) const
3312 Glib::Threads::RWLock::ReaderLock lm (lock);
3313 return meter_section_at_frame_locked (_metrics, frame);
3317 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3319 MeterSection* prev_m = 0;
3321 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3323 if (!(*i)->is_tempo()) {
3324 m = static_cast<MeterSection*> (*i);
3325 if (prev_m && m->beat() > beat) {
3336 TempoMap::meter_section_at_beat (double beat) const
3338 Glib::Threads::RWLock::ReaderLock lm (lock);
3339 return meter_section_at_beat_locked (_metrics, beat);
3343 TempoMap::meter_at_frame (framepos_t frame) const
3345 TempoMetric m (metric_at (frame));
3350 TempoMap::fix_legacy_session ()
3352 MeterSection* prev_m = 0;
3353 TempoSection* prev_t = 0;
3355 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3359 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3360 if (!m->movable()) {
3361 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3365 m->set_position_lock_style (AudioTime);
3370 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
3371 + (m->bbt().beats - 1)
3372 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
3374 m->set_beat (start);
3375 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
3376 + (m->bbt().beats - 1)
3377 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
3378 m->set_pulse (start_beat / prev_m->note_divisor());
3381 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3387 if (!t->movable()) {
3390 t->set_position_lock_style (AudioTime);
3396 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
3397 + (t->legacy_bbt().beats - 1)
3398 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
3400 t->set_pulse (beat / prev_m->note_divisor());
3402 /* really shouldn't happen but.. */
3403 t->set_pulse (beat / 4.0);
3412 TempoMap::get_state ()
3414 Metrics::const_iterator i;
3415 XMLNode *root = new XMLNode ("TempoMap");
3418 Glib::Threads::RWLock::ReaderLock lm (lock);
3419 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3420 root->add_child_nocopy ((*i)->get_state());
3428 TempoMap::set_state (const XMLNode& node, int /*version*/)
3431 Glib::Threads::RWLock::WriterLock lm (lock);
3434 XMLNodeConstIterator niter;
3435 Metrics old_metrics (_metrics);
3438 nlist = node.children();
3440 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
3441 XMLNode* child = *niter;
3443 if (child->name() == TempoSection::xml_state_node_name) {
3446 TempoSection* ts = new TempoSection (*child);
3447 _metrics.push_back (ts);
3450 catch (failed_constructor& err){
3451 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3452 _metrics = old_metrics;
3456 } else if (child->name() == MeterSection::xml_state_node_name) {
3459 MeterSection* ms = new MeterSection (*child);
3460 _metrics.push_back (ms);
3463 catch (failed_constructor& err) {
3464 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3465 _metrics = old_metrics;
3471 if (niter == nlist.end()) {
3472 MetricSectionSorter cmp;
3473 _metrics.sort (cmp);
3476 /* check for legacy sessions where bbt was the base musical unit for tempo */
3477 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3479 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3480 if (t->legacy_bbt().bars != 0) {
3481 fix_legacy_session();
3488 /* check for multiple tempo/meters at the same location, which
3489 ardour2 somehow allowed.
3492 Metrics::iterator prev = _metrics.end();
3493 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3494 if (prev != _metrics.end()) {
3496 MeterSection* prev_m;
3498 TempoSection* prev_t;
3499 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
3500 if (prev_m->pulse() == ms->pulse()) {
3501 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3502 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3505 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
3506 if (prev_t->pulse() == ts->pulse()) {
3507 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3508 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3516 recompute_map (_metrics);
3519 PropertyChanged (PropertyChange ());
3525 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
3527 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
3528 const MeterSection* m;
3529 const TempoSection* t;
3530 const TempoSection* prev_t = 0;
3532 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3534 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
3535 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
3536 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
3537 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
3539 o << "previous : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
3540 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;
3543 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
3544 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
3545 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
3548 o << "------" << std::endl;
3552 TempoMap::n_tempos() const
3554 Glib::Threads::RWLock::ReaderLock lm (lock);
3557 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3558 if ((*i)->is_tempo()) {
3567 TempoMap::n_meters() const
3569 Glib::Threads::RWLock::ReaderLock lm (lock);
3572 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3573 if (!(*i)->is_tempo()) {
3582 TempoMap::insert_time (framepos_t where, framecnt_t amount)
3585 Glib::Threads::RWLock::WriterLock lm (lock);
3586 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3587 if ((*i)->frame() >= where && (*i)->movable ()) {
3588 (*i)->set_frame ((*i)->frame() + amount);
3592 /* now reset the BBT time of all metrics, based on their new
3593 * audio time. This is the only place where we do this reverse
3597 Metrics::iterator i;
3598 const MeterSection* meter;
3599 const TempoSection* tempo;
3603 meter = &first_meter ();
3604 tempo = &first_tempo ();
3609 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
3612 MetricSection* prev = 0;
3614 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3617 //TempoMetric metric (*meter, *tempo);
3618 MeterSection* ms = const_cast<MeterSection*>(meter);
3619 TempoSection* ts = const_cast<TempoSection*>(tempo);
3622 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3626 ts->set_pulse (t->pulse());
3628 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3629 ts->set_pulse (m->pulse());
3631 ts->set_frame (prev->frame());
3635 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3636 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3637 ms->set_beat (start);
3638 ms->set_pulse (m->pulse());
3640 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3644 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3645 pair<double, BBT_Time> start = make_pair (beat, bbt_at_beat_locked (_metrics, beat));
3646 ms->set_beat (start);
3647 ms->set_pulse (t->pulse());
3649 ms->set_frame (prev->frame());
3653 // metric will be at frames=0 bbt=1|1|0 by default
3654 // which is correct for our purpose
3657 // cerr << bbt << endl;
3659 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3663 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3665 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3666 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3667 bbt = bbt_at_frame_locked (_metrics, m->frame());
3669 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3675 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3676 /* round up to next beat */
3682 if (bbt.beats != 1) {
3683 /* round up to next bar */
3688 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3689 m->set_beat (start);
3690 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3692 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3694 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3695 abort(); /*NOTREACHED*/
3701 recompute_map (_metrics);
3705 PropertyChanged (PropertyChange ());
3708 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3712 std::list<MetricSection*> metric_kill_list;
3714 TempoSection* last_tempo = NULL;
3715 MeterSection* last_meter = NULL;
3716 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3717 bool meter_after = false; // is there a meter marker likewise?
3719 Glib::Threads::RWLock::WriterLock lm (lock);
3720 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3721 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3722 metric_kill_list.push_back(*i);
3723 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3726 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3730 else if ((*i)->frame() >= where) {
3731 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3732 (*i)->set_frame ((*i)->frame() - amount);
3733 if ((*i)->frame() == where) {
3734 // marker was immediately after end of range
3735 tempo_after = dynamic_cast<TempoSection*> (*i);
3736 meter_after = dynamic_cast<MeterSection*> (*i);
3742 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3743 if (last_tempo && !tempo_after) {
3744 metric_kill_list.remove(last_tempo);
3745 last_tempo->set_frame(where);
3748 if (last_meter && !meter_after) {
3749 metric_kill_list.remove(last_meter);
3750 last_meter->set_frame(where);
3754 //remove all the remaining metrics
3755 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3756 _metrics.remove(*i);
3761 recompute_map (_metrics);
3764 PropertyChanged (PropertyChange ());
3768 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3769 * pos can be -ve, if required.
3772 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3774 Glib::Threads::RWLock::ReaderLock lm (lock);
3776 return frame_at_beat_locked (_metrics, beat_at_frame_locked (_metrics, pos) + beats.to_double());
3779 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3781 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3783 Glib::Threads::RWLock::ReaderLock lm (lock);
3785 return frame_at_beat_locked (_metrics, beat_at_frame_locked (_metrics, pos) - beats.to_double());
3788 /** Add the BBT interval op to pos and return the result */
3790 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3792 Glib::Threads::RWLock::ReaderLock lm (lock);
3794 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3795 pos_bbt.ticks += op.ticks;
3796 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3798 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3800 pos_bbt.beats += op.beats;
3801 /* the meter in effect will start on the bar */
3802 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();
3803 while (pos_bbt.beats >= divisions_per_bar + 1) {
3805 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3806 pos_bbt.beats -= divisions_per_bar;
3808 pos_bbt.bars += op.bars;
3810 return frame_at_bbt_locked (_metrics, pos_bbt);
3813 /** Count the number of beats that are equivalent to distance when going forward,
3817 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3819 Glib::Threads::RWLock::ReaderLock lm (lock);
3821 return Evoral::Beats (beat_at_frame_locked (_metrics, pos + distance) - beat_at_frame_locked (_metrics, pos));
3825 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3831 operator<< (std::ostream& o, const Meter& m) {
3832 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3836 operator<< (std::ostream& o, const Tempo& t) {
3837 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3841 operator<< (std::ostream& o, const MetricSection& section) {
3843 o << "MetricSection @ " << section.frame() << ' ';
3845 const TempoSection* ts;
3846 const MeterSection* ms;
3848 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3849 o << *((const Tempo*) ts);
3850 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3851 o << *((const Meter*) ms);