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 solve_map_frame (_metrics, t, t->frame());
990 solve_map_pulse (_metrics, t, t->pulse());
992 recompute_meters (_metrics);
999 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, const framepos_t& frame, PositionLockStyle pls)
1001 MeterSection* m = 0;
1003 Glib::Threads::RWLock::WriterLock lm (lock);
1004 m = add_meter_locked (meter, beat, where, frame, pls, true);
1009 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1010 dump (_metrics, std::cerr);
1014 PropertyChanged (PropertyChange ());
1019 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, const framepos_t& frame, PositionLockStyle pls)
1022 Glib::Threads::RWLock::WriterLock lm (lock);
1023 const double beat = beat_at_bbt_locked (_metrics, where);
1026 remove_meter_locked (ms);
1027 add_meter_locked (meter, beat, where, frame, pls, true);
1029 MeterSection& first (first_meter());
1030 TempoSection& first_t (first_tempo());
1031 /* cannot move the first meter section */
1032 *static_cast<Meter*>(&first) = meter;
1033 first.set_position_lock_style (AudioTime);
1034 first.set_pulse (0.0);
1035 first.set_frame (frame);
1036 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1037 first.set_beat (beat);
1038 first_t.set_frame (first.frame());
1039 first_t.set_pulse (0.0);
1040 first_t.set_position_lock_style (AudioTime);
1041 recompute_map (_metrics);
1045 PropertyChanged (PropertyChange ());
1049 TempoMap::add_meter_locked (const Meter& meter, double beat, const Timecode::BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute)
1051 const MeterSection& prev_m = meter_section_at_frame_locked (_metrics, frame - 1);
1052 const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1054 if (pls == AudioTime) {
1055 /* add meter-locked tempo */
1056 add_tempo_locked (tempo_at_frame_locked (_metrics, frame), pulse, frame, TempoSection::Ramp, AudioTime, true, true);
1059 MeterSection* new_meter = new MeterSection (pulse, frame, beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls);
1061 do_insert (new_meter);
1065 if (pls == AudioTime) {
1066 solve_map_frame (_metrics, new_meter, frame);
1068 solve_map_bbt (_metrics, new_meter, where);
1076 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1078 Tempo newtempo (beats_per_minute, note_type);
1081 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1082 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1087 Glib::Threads::RWLock::WriterLock lm (lock);
1088 *((Tempo*) t) = newtempo;
1089 recompute_map (_metrics);
1091 PropertyChanged (PropertyChange ());
1098 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1100 Tempo newtempo (beats_per_minute, note_type);
1103 TempoSection* first;
1104 Metrics::iterator i;
1106 /* find the TempoSection immediately preceding "where"
1109 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1111 if ((*i)->frame() > where) {
1117 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1130 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1140 Glib::Threads::RWLock::WriterLock lm (lock);
1141 /* cannot move the first tempo section */
1142 *((Tempo*)prev) = newtempo;
1143 recompute_map (_metrics);
1146 PropertyChanged (PropertyChange ());
1150 TempoMap::first_meter () const
1152 const MeterSection *m = 0;
1154 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1155 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1160 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1161 abort(); /*NOTREACHED*/
1166 TempoMap::first_meter ()
1168 MeterSection *m = 0;
1170 /* CALLER MUST HOLD LOCK */
1172 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1173 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1178 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1179 abort(); /*NOTREACHED*/
1184 TempoMap::first_tempo () const
1186 const TempoSection *t = 0;
1188 /* CALLER MUST HOLD LOCK */
1190 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1191 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1195 if (!t->movable()) {
1201 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1202 abort(); /*NOTREACHED*/
1207 TempoMap::first_tempo ()
1209 TempoSection *t = 0;
1211 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1212 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1216 if (!t->movable()) {
1222 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1223 abort(); /*NOTREACHED*/
1227 TempoMap::recompute_tempos (Metrics& metrics)
1229 TempoSection* prev_t = 0;
1231 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1234 if ((*i)->is_tempo()) {
1235 t = static_cast<TempoSection*> (*i);
1239 if (!t->movable()) {
1247 if (t->position_lock_style() == AudioTime) {
1248 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1249 if (!t->locked_to_meter()) {
1250 t->set_pulse (prev_t->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
1254 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1255 t->set_frame (prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
1262 prev_t->set_c_func (0.0);
1265 /* tempos must be positioned correctly.
1266 the current approach is to use a meter's bbt time as its base position unit.
1267 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1268 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1271 TempoMap::recompute_meters (Metrics& metrics)
1273 MeterSection* meter = 0;
1274 MeterSection* prev_m = 0;
1276 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1277 if (!(*mi)->is_tempo()) {
1278 meter = static_cast<MeterSection*> (*mi);
1279 if (meter->position_lock_style() == AudioTime) {
1281 pair<double, BBT_Time> b_bbt;
1282 TempoSection* meter_locked_tempo = 0;
1283 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1285 if ((*ii)->is_tempo()) {
1286 t = static_cast<TempoSection*> (*ii);
1287 if ((t->locked_to_meter() || !t->movable()) && t->frame() == meter->frame()) {
1288 meter_locked_tempo = t;
1295 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1296 if (beats + prev_m->beat() != meter->beat()) {
1297 /* reordering caused a bbt change */
1298 b_bbt = make_pair (beats + prev_m->beat()
1299 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1300 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1302 } else if (meter->movable()) {
1303 b_bbt = make_pair (meter->beat(), meter->bbt());
1304 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1307 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1309 if (meter_locked_tempo) {
1310 meter_locked_tempo->set_pulse (pulse);
1312 meter->set_beat (b_bbt);
1313 meter->set_pulse (pulse);
1318 pair<double, BBT_Time> b_bbt;
1320 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1321 if (beats + prev_m->beat() != meter->beat()) {
1322 /* reordering caused a bbt change */
1323 b_bbt = make_pair (beats + prev_m->beat()
1324 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1326 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1328 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1330 /* shouldn't happen - the first is audio-locked */
1331 pulse = pulse_at_beat_locked (metrics, meter->beat());
1332 b_bbt = make_pair (meter->beat(), meter->bbt());
1335 meter->set_beat (b_bbt);
1336 meter->set_pulse (pulse);
1337 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1346 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1348 /* CALLER MUST HOLD WRITE LOCK */
1352 /* we will actually stop once we hit
1359 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1362 /* silly call from Session::process() during startup
1367 recompute_tempos (metrics);
1368 recompute_meters (metrics);
1372 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1374 Glib::Threads::RWLock::ReaderLock lm (lock);
1375 TempoMetric m (first_meter(), first_tempo());
1377 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1378 at something, because we insert the default tempo and meter during
1379 TempoMap construction.
1381 now see if we can find better candidates.
1384 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1386 if ((*i)->frame() > frame) {
1400 /* XX meters only */
1402 TempoMap::metric_at (BBT_Time bbt) const
1404 Glib::Threads::RWLock::ReaderLock lm (lock);
1405 TempoMetric m (first_meter(), first_tempo());
1407 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1408 at something, because we insert the default tempo and meter during
1409 TempoMap construction.
1411 now see if we can find better candidates.
1414 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1416 if (!(*i)->is_tempo()) {
1417 mw = static_cast<MeterSection*> (*i);
1418 BBT_Time section_start (mw->bbt());
1420 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1432 TempoMap::beat_at_frame (const framecnt_t& frame) const
1434 Glib::Threads::RWLock::ReaderLock lm (lock);
1435 return beat_at_frame_locked (_metrics, frame);
1438 /* meter / tempo section based */
1440 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1442 const TempoSection& ts = tempo_section_at_frame_locked (metrics, frame);
1443 MeterSection* prev_m = 0;
1444 MeterSection* next_m = 0;
1446 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1447 if (!(*i)->is_tempo()) {
1448 if (prev_m && (*i)->frame() > frame) {
1449 next_m = static_cast<MeterSection*> (*i);
1452 prev_m = static_cast<MeterSection*> (*i);
1455 if (frame < prev_m->frame()) {
1458 const double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
1460 /* audio locked meters fake their beat */
1461 if (next_m && next_m->beat() < beat) {
1462 return next_m->beat();
1469 TempoMap::frame_at_beat (const double& beat) const
1471 Glib::Threads::RWLock::ReaderLock lm (lock);
1472 return frame_at_beat_locked (_metrics, beat);
1475 /* meter & tempo section based */
1477 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1479 MeterSection* prev_m = 0;
1480 TempoSection* prev_t = 0;
1484 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1485 if (!(*i)->is_tempo()) {
1486 m = static_cast<MeterSection*> (*i);
1487 if (prev_m && m->beat() > beat) {
1496 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1497 if ((*i)->is_tempo()) {
1498 t = static_cast<TempoSection*> (*i);
1499 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1507 return prev_t->frame_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse(), _frame_rate);
1511 TempoMap::tempo_at_frame (const framepos_t& frame) const
1513 Glib::Threads::RWLock::ReaderLock lm (lock);
1514 return tempo_at_frame_locked (_metrics, frame);
1518 TempoMap::tempo_at_frame_locked (const Metrics& metrics, const framepos_t& frame) const
1520 TempoSection* prev_t = 0;
1524 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1525 if ((*i)->is_tempo()) {
1526 t = static_cast<TempoSection*> (*i);
1530 if ((prev_t) && t->frame() > frame) {
1531 /* t is the section past frame */
1532 const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
1533 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
1540 const double ret = prev_t->beats_per_minute();
1541 const Tempo ret_tempo (ret, prev_t->note_type ());
1546 /** returns the frame at which the supplied tempo occurs, or
1547 * the frame of the last tempo section (search exhausted)
1548 * only the position of the first occurence will be returned
1552 TempoMap::frame_at_tempo (const Tempo& tempo) const
1554 Glib::Threads::RWLock::ReaderLock lm (lock);
1555 return frame_at_tempo_locked (_metrics, tempo);
1560 TempoMap::frame_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1562 TempoSection* prev_t = 0;
1563 const double tempo_ppm = tempo.beats_per_minute() / tempo.note_type();
1565 Metrics::const_iterator i;
1567 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1569 if ((*i)->is_tempo()) {
1570 t = static_cast<TempoSection*> (*i);
1576 const double t_ppm = t->beats_per_minute() / t->note_type();
1578 if (t_ppm == tempo_ppm) {
1583 const double prev_t_ppm = prev_t->beats_per_minute() / prev_t->note_type();
1585 if ((t_ppm > tempo_ppm && prev_t_ppm < tempo_ppm) || (t_ppm < tempo_ppm && prev_t_ppm > tempo_ppm)) {
1586 return prev_t->frame_at_tempo (tempo_ppm, prev_t->pulse(), _frame_rate);
1593 return prev_t->frame();
1596 /** more precise than doing tempo_at_frame (frame_at_beat (b)),
1597 * as there is no intermediate frame rounding.
1600 TempoMap::tempo_at_beat (const double& beat) const
1602 Glib::Threads::RWLock::ReaderLock lm (lock);
1603 const MeterSection* prev_m = &meter_section_at_beat_locked (_metrics, beat);
1604 const TempoSection* prev_t = &tempo_section_at_beat_locked (_metrics, beat);
1605 const double note_type = prev_t->note_type();
1607 return Tempo (prev_t->tempo_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse()) * note_type, note_type);
1611 TempoMap::pulse_at_beat (const double& beat) const
1613 Glib::Threads::RWLock::ReaderLock lm (lock);
1614 return pulse_at_beat_locked (_metrics, beat);
1618 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1620 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1622 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1626 TempoMap::beat_at_pulse (const double& pulse) const
1628 Glib::Threads::RWLock::ReaderLock lm (lock);
1629 return beat_at_pulse_locked (_metrics, pulse);
1633 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1635 MeterSection* prev_m = 0;
1637 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1639 if (!(*i)->is_tempo()) {
1640 m = static_cast<MeterSection*> (*i);
1641 if (prev_m && m->pulse() > pulse) {
1642 if (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > m->beat()) {
1650 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1655 TempoMap::pulse_at_frame (const framepos_t& frame) const
1657 Glib::Threads::RWLock::ReaderLock lm (lock);
1658 return pulse_at_frame_locked (_metrics, frame);
1661 /* tempo section based */
1663 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framepos_t& frame) const
1665 /* HOLD (at least) THE READER LOCK */
1666 TempoSection* prev_t = 0;
1668 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1670 if ((*i)->is_tempo()) {
1671 t = static_cast<TempoSection*> (*i);
1675 if (prev_t && t->frame() > frame) {
1676 /*the previous ts is the one containing the frame */
1677 const double ret = prev_t->pulse_at_frame (frame, _frame_rate);
1684 /* treated as constant for this ts */
1685 const double pulses_in_section = (frame - prev_t->frame()) / prev_t->frames_per_pulse (_frame_rate);
1687 return pulses_in_section + prev_t->pulse();
1691 TempoMap::frame_at_pulse (const double& pulse) const
1693 Glib::Threads::RWLock::ReaderLock lm (lock);
1694 return frame_at_pulse_locked (_metrics, pulse);
1697 /* tempo section based */
1699 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1701 /* HOLD THE READER LOCK */
1703 const TempoSection* prev_t = 0;
1705 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1708 if ((*i)->is_tempo()) {
1709 t = static_cast<TempoSection*> (*i);
1713 if (prev_t && t->pulse() > pulse) {
1714 return prev_t->frame_at_pulse (pulse, _frame_rate);
1720 /* must be treated as constant, irrespective of _type */
1721 double const dtime = (pulse - prev_t->pulse()) * prev_t->frames_per_pulse (_frame_rate);
1723 return (framecnt_t) floor (dtime) + prev_t->frame();
1727 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
1729 Glib::Threads::RWLock::ReaderLock lm (lock);
1730 return beat_at_bbt_locked (_metrics, bbt);
1735 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1737 /* CALLER HOLDS READ LOCK */
1739 MeterSection* prev_m = 0;
1741 /* because audio-locked meters have 'fake' integral beats,
1742 there is no pulse offset here.
1746 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1747 if (!(*i)->is_tempo()) {
1748 m = static_cast<MeterSection*> (*i);
1750 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1751 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1759 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1760 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1761 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1767 TempoMap::bbt_at_beat (const double& beats)
1769 Glib::Threads::RWLock::ReaderLock lm (lock);
1770 return bbt_at_beat_locked (_metrics, beats);
1774 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
1776 /* CALLER HOLDS READ LOCK */
1777 MeterSection* prev_m = 0;
1778 const double beats = max (0.0, b);
1780 MeterSection* m = 0;
1782 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1783 if (!(*i)->is_tempo()) {
1784 m = static_cast<MeterSection*> (*i);
1786 if (m->beat() > beats) {
1787 /* this is the meter after the one our beat is on*/
1796 const double beats_in_ms = beats - prev_m->beat();
1797 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1798 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1799 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1800 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1804 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1805 ret.beats = (uint32_t) floor (remaining_beats);
1806 ret.bars = total_bars;
1808 /* 0 0 0 to 1 1 0 - based mapping*/
1812 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1814 ret.ticks -= BBT_Time::ticks_per_beat;
1817 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1826 TempoMap::pulse_at_bbt (const Timecode::BBT_Time& bbt)
1828 Glib::Threads::RWLock::ReaderLock lm (lock);
1830 return pulse_at_bbt_locked (_metrics, bbt);
1834 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1836 /* CALLER HOLDS READ LOCK */
1838 MeterSection* prev_m = 0;
1840 /* because audio-locked meters have 'fake' integral beats,
1841 there is no pulse offset here.
1845 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1846 if (!(*i)->is_tempo()) {
1847 m = static_cast<MeterSection*> (*i);
1849 if (m->bbt().bars > bbt.bars) {
1857 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1858 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
1859 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
1865 TempoMap::bbt_at_pulse (const double& pulse)
1867 Glib::Threads::RWLock::ReaderLock lm (lock);
1869 return bbt_at_pulse_locked (_metrics, pulse);
1873 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1875 MeterSection* prev_m = 0;
1877 MeterSection* m = 0;
1879 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1881 if (!(*i)->is_tempo()) {
1882 m = static_cast<MeterSection*> (*i);
1885 double const pulses_to_m = m->pulse() - prev_m->pulse();
1886 if (prev_m->pulse() + pulses_to_m > pulse) {
1887 /* this is the meter after the one our beat is on*/
1896 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1897 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1898 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1899 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1900 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1904 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1905 ret.beats = (uint32_t) floor (remaining_beats);
1906 ret.bars = total_bars;
1908 /* 0 0 0 to 1 1 0 mapping*/
1912 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1914 ret.ticks -= BBT_Time::ticks_per_beat;
1917 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1926 TempoMap::bbt_at_frame (framepos_t frame)
1933 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1936 Glib::Threads::RWLock::ReaderLock lm (lock);
1938 return bbt_at_frame_locked (_metrics, frame);
1942 TempoMap::bbt_at_frame_rt (framepos_t frame)
1944 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
1947 throw std::logic_error ("TempoMap::bbt_time_rt() could not lock tempo map");
1950 return bbt_at_frame_locked (_metrics, frame);
1954 TempoMap::bbt_at_frame_locked (const Metrics& metrics, const framepos_t& frame) const
1961 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1965 const TempoSection& ts = tempo_section_at_frame_locked (metrics, frame);
1966 MeterSection* prev_m = 0;
1967 MeterSection* next_m = 0;
1971 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1972 if (!(*i)->is_tempo()) {
1973 m = static_cast<MeterSection*> (*i);
1974 if (prev_m && m->frame() > frame) {
1982 double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
1984 /* handle frame before first meter */
1985 if (frame < prev_m->frame()) {
1988 /* audio locked meters fake their beat */
1989 if (next_m && next_m->beat() < beat) {
1990 beat = next_m->beat();
1993 beat = max (0.0, beat);
1995 const double beats_in_ms = beat - prev_m->beat();
1996 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1997 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1998 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1999 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2003 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2004 ret.beats = (uint32_t) floor (remaining_beats);
2005 ret.bars = total_bars;
2007 /* 0 0 0 to 1 1 0 - based mapping*/
2011 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2013 ret.ticks -= BBT_Time::ticks_per_beat;
2016 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2025 TempoMap::frame_at_bbt (const BBT_Time& bbt)
2028 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
2032 if (bbt.beats < 1) {
2033 throw std::logic_error ("beats are counted from one");
2035 Glib::Threads::RWLock::ReaderLock lm (lock);
2037 return frame_at_bbt_locked (_metrics, bbt);
2040 /* meter & tempo section based */
2042 TempoMap::frame_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2044 /* HOLD THE READER LOCK */
2046 const framepos_t ret = frame_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2051 TempoMap::check_solved (const Metrics& metrics) const
2053 TempoSection* prev_t = 0;
2054 MeterSection* prev_m = 0;
2056 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2059 if ((*i)->is_tempo()) {
2060 t = static_cast<TempoSection*> (*i);
2065 /* check ordering */
2066 if ((t->frame() <= prev_t->frame()) || (t->pulse() <= prev_t->pulse())) {
2070 /* precision check ensures tempo and frames align.*/
2071 if (t->frame() != prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate)) {
2072 if (!t->locked_to_meter()) {
2077 /* gradient limit - who knows what it should be?
2078 things are also ok (if a little chaotic) without this
2080 if (fabs (prev_t->c_func()) > 1000.0) {
2081 //std::cout << "c : " << prev_t->c_func() << std::endl;
2088 if (!(*i)->is_tempo()) {
2089 m = static_cast<MeterSection*> (*i);
2090 if (prev_m && m->position_lock_style() == AudioTime) {
2091 const TempoSection* t = &tempo_section_at_frame_locked (metrics, m->frame() - 1);
2092 const double nascent_m_pulse = ((m->beat() - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse();
2093 const framepos_t nascent_m_frame = t->frame_at_pulse (nascent_m_pulse, _frame_rate);
2095 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
2109 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
2111 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2113 if ((*i)->is_tempo()) {
2114 t = static_cast<TempoSection*> (*i);
2115 if (!t->movable()) {
2116 t->set_active (true);
2119 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
2120 t->set_active (false);
2122 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
2123 t->set_active (true);
2124 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
2133 TempoMap::solve_map_frame (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
2135 TempoSection* prev_t = 0;
2136 TempoSection* section_prev = 0;
2137 framepos_t first_m_frame = 0;
2139 /* can't move a tempo before the first meter */
2140 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2142 if (!(*i)->is_tempo()) {
2143 m = static_cast<MeterSection*> (*i);
2144 if (!m->movable()) {
2145 first_m_frame = m->frame();
2150 if (section->movable() && frame <= first_m_frame) {
2154 section->set_active (true);
2155 section->set_frame (frame);
2157 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2159 if ((*i)->is_tempo()) {
2160 t = static_cast<TempoSection*> (*i);
2167 section_prev = prev_t;
2168 if (t->locked_to_meter()) {
2173 if (t->position_lock_style() == MusicTime) {
2174 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
2175 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
2177 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
2178 if (!t->locked_to_meter()) {
2179 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
2188 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
2189 if (!section->locked_to_meter()) {
2190 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
2195 recompute_tempos (imaginary);
2197 if (check_solved (imaginary)) {
2200 dunp (imaginary, std::cout);
2204 MetricSectionFrameSorter fcmp;
2205 imaginary.sort (fcmp);
2207 recompute_tempos (imaginary);
2209 if (check_solved (imaginary)) {
2217 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2219 TempoSection* prev_t = 0;
2220 TempoSection* section_prev = 0;
2222 section->set_pulse (pulse);
2224 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2226 if ((*i)->is_tempo()) {
2227 t = static_cast<TempoSection*> (*i);
2231 if (!t->movable()) {
2238 section_prev = prev_t;
2241 if (t->position_lock_style() == MusicTime) {
2242 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
2243 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
2245 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
2246 if (!t->locked_to_meter()) {
2247 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
2256 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
2257 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
2261 recompute_tempos (imaginary);
2263 if (check_solved (imaginary)) {
2266 dunp (imaginary, std::cout);
2270 MetricSectionSorter cmp;
2271 imaginary.sort (cmp);
2273 recompute_tempos (imaginary);
2275 * XX need a restriction here, but only for this case,
2276 * as audio locked tempos don't interact in the same way.
2278 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2280 * |50 bpm |250 bpm |60 bpm
2281 * drag 250 to the pulse after 60->
2282 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2284 if (check_solved (imaginary)) {
2292 TempoMap::solve_map_frame (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
2294 /* disallow moving first meter past any subsequent one, and any movable meter before the first one */
2295 const MeterSection* other = &meter_section_at_frame_locked (imaginary, frame);
2296 if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->frame() >= frame)) {
2300 if (!section->movable()) {
2301 /* lock the first tempo to our first meter */
2302 if (!set_active_tempos (imaginary, frame)) {
2307 TempoSection* meter_locked_tempo = 0;
2309 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2311 if ((*ii)->is_tempo()) {
2312 t = static_cast<TempoSection*> (*ii);
2313 if ((t->locked_to_meter() || !t->movable()) && t->frame() == section->frame()) {
2314 meter_locked_tempo = t;
2320 if (!meter_locked_tempo) {
2324 MeterSection* prev_m = 0;
2326 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2327 bool solved = false;
2329 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2331 if (!(*i)->is_tempo()) {
2332 m = static_cast<MeterSection*> (*i);
2334 if (prev_m && section->movable()) {
2335 const double beats = (pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor();
2336 if (beats + prev_m->beat() < section->beat()) {
2337 /* set the frame/pulse corresponding to its musical position,
2338 * as an earlier time than this has been requested.
2340 const double new_pulse = ((section->beat() - prev_m->beat())
2341 / prev_m->note_divisor()) + prev_m->pulse();
2343 const framepos_t smallest_frame = frame_at_pulse_locked (future_map, new_pulse);
2345 if ((solved = solve_map_frame (future_map, tempo_copy, smallest_frame))) {
2346 meter_locked_tempo->set_pulse (new_pulse);
2347 solve_map_frame (imaginary, meter_locked_tempo, smallest_frame);
2348 section->set_frame (smallest_frame);
2349 section->set_pulse (new_pulse);
2354 Metrics::const_iterator d = future_map.begin();
2355 while (d != future_map.end()) {
2364 /* all is ok. set section's locked tempo if allowed.
2365 possibly disallowed if there is an adjacent audio-locked tempo.
2366 XX this check could possibly go. its never actually happened here.
2368 MeterSection* meter_copy = const_cast<MeterSection*> (&meter_section_at_frame_locked (future_map, section->frame()));
2369 meter_copy->set_frame (frame);
2371 if ((solved = solve_map_frame (future_map, tempo_copy, frame))) {
2372 section->set_frame (frame);
2373 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2374 / prev_m->note_divisor()) + prev_m->pulse());
2375 solve_map_frame (imaginary, meter_locked_tempo, frame);
2380 Metrics::const_iterator d = future_map.begin();
2381 while (d != future_map.end()) {
2391 /* not movable (first meter atm) */
2393 tempo_copy->set_frame (frame);
2394 tempo_copy->set_pulse (0.0);
2396 if ((solved = solve_map_frame (future_map, tempo_copy, frame))) {
2397 section->set_frame (frame);
2398 meter_locked_tempo->set_frame (frame);
2399 meter_locked_tempo->set_pulse (0.0);
2400 solve_map_frame (imaginary, meter_locked_tempo, frame);
2405 Metrics::const_iterator d = future_map.begin();
2406 while (d != future_map.end()) {
2415 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2416 section->set_beat (b_bbt);
2417 section->set_pulse (0.0);
2427 MetricSectionFrameSorter fcmp;
2428 imaginary.sort (fcmp);
2430 recompute_meters (imaginary);
2436 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2438 /* disallow setting section to an existing meter's bbt */
2439 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2441 if (!(*i)->is_tempo()) {
2442 m = static_cast<MeterSection*> (*i);
2443 if (m != section && m->bbt().bars == when.bars) {
2449 MeterSection* prev_m = 0;
2450 MeterSection* section_prev = 0;
2452 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2454 if (!(*i)->is_tempo()) {
2455 m = static_cast<MeterSection*> (*i);
2456 pair<double, BBT_Time> b_bbt;
2457 double new_pulse = 0.0;
2459 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2460 section_prev = prev_m;
2461 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2462 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2463 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2465 section->set_beat (b_bbt);
2466 section->set_pulse (pulse);
2467 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2472 if (m->position_lock_style() == AudioTime) {
2473 TempoSection* meter_locked_tempo = 0;
2475 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2477 if ((*ii)->is_tempo()) {
2478 t = static_cast<TempoSection*> (*ii);
2479 if ((t->locked_to_meter() || !t->movable()) && t->frame() == m->frame()) {
2480 meter_locked_tempo = t;
2486 if (!meter_locked_tempo) {
2491 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2493 if (beats + prev_m->beat() != m->beat()) {
2494 /* tempo/ meter change caused a change in beat (bar). */
2495 b_bbt = make_pair (beats + prev_m->beat()
2496 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2497 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2498 } else if (m->movable()) {
2499 b_bbt = make_pair (m->beat(), m->bbt());
2500 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2503 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2506 meter_locked_tempo->set_pulse (new_pulse);
2507 m->set_beat (b_bbt);
2508 m->set_pulse (new_pulse);
2512 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2513 if (beats + prev_m->beat() != m->beat()) {
2514 /* tempo/ meter change caused a change in beat (bar). */
2515 b_bbt = make_pair (beats + prev_m->beat()
2516 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2518 b_bbt = make_pair (beats + prev_m->beat()
2521 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2522 m->set_beat (b_bbt);
2523 m->set_pulse (new_pulse);
2524 m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2531 if (!section_prev) {
2533 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
2534 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2535 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
2537 section->set_beat (b_bbt);
2538 section->set_pulse (pulse);
2539 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2542 MetricSectionSorter cmp;
2543 imaginary.sort (cmp);
2545 recompute_meters (imaginary);
2550 /** places a copy of _metrics into copy and returns a pointer
2551 * to section's equivalent in copy.
2554 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
2556 TempoSection* ret = 0;
2558 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2561 if ((*i)->is_tempo()) {
2562 t = static_cast<TempoSection*> (*i);
2564 ret = new TempoSection (*t);
2565 copy.push_back (ret);
2569 TempoSection* cp = new TempoSection (*t);
2570 copy.push_back (cp);
2572 if (!(*i)->is_tempo()) {
2573 m = static_cast<MeterSection *> (*i);
2574 MeterSection* cp = new MeterSection (*m);
2575 copy.push_back (cp);
2583 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
2585 MeterSection* ret = 0;
2587 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2590 if ((*i)->is_tempo()) {
2591 t = static_cast<TempoSection*> (*i);
2592 TempoSection* cp = new TempoSection (*t);
2593 copy.push_back (cp);
2596 if (!(*i)->is_tempo()) {
2597 m = static_cast<MeterSection *> (*i);
2599 ret = new MeterSection (*m);
2600 copy.push_back (ret);
2603 MeterSection* cp = new MeterSection (*m);
2604 copy.push_back (cp);
2611 /** answers the question "is this a valid beat position for this tempo section?".
2612 * it returns true if the tempo section can be moved to the requested bbt position,
2613 * leaving the tempo map in a solved state.
2614 * @param section the tempo section to be moved
2615 * @param bbt the requested new position for the tempo section
2616 * @return true if the tempo section can be moved to the position, otherwise false.
2619 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2622 TempoSection* tempo_copy = 0;
2625 Glib::Threads::RWLock::ReaderLock lm (lock);
2626 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
2632 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
2634 Metrics::const_iterator d = copy.begin();
2635 while (d != copy.end()) {
2644 * 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,
2645 * taking any possible reordering as a consequence of this into account.
2646 * @param section - the section to be altered
2647 * @param bbt - the bbt where the altered tempo will fall
2648 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
2650 pair<double, framepos_t>
2651 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
2654 pair<double, framepos_t> ret = make_pair (0.0, 0);
2656 Glib::Threads::RWLock::ReaderLock lm (lock);
2658 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
2660 const double beat = beat_at_bbt_locked (future_map, bbt);
2662 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
2663 ret.first = tempo_copy->pulse();
2664 ret.second = tempo_copy->frame();
2666 ret.first = section->pulse();
2667 ret.second = section->frame();
2670 Metrics::const_iterator d = future_map.begin();
2671 while (d != future_map.end()) {
2679 TempoMap::gui_move_tempo (TempoSection* ts, const framepos_t& frame, const int& sub_num)
2682 bool was_musical = ts->position_lock_style() == MusicTime;
2684 if (sub_num == 0 && was_musical) {
2685 /* if we're not snapping to music,
2686 AudioTime and MusicTime may be treated identically.
2688 ts->set_position_lock_style (AudioTime);
2691 if (ts->position_lock_style() == MusicTime) {
2693 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
2694 Glib::Threads::RWLock::WriterLock lm (lock);
2695 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2696 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
2697 double pulse = pulse_at_beat_locked (future_map, beat);
2699 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
2700 solve_map_pulse (_metrics, ts, pulse);
2701 recompute_meters (_metrics);
2708 Glib::Threads::RWLock::WriterLock lm (lock);
2709 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2710 if (solve_map_frame (future_map, tempo_copy, frame)) {
2711 solve_map_frame (_metrics, ts, frame);
2712 recompute_meters (_metrics);
2717 if (sub_num == 0 && was_musical) {
2718 ts->set_position_lock_style (MusicTime);
2721 Metrics::const_iterator d = future_map.begin();
2722 while (d != future_map.end()) {
2727 MetricPositionChanged (); // Emit Signal
2731 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
2735 if (ms->position_lock_style() == AudioTime) {
2738 Glib::Threads::RWLock::WriterLock lm (lock);
2739 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2741 if (solve_map_frame (future_map, copy, frame)) {
2742 solve_map_frame (_metrics, ms, frame);
2743 recompute_tempos (_metrics);
2748 Glib::Threads::RWLock::WriterLock lm (lock);
2749 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2751 const double beat = beat_at_frame_locked (_metrics, frame);
2752 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
2754 if (solve_map_bbt (future_map, copy, bbt)) {
2755 solve_map_bbt (_metrics, ms, bbt);
2756 recompute_tempos (_metrics);
2761 Metrics::const_iterator d = future_map.begin();
2762 while (d != future_map.end()) {
2767 MetricPositionChanged (); // Emit Signal
2771 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2774 bool can_solve = false;
2776 Glib::Threads::RWLock::WriterLock lm (lock);
2777 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2778 tempo_copy->set_beats_per_minute (bpm.beats_per_minute());
2779 recompute_tempos (future_map);
2781 if (check_solved (future_map)) {
2782 ts->set_beats_per_minute (bpm.beats_per_minute());
2783 recompute_map (_metrics);
2788 Metrics::const_iterator d = future_map.begin();
2789 while (d != future_map.end()) {
2794 MetricPositionChanged (); // Emit Signal
2800 TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame, const double& pulse)
2803 Ts (future prev_t) Tnext
2806 |----------|----------
2813 Glib::Threads::RWLock::WriterLock lm (lock);
2819 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
2820 TempoSection* prev_to_prev_t = 0;
2821 const frameoffset_t fr_off = end_frame - frame;
2823 if (prev_t && prev_t->pulse() > 0.0) {
2824 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_frame_locked (future_map, prev_t->frame() - 1));
2827 TempoSection* next_t = 0;
2828 for (Metrics::iterator i = future_map.begin(); i != future_map.end(); ++i) {
2829 TempoSection* t = 0;
2830 if ((*i)->is_tempo()) {
2831 t = static_cast<TempoSection*> (*i);
2832 if (t->frame() > ts->frame()) {
2838 /* minimum allowed measurement distance in frames */
2839 const framepos_t min_dframe = 2;
2841 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
2842 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
2844 double contribution = 0.0;
2846 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2847 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
2850 const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
2852 const double start_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
2853 const double end_pulse = prev_t->pulse_at_frame (end_frame, _frame_rate);
2857 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
2859 if (prev_t->position_lock_style() == MusicTime) {
2860 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2861 if (frame > prev_to_prev_t->frame() + min_dframe && (frame + prev_t_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
2863 new_bpm = prev_t->beats_per_minute() * ((frame - prev_to_prev_t->frame())
2864 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
2866 new_bpm = prev_t->beats_per_minute();
2869 /* prev to prev is irrelevant */
2871 if (start_pulse > prev_t->pulse() && end_pulse > prev_t->pulse()) {
2872 new_bpm = prev_t->beats_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
2874 new_bpm = prev_t->beats_per_minute();
2879 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2880 if (frame > prev_to_prev_t->frame() + min_dframe && end_frame > prev_to_prev_t->frame() + min_dframe) {
2882 new_bpm = prev_t->beats_per_minute() * ((frame - prev_to_prev_t->frame())
2883 / (double) ((end_frame) - prev_to_prev_t->frame()));
2885 new_bpm = prev_t->beats_per_minute();
2888 /* prev_to_prev_t is irrelevant */
2890 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
2891 new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
2893 new_bpm = prev_t->beats_per_minute();
2899 double frame_ratio = 1.0;
2900 double pulse_ratio = 1.0;
2901 const framepos_t pulse_pos = prev_t->frame_at_pulse (pulse, _frame_rate);
2903 if (prev_to_prev_t) {
2904 if (pulse_pos > prev_to_prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_to_prev_t->frame() + min_dframe) {
2905 frame_ratio = (((pulse_pos - fr_off) - prev_to_prev_t->frame()) / (double) ((pulse_pos) - prev_to_prev_t->frame()));
2907 if (end_pulse > prev_to_prev_t->pulse() && start_pulse > prev_to_prev_t->pulse()) {
2908 pulse_ratio = ((start_pulse - prev_to_prev_t->pulse()) / (end_pulse - prev_to_prev_t->pulse()));
2911 if (pulse_pos > prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_t->frame() + min_dframe) {
2912 frame_ratio = (((pulse_pos - fr_off) - prev_t->frame()) / (double) ((pulse_pos) - prev_t->frame()));
2914 pulse_ratio = (start_pulse / end_pulse);
2916 new_bpm = prev_t->beats_per_minute() * (pulse_ratio * frame_ratio);
2919 /* don't clamp and proceed here.
2920 testing has revealed that this can go negative,
2921 which is an entirely different thing to just being too low.
2923 if (new_bpm < 0.5) {
2926 new_bpm = min (new_bpm, (double) 1000.0);
2927 prev_t->set_beats_per_minute (new_bpm);
2928 recompute_tempos (future_map);
2929 recompute_meters (future_map);
2931 if (check_solved (future_map)) {
2932 ts->set_beats_per_minute (new_bpm);
2933 recompute_tempos (_metrics);
2934 recompute_meters (_metrics);
2938 Metrics::const_iterator d = future_map.begin();
2939 while (d != future_map.end()) {
2944 MetricPositionChanged (); // Emit Signal
2948 TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t& sub_num)
2950 Glib::Threads::RWLock::ReaderLock lm (lock);
2952 return exact_beat_at_frame_locked (_metrics, frame, sub_num);
2956 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t& sub_num)
2958 double beat = beat_at_frame_locked (metrics, frame);
2960 beat = floor (beat) + (floor (((beat - floor (beat)) * (double) sub_num) + 0.5) / sub_num);
2961 } else if (sub_num == 1) {
2963 beat = floor (beat + 0.5);
2964 } else if (sub_num == -1) {
2966 Timecode::BBT_Time bbt = bbt_at_beat_locked (metrics, beat);
2969 beat = beat_at_bbt_locked (metrics, bbt);
2975 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2977 Glib::Threads::RWLock::ReaderLock lm (lock);
2979 const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2980 const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2981 const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2983 return frame_at_beat_locked (_metrics, total_beats);
2987 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2989 return round_to_type (fr, dir, Bar);
2993 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2995 return round_to_type (fr, dir, Beat);
2999 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3001 Glib::Threads::RWLock::ReaderLock lm (lock);
3002 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
3003 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3004 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3006 ticks -= beats * BBT_Time::ticks_per_beat;
3009 /* round to next (or same iff dir == RoundUpMaybe) */
3011 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3013 if (mod == 0 && dir == RoundUpMaybe) {
3014 /* right on the subdivision, which is fine, so do nothing */
3016 } else if (mod == 0) {
3017 /* right on the subdivision, so the difference is just the subdivision ticks */
3018 ticks += ticks_one_subdivisions_worth;
3021 /* not on subdivision, compute distance to next subdivision */
3023 ticks += ticks_one_subdivisions_worth - mod;
3026 if (ticks >= BBT_Time::ticks_per_beat) {
3027 ticks -= BBT_Time::ticks_per_beat;
3029 } else if (dir < 0) {
3031 /* round to previous (or same iff dir == RoundDownMaybe) */
3033 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3035 if (difference == 0 && dir == RoundDownAlways) {
3036 /* right on the subdivision, but force-rounding down,
3037 so the difference is just the subdivision ticks */
3038 difference = ticks_one_subdivisions_worth;
3041 if (ticks < difference) {
3042 ticks = BBT_Time::ticks_per_beat - ticks;
3044 ticks -= difference;
3048 /* round to nearest */
3051 /* compute the distance to the previous and next subdivision */
3053 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3055 /* closer to the next subdivision, so shift forward */
3057 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3059 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3061 if (ticks > BBT_Time::ticks_per_beat) {
3063 ticks -= BBT_Time::ticks_per_beat;
3064 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3067 } else if (rem > 0) {
3069 /* closer to previous subdivision, so shift backward */
3073 /* can't go backwards past zero, so ... */
3076 /* step back to previous beat */
3078 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3079 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3081 ticks = lrint (ticks - rem);
3082 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3085 /* on the subdivision, do nothing */
3089 const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
3095 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
3097 Glib::Threads::RWLock::ReaderLock lm (lock);
3099 const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
3100 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
3105 /* find bar previous to 'frame' */
3108 return frame_at_bbt_locked (_metrics, bbt);
3110 } else if (dir > 0) {
3111 /* find bar following 'frame' */
3115 return frame_at_bbt_locked (_metrics, bbt);
3117 /* true rounding: find nearest bar */
3118 framepos_t raw_ft = frame_at_bbt_locked (_metrics, bbt);
3121 framepos_t prev_ft = frame_at_bbt_locked (_metrics, bbt);
3123 framepos_t next_ft = frame_at_bbt_locked (_metrics, bbt);
3125 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
3136 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
3137 } else if (dir > 0) {
3138 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
3140 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
3149 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
3150 framepos_t lower, framepos_t upper)
3152 Glib::Threads::RWLock::ReaderLock lm (lock);
3153 int32_t cnt = ceil (beat_at_frame_locked (_metrics, lower));
3155 /* although the map handles negative beats, bbt doesn't. */
3160 if (frame_at_beat_locked (_metrics, cnt) >= upper) {
3164 while (pos < upper) {
3165 pos = frame_at_beat_locked (_metrics, cnt);
3166 const TempoSection tempo = tempo_section_at_frame_locked (_metrics, pos);
3167 const MeterSection meter = meter_section_at_frame_locked (_metrics, pos);
3168 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
3169 points.push_back (BBTPoint (meter, tempo_at_frame_locked (_metrics, pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
3175 TempoMap::tempo_section_at_frame (framepos_t frame) const
3177 Glib::Threads::RWLock::ReaderLock lm (lock);
3178 return tempo_section_at_frame_locked (_metrics, frame);
3182 TempoMap::tempo_section_at_frame_locked (const Metrics& metrics, framepos_t frame) const
3184 TempoSection* prev = 0;
3188 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3190 if ((*i)->is_tempo()) {
3191 t = static_cast<TempoSection*> (*i);
3195 if (prev && t->frame() > frame) {
3205 abort(); /*NOTREACHED*/
3212 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3214 TempoSection* prev_t = 0;
3215 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
3219 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3220 if ((*i)->is_tempo()) {
3221 t = static_cast<TempoSection*> (*i);
3222 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
3232 /* don't use this to calculate length (the tempo is only correct for this frame).
3233 do that stuff based on the beat_at_frame and frame_at_beat api
3236 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
3238 Glib::Threads::RWLock::ReaderLock lm (lock);
3240 const TempoSection* ts_at = 0;
3241 const TempoSection* ts_after = 0;
3242 Metrics::const_iterator i;
3245 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3247 if ((*i)->is_tempo()) {
3248 t = static_cast<TempoSection*> (*i);
3252 if (ts_at && (*i)->frame() > frame) {
3261 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate) * ts_at->note_type());
3263 /* must be treated as constant tempo */
3264 return ts_at->frames_per_beat (_frame_rate);
3268 TempoMap::meter_section_at_frame_locked (const Metrics& metrics, framepos_t frame) const
3270 Metrics::const_iterator i;
3271 MeterSection* prev = 0;
3275 for (i = metrics.begin(); i != metrics.end(); ++i) {
3277 if (!(*i)->is_tempo()) {
3278 m = static_cast<MeterSection*> (*i);
3280 if (prev && (*i)->frame() > frame) {
3290 abort(); /*NOTREACHED*/
3298 TempoMap::meter_section_at_frame (framepos_t frame) const
3300 Glib::Threads::RWLock::ReaderLock lm (lock);
3301 return meter_section_at_frame_locked (_metrics, frame);
3305 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3307 MeterSection* prev_m = 0;
3309 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3311 if (!(*i)->is_tempo()) {
3312 m = static_cast<MeterSection*> (*i);
3313 if (prev_m && m->beat() > beat) {
3324 TempoMap::meter_section_at_beat (double beat) const
3326 Glib::Threads::RWLock::ReaderLock lm (lock);
3327 return meter_section_at_beat_locked (_metrics, beat);
3331 TempoMap::meter_at_frame (framepos_t frame) const
3333 TempoMetric m (metric_at (frame));
3338 TempoMap::fix_legacy_session ()
3340 MeterSection* prev_m = 0;
3341 TempoSection* prev_t = 0;
3343 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3347 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3348 if (!m->movable()) {
3349 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3353 m->set_position_lock_style (AudioTime);
3358 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
3359 + (m->bbt().beats - 1)
3360 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
3362 m->set_beat (start);
3363 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
3364 + (m->bbt().beats - 1)
3365 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
3366 m->set_pulse (start_beat / prev_m->note_divisor());
3369 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3375 if (!t->movable()) {
3378 t->set_position_lock_style (AudioTime);
3384 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
3385 + (t->legacy_bbt().beats - 1)
3386 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
3388 t->set_pulse (beat / prev_m->note_divisor());
3390 /* really shouldn't happen but.. */
3391 t->set_pulse (beat / 4.0);
3400 TempoMap::get_state ()
3402 Metrics::const_iterator i;
3403 XMLNode *root = new XMLNode ("TempoMap");
3406 Glib::Threads::RWLock::ReaderLock lm (lock);
3407 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3408 root->add_child_nocopy ((*i)->get_state());
3416 TempoMap::set_state (const XMLNode& node, int /*version*/)
3419 Glib::Threads::RWLock::WriterLock lm (lock);
3422 XMLNodeConstIterator niter;
3423 Metrics old_metrics (_metrics);
3426 nlist = node.children();
3428 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
3429 XMLNode* child = *niter;
3431 if (child->name() == TempoSection::xml_state_node_name) {
3434 TempoSection* ts = new TempoSection (*child);
3435 _metrics.push_back (ts);
3438 catch (failed_constructor& err){
3439 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3440 _metrics = old_metrics;
3444 } else if (child->name() == MeterSection::xml_state_node_name) {
3447 MeterSection* ms = new MeterSection (*child);
3448 _metrics.push_back (ms);
3451 catch (failed_constructor& err) {
3452 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3453 _metrics = old_metrics;
3459 if (niter == nlist.end()) {
3460 MetricSectionSorter cmp;
3461 _metrics.sort (cmp);
3464 /* check for legacy sessions where bbt was the base musical unit for tempo */
3465 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3467 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3468 if (t->legacy_bbt().bars != 0) {
3469 fix_legacy_session();
3476 /* check for multiple tempo/meters at the same location, which
3477 ardour2 somehow allowed.
3480 Metrics::iterator prev = _metrics.end();
3481 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3482 if (prev != _metrics.end()) {
3484 MeterSection* prev_m;
3486 TempoSection* prev_t;
3487 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
3488 if (prev_m->pulse() == ms->pulse()) {
3489 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3490 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3493 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
3494 if (prev_t->pulse() == ts->pulse()) {
3495 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3496 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3504 recompute_map (_metrics);
3507 PropertyChanged (PropertyChange ());
3513 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
3515 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
3516 const MeterSection* m;
3517 const TempoSection* t;
3518 const TempoSection* prev_t = 0;
3520 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3522 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
3523 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
3524 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
3525 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
3527 o << "previous : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
3528 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;
3531 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
3532 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
3533 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
3536 o << "------" << std::endl;
3540 TempoMap::n_tempos() const
3542 Glib::Threads::RWLock::ReaderLock lm (lock);
3545 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3546 if ((*i)->is_tempo()) {
3555 TempoMap::n_meters() const
3557 Glib::Threads::RWLock::ReaderLock lm (lock);
3560 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3561 if (!(*i)->is_tempo()) {
3570 TempoMap::insert_time (framepos_t where, framecnt_t amount)
3573 Glib::Threads::RWLock::WriterLock lm (lock);
3574 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3575 if ((*i)->frame() >= where && (*i)->movable ()) {
3576 (*i)->set_frame ((*i)->frame() + amount);
3580 /* now reset the BBT time of all metrics, based on their new
3581 * audio time. This is the only place where we do this reverse
3585 Metrics::iterator i;
3586 const MeterSection* meter;
3587 const TempoSection* tempo;
3591 meter = &first_meter ();
3592 tempo = &first_tempo ();
3597 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
3600 MetricSection* prev = 0;
3602 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3605 //TempoMetric metric (*meter, *tempo);
3606 MeterSection* ms = const_cast<MeterSection*>(meter);
3607 TempoSection* ts = const_cast<TempoSection*>(tempo);
3610 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3614 ts->set_pulse (t->pulse());
3616 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3617 ts->set_pulse (m->pulse());
3619 ts->set_frame (prev->frame());
3623 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3624 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3625 ms->set_beat (start);
3626 ms->set_pulse (m->pulse());
3628 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3632 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3633 pair<double, BBT_Time> start = make_pair (beat, bbt_at_beat_locked (_metrics, beat));
3634 ms->set_beat (start);
3635 ms->set_pulse (t->pulse());
3637 ms->set_frame (prev->frame());
3641 // metric will be at frames=0 bbt=1|1|0 by default
3642 // which is correct for our purpose
3645 // cerr << bbt << endl;
3647 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3651 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3653 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3654 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3655 bbt = bbt_at_frame_locked (_metrics, m->frame());
3657 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3663 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3664 /* round up to next beat */
3670 if (bbt.beats != 1) {
3671 /* round up to next bar */
3676 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3677 m->set_beat (start);
3678 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3680 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3682 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3683 abort(); /*NOTREACHED*/
3689 recompute_map (_metrics);
3693 PropertyChanged (PropertyChange ());
3696 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3700 std::list<MetricSection*> metric_kill_list;
3702 TempoSection* last_tempo = NULL;
3703 MeterSection* last_meter = NULL;
3704 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3705 bool meter_after = false; // is there a meter marker likewise?
3707 Glib::Threads::RWLock::WriterLock lm (lock);
3708 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3709 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3710 metric_kill_list.push_back(*i);
3711 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3714 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3718 else if ((*i)->frame() >= where) {
3719 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3720 (*i)->set_frame ((*i)->frame() - amount);
3721 if ((*i)->frame() == where) {
3722 // marker was immediately after end of range
3723 tempo_after = dynamic_cast<TempoSection*> (*i);
3724 meter_after = dynamic_cast<MeterSection*> (*i);
3730 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3731 if (last_tempo && !tempo_after) {
3732 metric_kill_list.remove(last_tempo);
3733 last_tempo->set_frame(where);
3736 if (last_meter && !meter_after) {
3737 metric_kill_list.remove(last_meter);
3738 last_meter->set_frame(where);
3742 //remove all the remaining metrics
3743 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3744 _metrics.remove(*i);
3749 recompute_map (_metrics);
3752 PropertyChanged (PropertyChange ());
3756 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3757 * pos can be -ve, if required.
3760 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3762 Glib::Threads::RWLock::ReaderLock lm (lock);
3764 return frame_at_beat_locked (_metrics, beat_at_frame_locked (_metrics, pos) + beats.to_double());
3767 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3769 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3771 Glib::Threads::RWLock::ReaderLock lm (lock);
3773 return frame_at_beat_locked (_metrics, beat_at_frame_locked (_metrics, pos) - beats.to_double());
3776 /** Add the BBT interval op to pos and return the result */
3778 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3780 Glib::Threads::RWLock::ReaderLock lm (lock);
3782 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3783 pos_bbt.ticks += op.ticks;
3784 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3786 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3788 pos_bbt.beats += op.beats;
3789 /* the meter in effect will start on the bar */
3790 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();
3791 while (pos_bbt.beats >= divisions_per_bar + 1) {
3793 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3794 pos_bbt.beats -= divisions_per_bar;
3796 pos_bbt.bars += op.bars;
3798 return frame_at_bbt_locked (_metrics, pos_bbt);
3801 /** Count the number of beats that are equivalent to distance when going forward,
3805 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3807 Glib::Threads::RWLock::ReaderLock lm (lock);
3809 return Evoral::Beats (beat_at_frame_locked (_metrics, pos + distance) - beat_at_frame_locked (_metrics, pos));
3813 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3819 operator<< (std::ostream& o, const Meter& m) {
3820 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3824 operator<< (std::ostream& o, const Tempo& t) {
3825 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3829 operator<< (std::ostream& o, const MetricSection& section) {
3831 o << "MetricSection @ " << section.frame() << ' ';
3833 const TempoSection* ts;
3834 const MeterSection* ms;
3836 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3837 o << *((const Tempo*) ts);
3838 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3839 o << *((const Meter*) ms);