2 Copyright (C) 2000-2002 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <glibmm/threads.h>
28 #include "pbd/enumwriter.h"
29 #include "pbd/xml++.h"
30 #include "evoral/Beats.hpp"
32 #include "ardour/debug.h"
33 #include "ardour/lmath.h"
34 #include "ardour/tempo.h"
40 using namespace ARDOUR;
43 using Timecode::BBT_Time;
45 /* _default tempo is 4/4 qtr=120 */
47 Meter TempoMap::_default_meter (4.0, 4.0);
48 Tempo TempoMap::_default_tempo (120.0);
50 /***********************************************************************/
53 Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
55 /* This is tempo- and meter-sensitive. The number it returns
56 is based on the interval between any two lines in the
57 grid that is constructed from tempo and meter sections.
59 The return value IS NOT interpretable in terms of "beats".
62 return (60.0 * sr) / (tempo.beats_per_minute() * (_note_type/tempo.note_type()));
66 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
68 return frames_per_grid (tempo, sr) * _divisions_per_bar;
71 /***********************************************************************/
73 const string TempoSection::xml_state_node_name = "Tempo";
75 TempoSection::TempoSection (const XMLNode& node)
76 : MetricSection (0.0, 0, MusicTime, true)
77 , Tempo (TempoMap::default_tempo())
80 , _locked_to_meter (false)
82 XMLProperty const * prop;
88 _legacy_bbt = BBT_Time (0, 0, 0);
90 if ((prop = node.property ("start")) != 0) {
91 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
95 /* legacy session - start used to be in bbt*/
98 info << _("Legacy session detected. TempoSection XML node will be altered.") << endmsg;
102 if ((prop = node.property ("pulse")) != 0) {
103 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
104 error << _("TempoSection XML node has an illegal \"pulse\" value") << endmsg;
110 if ((prop = node.property ("frame")) != 0) {
111 if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) {
112 error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg;
118 if ((prop = node.property ("beats-per-minute")) == 0) {
119 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
120 throw failed_constructor();
123 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
124 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
125 throw failed_constructor();
128 if ((prop = node.property ("note-type")) == 0) {
129 /* older session, make note type be quarter by default */
132 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
133 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
134 throw failed_constructor();
138 if ((prop = node.property ("movable")) == 0) {
139 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
140 throw failed_constructor();
143 set_movable (string_is_affirmative (prop->value()));
145 if ((prop = node.property ("active")) == 0) {
146 warning << _("TempoSection XML node has no \"active\" property") << endmsg;
149 set_active (string_is_affirmative (prop->value()));
152 if ((prop = node.property ("tempo-type")) == 0) {
155 _type = Type (string_2_enum (prop->value(), _type));
158 if ((prop = node.property ("lock-style")) == 0) {
160 set_position_lock_style (MusicTime);
162 set_position_lock_style (AudioTime);
165 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
168 if ((prop = node.property ("locked-to-meter")) == 0) {
169 set_locked_to_meter (false);
171 set_locked_to_meter (string_is_affirmative (prop->value()));
176 TempoSection::get_state() const
178 XMLNode *root = new XMLNode (xml_state_node_name);
182 snprintf (buf, sizeof (buf), "%f", pulse());
183 root->add_property ("pulse", buf);
184 snprintf (buf, sizeof (buf), "%li", frame());
185 root->add_property ("frame", buf);
186 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
187 root->add_property ("beats-per-minute", buf);
188 snprintf (buf, sizeof (buf), "%f", _note_type);
189 root->add_property ("note-type", buf);
190 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
191 root->add_property ("movable", buf);
192 snprintf (buf, sizeof (buf), "%s", active()?"yes":"no");
193 root->add_property ("active", buf);
194 root->add_property ("tempo-type", enum_2_string (_type));
195 root->add_property ("lock-style", enum_2_string (position_lock_style()));
196 root->add_property ("locked-to-meter", locked_to_meter()?"yes":"no");
202 TempoSection::set_type (Type type)
207 /** returns the tempo in whole pulses per minute at the zero-based (relative to session) frame.
210 TempoSection::tempo_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
213 if (_type == Constant || _c_func == 0.0) {
214 return pulses_per_minute();
217 return pulse_tempo_at_time (frame_to_minute (f - frame(), frame_rate));
220 /** returns the zero-based frame (relative to session)
221 where the tempo in whole pulses per minute occurs in this section.
222 pulse p is only used for constant tempos.
223 note that the tempo map may have multiple such values.
226 TempoSection::frame_at_tempo (const double& ppm, const double& p, const framecnt_t& frame_rate) const
228 if (_type == Constant || _c_func == 0.0) {
229 return ((p - pulse()) * frames_per_pulse (frame_rate)) + frame();
232 return minute_to_frame (time_at_pulse_tempo (ppm), frame_rate) + frame();
234 /** returns the tempo in pulses per minute at the zero-based (relative to session) beat.
237 TempoSection::tempo_at_pulse (const double& p) const
240 if (_type == Constant || _c_func == 0.0) {
241 return pulses_per_minute();
243 double const ppm = pulse_tempo_at_pulse (p - pulse());
247 /** returns the zero-based beat (relative to session)
248 where the tempo in whole pulses per minute occurs given frame f. frame f is only used for constant tempos.
249 note that the session tempo map may have multiple beats at a given tempo.
252 TempoSection::pulse_at_tempo (const double& ppm, const framepos_t& f, const framecnt_t& frame_rate) const
254 if (_type == Constant || _c_func == 0.0) {
255 double const pulses = ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
258 return pulse_at_pulse_tempo (ppm) + pulse();
261 /** returns the zero-based pulse (relative to session origin)
262 where the zero-based frame (relative to session)
266 TempoSection::pulse_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
268 if (_type == Constant || _c_func == 0.0) {
269 return ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
272 return pulse_at_time (frame_to_minute (f - frame(), frame_rate)) + pulse();
275 /** returns the zero-based frame (relative to session start frame)
276 where the zero-based pulse (relative to session start)
281 TempoSection::frame_at_pulse (const double& p, const framecnt_t& frame_rate) const
283 if (_type == Constant || _c_func == 0.0) {
284 return (framepos_t) floor ((p - pulse()) * frames_per_pulse (frame_rate)) + frame();
287 return minute_to_frame (time_at_pulse (p - pulse()), frame_rate) + frame();
295 Tt----|-----------------*|
296 Ta----|--------------|* |
302 _______________|___|____
303 time a t (next tempo)
306 Duration in beats at time a is the integral of some Tempo function.
307 In our case, the Tempo function (Tempo at time t) is
310 with function constant
315 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
316 b(t) = T0(e^(ct) - 1) / c
318 To find the time t at beat duration b, we use the inverse function of the beat function (the time function) which can be shown to be:
319 t(b) = log((c.b / T0) + 1) / c
321 The time t at which Tempo T occurs is a as above:
322 t(T) = log(T / T0) / c
324 The beat at which a Tempo T occurs is:
327 The Tempo at which beat b occurs is:
330 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
331 Our problem is that we usually don't know t.
332 We almost always know the duration in beats between this and the new section, so we need to find c in terms of the beat function.
333 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
334 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
336 By substituting our expanded t as a in the c function above, our problem is reduced to:
337 c = T0 (e^(log (Ta / T0)) - 1) / b
339 Of course the word 'beat' has been left loosely defined above.
340 In music, a beat is defined by the musical pulse (which comes from the tempo)
341 and the meter in use at a particular time (how many pulse divisions there are in one bar).
342 It would be more accurate to substitute the work 'pulse' for 'beat' above.
346 We can now store c for future time calculations.
347 If the following tempo section (the one that defines c in conjunction with this one)
348 is changed or moved, c is no longer valid.
350 The public methods are session-relative.
352 Most of this stuff is taken from this paper:
355 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
358 Zurich University of Arts
359 Institute for Computer Music and Sound Technology
361 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
366 compute this ramp's function constant using the end tempo (in whole pulses per minute)
367 and duration (pulses into global start) of some later tempo section.
370 TempoSection::compute_c_func_pulse (const double& end_bpm, const double& end_pulse, const framecnt_t& frame_rate)
372 double const log_tempo_ratio = log (end_bpm / pulses_per_minute());
373 return pulses_per_minute() * (expm1 (log_tempo_ratio)) / (end_pulse - pulse());
376 /* compute the function constant from some later tempo section, given tempo (whole pulses/min.) and distance (in frames) from session origin */
378 TempoSection::compute_c_func_frame (const double& end_bpm, const framepos_t& end_frame, const framecnt_t& frame_rate) const
380 return c_func (end_bpm, frame_to_minute (end_frame - frame(), frame_rate));
384 TempoSection::minute_to_frame (const double& time, const framecnt_t& frame_rate) const
386 return (framepos_t) floor ((time * 60.0 * frame_rate) + 0.5);
390 TempoSection::frame_to_minute (const framepos_t& frame, const framecnt_t& frame_rate) const
392 return (frame / (double) frame_rate) / 60.0;
395 /* position function */
397 TempoSection::a_func (double end_ppm, double c_func) const
399 return log (end_ppm / pulses_per_minute()) / c_func;
402 /*function constant*/
404 TempoSection::c_func (double end_ppm, double end_time) const
406 return log (end_ppm / pulses_per_minute()) / end_time;
409 /* tempo in ppm at time in minutes */
411 TempoSection::pulse_tempo_at_time (const double& time) const
413 return exp (_c_func * time) * pulses_per_minute();
416 /* time in minutes at tempo in ppm */
418 TempoSection::time_at_pulse_tempo (const double& pulse_tempo) const
420 return log (pulse_tempo / pulses_per_minute()) / _c_func;
423 /* tick at tempo in ppm */
425 TempoSection::pulse_at_pulse_tempo (const double& pulse_tempo) const
427 return (pulse_tempo - pulses_per_minute()) / _c_func;
430 /* tempo in ppm at tick */
432 TempoSection::pulse_tempo_at_pulse (const double& pulse) const
434 return (pulse * _c_func) + pulses_per_minute();
437 /* pulse at time in minutes */
439 TempoSection::pulse_at_time (const double& time) const
441 return expm1 (_c_func * time) * (pulses_per_minute() / _c_func);
444 /* time in minutes at pulse */
446 TempoSection::time_at_pulse (const double& pulse) const
448 return log1p ((_c_func * pulse) / pulses_per_minute()) / _c_func;
451 /***********************************************************************/
453 const string MeterSection::xml_state_node_name = "Meter";
455 MeterSection::MeterSection (const XMLNode& node)
456 : MetricSection (0.0, 0, MusicTime, false), Meter (TempoMap::default_meter())
458 XMLProperty const * prop;
463 framepos_t frame = 0;
464 pair<double, BBT_Time> start;
466 if ((prop = node.property ("start")) != 0) {
467 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
471 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
473 /* legacy session - start used to be in bbt*/
474 info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
479 if ((prop = node.property ("pulse")) != 0) {
480 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
481 error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
486 if ((prop = node.property ("beat")) != 0) {
487 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
488 error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
494 if ((prop = node.property ("bbt")) == 0) {
495 warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
496 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
500 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
501 throw failed_constructor();
507 if ((prop = node.property ("frame")) != 0) {
508 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
509 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
515 /* beats-per-bar is old; divisions-per-bar is new */
517 if ((prop = node.property ("divisions-per-bar")) == 0) {
518 if ((prop = node.property ("beats-per-bar")) == 0) {
519 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
520 throw failed_constructor();
523 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
524 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
525 throw failed_constructor();
528 if ((prop = node.property ("note-type")) == 0) {
529 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
530 throw failed_constructor();
532 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
533 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
534 throw failed_constructor();
537 if ((prop = node.property ("movable")) == 0) {
538 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
539 throw failed_constructor();
542 set_movable (string_is_affirmative (prop->value()));
544 if ((prop = node.property ("lock-style")) == 0) {
545 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
547 set_position_lock_style (MusicTime);
549 set_position_lock_style (AudioTime);
552 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
557 MeterSection::get_state() const
559 XMLNode *root = new XMLNode (xml_state_node_name);
563 snprintf (buf, sizeof (buf), "%lf", pulse());
564 root->add_property ("pulse", buf);
565 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
569 root->add_property ("bbt", buf);
570 snprintf (buf, sizeof (buf), "%lf", beat());
571 root->add_property ("beat", buf);
572 snprintf (buf, sizeof (buf), "%f", _note_type);
573 root->add_property ("note-type", buf);
574 snprintf (buf, sizeof (buf), "%li", frame());
575 root->add_property ("frame", buf);
576 root->add_property ("lock-style", enum_2_string (position_lock_style()));
577 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
578 root->add_property ("divisions-per-bar", buf);
579 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
580 root->add_property ("movable", buf);
585 /***********************************************************************/
589 The Shaggs - Things I Wonder
590 https://www.youtube.com/watch?v=9wQK6zMJOoQ
592 Tempo is the rate of the musical pulse.
593 Meters divide the pulses into measures and beats.
595 TempoSections - provide pulses in the form of beats_per_minute() and note_type() where note_type is the division of a whole pulse,
596 and beats_per_minute is the number of note_types in one minute (unlike what its name suggests).
597 Note that Tempo::beats_per_minute() has nothing to do with musical beats. It has been left that way because
598 a shorter one hasn't been found yet (pulse_divisions_per_minute()?).
600 MeterSecions - divide pulses into measures (via divisions_per_bar) and beats (via note_divisor).
602 Both tempos and meters have a pulse position and a frame position.
603 Meters also have a beat position, which is always 0.0 for the first meter.
604 TempoSections and MeterSections may be locked to either audio or music (position lock style).
605 The lock style determines the 'true' position of the section wich is used to calculate the other postion parameters of the section.
607 The first tempo and first meter are special. they must move together, and must be locked to audio.
608 Audio locked tempos which lie before the first meter are made inactive.
609 They will be re-activated if the first meter is again placed before them.
611 With tepo sections potentially being ramped, meters provide a way of mapping beats to whole pulses without
612 referring to the tempo function(s) involved as the distance in whole pulses between a meter and a subsequent beat is
613 sb->beat() - meter->beat() / meter->note_divisor().
614 Because every meter falls on a known pulse, (derived from its bar), the rest is easy as the duration in pulses between
615 two meters is of course
616 (meater_b->bar - meter_a->bar) * meter_a->divisions_per_bar / meter_a->note_divisor.
618 Below, beat calculations are based on meter sections and all pulse and tempo calculations are based on tempo sections.
619 Beat to frame conversion of course requires the use of meter and tempo.
621 Remembering that ramped tempo sections interact, it is important to avoid referring to any other tempos when moving tempo sections,
622 Here, beats (meters) are used to determine the new pulse (see predict_tempo_position())
624 Recomputing the map is the process where the 'missing' position
625 (tempo pulse or meter pulse & beat in the case of AudioTime, frame for MusicTime) is calculated.
626 We construct the tempo map by first using the frame or pulse position (depending on position lock style) of each tempo.
627 We then use this tempo map (really just the tempos) to find the pulse or frame position of each meter (again depending on lock style).
629 Having done this, we can now find any musical duration by selecting the tempo and meter covering the position (or tempo) in question
630 and querying its appropriate meter/tempo.
632 It is important to keep the _metrics in an order that makes sense.
633 Because ramped MusicTime and AudioTime tempos can interact with each other,
634 reordering is frequent. Care must be taken to keep _metrics in a solved state.
635 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
639 Music and audio-locked objects may seem interchangeable on the surface, but when translating
640 between audio samples and beats, keep in mind that a sample is only a quantised approximation
641 of the actual time (in minutes) of a beat.
642 Thus if a gui user points to the frame occupying the start of a music-locked object on 1|3|0, it does not
643 mean that this frame is the actual location in time of 1|3|0.
645 You cannot use a frame measurement to determine beat distance except under special circumstances
646 (e.g. where the user has requested that a beat lie on a SMPTE frame or if the tempo is known to be constant over the duration).
648 This means is that a user operating on a musical grid must supply the desired beat position and/or current beat quantization in order for the
649 sample space the user is operating at to be translated correctly to the object.
651 The current approach is to interpret the supplied frame using the grid division the user has currently selected.
652 If the user has no musical grid set, they are actually operating in sample space (even SMPTE frames are rounded to audio frame), so
653 the supplied audio frame is interpreted as the desired musical location (beat_at_frame()).
655 tldr: Beat, being a function of time, has nothing to do with sample rate, but time quantization can get in the way of precision.
657 When frame_at_beat() is called, the position calculation is performed in pulses and minutes.
658 The result is rounded to audio frames.
659 When beat_at_frame() is called, the frame is converted to minutes, with no rounding performed on the result.
662 frame_at_beat (beat_at_frame (frame)) == frame
664 beat_at_frame (frame_at_beat (beat)) != beat due to the time quantization of frame_at_beat().
666 Doing the second one will result in a beat distance error of up to 0.5 audio samples.
667 So instead work in pulses and/or beats and only use beat position to caclulate frame position (e.g. after tempo change).
668 For audio-locked objects, use frame position to calculate beat position.
670 The above pointless example would then do:
671 beat_at_pulse (pulse_at_beat (beat)) to avoid rounding.
674 struct MetricSectionSorter {
675 bool operator() (const MetricSection* a, const MetricSection* b) {
676 return a->pulse() < b->pulse();
680 struct MetricSectionFrameSorter {
681 bool operator() (const MetricSection* a, const MetricSection* b) {
682 return a->frame() < b->frame();
686 TempoMap::TempoMap (framecnt_t fr)
689 BBT_Time start (1, 1, 0);
691 TempoSection *t = new TempoSection (0.0, 0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Ramp, AudioTime);
692 MeterSection *m = new MeterSection (0.0, 0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime);
694 t->set_movable (false);
695 m->set_movable (false);
697 /* note: frame time is correct (zero) for both of these */
699 _metrics.push_back (t);
700 _metrics.push_back (m);
704 TempoMap::~TempoMap ()
706 Metrics::const_iterator d = _metrics.begin();
707 while (d != _metrics.end()) {
715 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
717 bool removed = false;
720 Glib::Threads::RWLock::WriterLock lm (lock);
721 if ((removed = remove_tempo_locked (tempo))) {
722 if (complete_operation) {
723 recompute_map (_metrics);
728 if (removed && complete_operation) {
729 PropertyChanged (PropertyChange ());
734 TempoMap::remove_tempo_locked (const TempoSection& tempo)
738 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
739 if (dynamic_cast<TempoSection*> (*i) != 0) {
740 if (tempo.frame() == (*i)->frame()) {
741 if ((*i)->movable()) {
754 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
756 bool removed = false;
759 Glib::Threads::RWLock::WriterLock lm (lock);
760 if ((removed = remove_meter_locked (tempo))) {
761 if (complete_operation) {
762 recompute_map (_metrics);
767 if (removed && complete_operation) {
768 PropertyChanged (PropertyChange ());
773 TempoMap::remove_meter_locked (const MeterSection& meter)
777 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
779 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
780 if (meter.frame() == (*i)->frame()) {
781 if (t->locked_to_meter()) {
790 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
791 if (dynamic_cast<MeterSection*> (*i) != 0) {
792 if (meter.frame() == (*i)->frame()) {
793 if ((*i)->movable()) {
806 TempoMap::do_insert (MetricSection* section)
808 bool need_add = true;
809 /* we only allow new meters to be inserted on beat 1 of an existing
813 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
815 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
817 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
818 corrected.second.beats = 1;
819 corrected.second.ticks = 0;
820 corrected.first = beat_at_bbt_locked (_metrics, corrected.second);
821 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
822 m->bbt(), corrected.second) << endmsg;
823 //m->set_pulse (corrected);
827 /* Look for any existing MetricSection that is of the same type and
828 in the same bar as the new one, and remove it before adding
829 the new one. Note that this means that if we find a matching,
830 existing section, we can break out of the loop since we're
831 guaranteed that there is only one such match.
834 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
836 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
837 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
838 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
839 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
841 if (tempo && insert_tempo) {
844 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
845 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
847 if (!tempo->movable()) {
849 /* can't (re)move this section, so overwrite
850 * its data content (but not its properties as
854 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
855 (*i)->set_position_lock_style (AudioTime);
857 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
858 t->set_type (insert_tempo->type());
868 } else if (meter && insert_meter) {
872 bool const ipm = insert_meter->position_lock_style() == MusicTime;
874 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
876 if (!meter->movable()) {
878 /* can't (re)move this section, so overwrite
879 * its data content (but not its properties as
883 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
884 (*i)->set_position_lock_style (AudioTime);
894 /* non-matching types, so we don't care */
898 /* Add the given MetricSection, if we didn't just reset an existing
903 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
904 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
907 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
908 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
911 bool const ipm = insert_meter->position_lock_style() == MusicTime;
912 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
917 } else if (insert_tempo) {
918 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
919 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
922 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
923 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
930 _metrics.insert (i, section);
931 //dump (_metrics, std::cout);
936 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, ARDOUR::TempoSection::Type type, PositionLockStyle pls)
938 TempoSection* ts = 0;
940 Glib::Threads::RWLock::WriterLock lm (lock);
941 ts = add_tempo_locked (tempo, pulse, frame, type, pls, true);
945 PropertyChanged (PropertyChange ());
951 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, TempoSection::Type type, PositionLockStyle pls)
953 const bool locked_to_meter = ts.locked_to_meter();
956 Glib::Threads::RWLock::WriterLock lm (lock);
957 TempoSection& first (first_tempo());
958 if (ts.frame() != first.frame()) {
959 remove_tempo_locked (ts);
960 add_tempo_locked (tempo, pulse, frame, type, pls, true, locked_to_meter);
962 first.set_type (type);
963 first.set_pulse (0.0);
964 first.set_frame (frame);
965 first.set_position_lock_style (AudioTime);
967 /* cannot move the first tempo section */
968 *static_cast<Tempo*>(&first) = tempo;
969 recompute_map (_metrics);
974 PropertyChanged (PropertyChange ());
978 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, framepos_t frame
979 , TempoSection::Type type, PositionLockStyle pls, bool recompute, bool locked_to_meter)
981 TempoSection* t = new TempoSection (pulse, frame, tempo.beats_per_minute(), tempo.note_type(), type, pls);
982 t->set_locked_to_meter (locked_to_meter);
987 if (pls == AudioTime) {
988 solved = solve_map_frame (_metrics, t, t->frame());
990 solved = solve_map_pulse (_metrics, t, t->pulse());
992 recompute_meters (_metrics);
995 if (!solved && recompute) {
996 remove_tempo_locked (*t);
1004 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, const framepos_t& frame, PositionLockStyle pls)
1006 MeterSection* m = 0;
1008 Glib::Threads::RWLock::WriterLock lm (lock);
1009 m = add_meter_locked (meter, beat, where, frame, pls, true);
1014 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1015 dump (_metrics, std::cerr);
1019 PropertyChanged (PropertyChange ());
1024 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, const framepos_t& frame, PositionLockStyle pls)
1027 Glib::Threads::RWLock::WriterLock lm (lock);
1028 const double beat = beat_at_bbt_locked (_metrics, where);
1031 remove_meter_locked (ms);
1032 add_meter_locked (meter, beat, where, frame, pls, true);
1034 MeterSection& first (first_meter());
1035 TempoSection& first_t (first_tempo());
1036 /* cannot move the first meter section */
1037 *static_cast<Meter*>(&first) = meter;
1038 first.set_position_lock_style (AudioTime);
1039 first.set_pulse (0.0);
1040 first.set_frame (frame);
1041 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1042 first.set_beat (beat);
1043 first_t.set_frame (first.frame());
1044 first_t.set_pulse (0.0);
1045 first_t.set_position_lock_style (AudioTime);
1046 recompute_map (_metrics);
1050 PropertyChanged (PropertyChange ());
1054 TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute)
1056 const MeterSection& prev_m = meter_section_at_frame_locked (_metrics, frame - 1);
1057 const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1058 TempoSection* mlt = 0;
1060 if (pls == AudioTime) {
1061 /* add meter-locked tempo */
1062 mlt = add_tempo_locked (tempo_at_frame_locked (_metrics, frame), pulse, frame, TempoSection::Ramp, AudioTime, true, true);
1070 MeterSection* new_meter = new MeterSection (pulse, frame, beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls);
1071 bool solved = false;
1073 do_insert (new_meter);
1077 if (pls == AudioTime) {
1078 solved = solve_map_frame (_metrics, new_meter, frame);
1080 solved = solve_map_bbt (_metrics, new_meter, where);
1081 /* required due to resetting the pulse of meter-locked tempi above.
1082 Arguably solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
1083 but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
1085 recompute_map (_metrics);
1089 if (!solved && recompute) {
1090 remove_meter_locked (*new_meter);
1098 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1100 Tempo newtempo (beats_per_minute, note_type);
1103 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1104 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1109 Glib::Threads::RWLock::WriterLock lm (lock);
1110 *((Tempo*) t) = newtempo;
1111 recompute_map (_metrics);
1113 PropertyChanged (PropertyChange ());
1120 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1122 Tempo newtempo (beats_per_minute, note_type);
1125 TempoSection* first;
1126 Metrics::iterator i;
1128 /* find the TempoSection immediately preceding "where"
1131 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1133 if ((*i)->frame() > where) {
1139 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1152 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1162 Glib::Threads::RWLock::WriterLock lm (lock);
1163 /* cannot move the first tempo section */
1164 *((Tempo*)prev) = newtempo;
1165 recompute_map (_metrics);
1168 PropertyChanged (PropertyChange ());
1172 TempoMap::first_meter () const
1174 const MeterSection *m = 0;
1176 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1177 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1182 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1183 abort(); /*NOTREACHED*/
1188 TempoMap::first_meter ()
1190 MeterSection *m = 0;
1192 /* CALLER MUST HOLD LOCK */
1194 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1195 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1200 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1201 abort(); /*NOTREACHED*/
1206 TempoMap::first_tempo () const
1208 const TempoSection *t = 0;
1210 /* CALLER MUST HOLD LOCK */
1212 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1213 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1217 if (!t->movable()) {
1223 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1224 abort(); /*NOTREACHED*/
1229 TempoMap::first_tempo ()
1231 TempoSection *t = 0;
1233 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1234 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1238 if (!t->movable()) {
1244 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1245 abort(); /*NOTREACHED*/
1249 TempoMap::recompute_tempos (Metrics& metrics)
1251 TempoSection* prev_t = 0;
1253 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1256 if ((*i)->is_tempo()) {
1257 t = static_cast<TempoSection*> (*i);
1261 if (!t->movable()) {
1269 if (t->position_lock_style() == AudioTime) {
1270 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1271 if (!t->locked_to_meter()) {
1272 t->set_pulse (prev_t->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
1276 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1277 t->set_frame (prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
1284 prev_t->set_c_func (0.0);
1287 /* tempos must be positioned correctly.
1288 the current approach is to use a meter's bbt time as its base position unit.
1289 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1290 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1293 TempoMap::recompute_meters (Metrics& metrics)
1295 MeterSection* meter = 0;
1296 MeterSection* prev_m = 0;
1298 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1299 if (!(*mi)->is_tempo()) {
1300 meter = static_cast<MeterSection*> (*mi);
1301 if (meter->position_lock_style() == AudioTime) {
1303 pair<double, BBT_Time> b_bbt;
1304 TempoSection* meter_locked_tempo = 0;
1305 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1307 if ((*ii)->is_tempo()) {
1308 t = static_cast<TempoSection*> (*ii);
1309 if ((t->locked_to_meter() || !t->movable()) && t->frame() == meter->frame()) {
1310 meter_locked_tempo = t;
1317 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1318 if (beats + prev_m->beat() != meter->beat()) {
1319 /* reordering caused a bbt change */
1320 b_bbt = make_pair (beats + prev_m->beat()
1321 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1322 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1324 } else if (meter->movable()) {
1325 b_bbt = make_pair (meter->beat(), meter->bbt());
1326 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1329 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1331 if (meter_locked_tempo) {
1332 meter_locked_tempo->set_pulse (pulse);
1334 meter->set_beat (b_bbt);
1335 meter->set_pulse (pulse);
1340 pair<double, BBT_Time> b_bbt;
1342 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1343 if (beats + prev_m->beat() != meter->beat()) {
1344 /* reordering caused a bbt change */
1345 b_bbt = make_pair (beats + prev_m->beat()
1346 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1348 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1350 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1352 /* shouldn't happen - the first is audio-locked */
1353 pulse = pulse_at_beat_locked (metrics, meter->beat());
1354 b_bbt = make_pair (meter->beat(), meter->bbt());
1357 meter->set_beat (b_bbt);
1358 meter->set_pulse (pulse);
1359 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1368 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1370 /* CALLER MUST HOLD WRITE LOCK */
1374 /* we will actually stop once we hit
1381 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1384 /* silly call from Session::process() during startup
1389 recompute_tempos (metrics);
1390 recompute_meters (metrics);
1394 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1396 Glib::Threads::RWLock::ReaderLock lm (lock);
1397 TempoMetric m (first_meter(), first_tempo());
1399 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1400 at something, because we insert the default tempo and meter during
1401 TempoMap construction.
1403 now see if we can find better candidates.
1406 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1408 if ((*i)->frame() > frame) {
1422 /* XX meters only */
1424 TempoMap::metric_at (BBT_Time bbt) const
1426 Glib::Threads::RWLock::ReaderLock lm (lock);
1427 TempoMetric m (first_meter(), first_tempo());
1429 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1430 at something, because we insert the default tempo and meter during
1431 TempoMap construction.
1433 now see if we can find better candidates.
1436 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1438 if (!(*i)->is_tempo()) {
1439 mw = static_cast<MeterSection*> (*i);
1440 BBT_Time section_start (mw->bbt());
1442 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1454 TempoMap::beat_at_frame (const framecnt_t& frame) const
1456 Glib::Threads::RWLock::ReaderLock lm (lock);
1457 return beat_at_frame_locked (_metrics, frame);
1460 /* meter / tempo section based */
1462 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1464 const TempoSection& ts = tempo_section_at_frame_locked (metrics, frame);
1465 MeterSection* prev_m = 0;
1466 MeterSection* next_m = 0;
1468 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1469 if (!(*i)->is_tempo()) {
1470 if (prev_m && (*i)->frame() > frame) {
1471 next_m = static_cast<MeterSection*> (*i);
1474 prev_m = static_cast<MeterSection*> (*i);
1477 if (frame < prev_m->frame()) {
1480 const double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
1482 /* audio locked meters fake their beat */
1483 if (next_m && next_m->beat() < beat) {
1484 return next_m->beat();
1491 TempoMap::frame_at_beat (const double& beat) const
1493 Glib::Threads::RWLock::ReaderLock lm (lock);
1494 return frame_at_beat_locked (_metrics, beat);
1497 /* meter & tempo section based */
1499 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1501 MeterSection* prev_m = 0;
1502 TempoSection* prev_t = 0;
1506 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1507 if (!(*i)->is_tempo()) {
1508 m = static_cast<MeterSection*> (*i);
1509 if (prev_m && m->beat() > beat) {
1518 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1519 if ((*i)->is_tempo()) {
1520 t = static_cast<TempoSection*> (*i);
1521 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1529 return prev_t->frame_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse(), _frame_rate);
1533 TempoMap::tempo_at_frame (const framepos_t& frame) const
1535 Glib::Threads::RWLock::ReaderLock lm (lock);
1536 return tempo_at_frame_locked (_metrics, frame);
1540 TempoMap::tempo_at_frame_locked (const Metrics& metrics, const framepos_t& frame) const
1542 TempoSection* prev_t = 0;
1546 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1547 if ((*i)->is_tempo()) {
1548 t = static_cast<TempoSection*> (*i);
1552 if ((prev_t) && t->frame() > frame) {
1553 /* t is the section past frame */
1554 const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
1555 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
1562 const double ret = prev_t->beats_per_minute();
1563 const Tempo ret_tempo (ret, prev_t->note_type ());
1568 /** returns the frame at which the supplied tempo occurs, or
1569 * the frame of the last tempo section (search exhausted)
1570 * only the position of the first occurence will be returned
1574 TempoMap::frame_at_tempo (const Tempo& tempo) const
1576 Glib::Threads::RWLock::ReaderLock lm (lock);
1577 return frame_at_tempo_locked (_metrics, tempo);
1582 TempoMap::frame_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1584 TempoSection* prev_t = 0;
1585 const double tempo_ppm = tempo.beats_per_minute() / tempo.note_type();
1587 Metrics::const_iterator i;
1589 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1591 if ((*i)->is_tempo()) {
1592 t = static_cast<TempoSection*> (*i);
1598 const double t_ppm = t->beats_per_minute() / t->note_type();
1600 if (t_ppm == tempo_ppm) {
1605 const double prev_t_ppm = prev_t->beats_per_minute() / prev_t->note_type();
1607 if ((t_ppm > tempo_ppm && prev_t_ppm < tempo_ppm) || (t_ppm < tempo_ppm && prev_t_ppm > tempo_ppm)) {
1608 return prev_t->frame_at_tempo (tempo_ppm, prev_t->pulse(), _frame_rate);
1615 return prev_t->frame();
1618 /** more precise than doing tempo_at_frame (frame_at_beat (b)),
1619 * as there is no intermediate frame rounding.
1622 TempoMap::tempo_at_beat (const double& beat) const
1624 Glib::Threads::RWLock::ReaderLock lm (lock);
1625 const MeterSection* prev_m = &meter_section_at_beat_locked (_metrics, beat);
1626 const TempoSection* prev_t = &tempo_section_at_beat_locked (_metrics, beat);
1627 const double note_type = prev_t->note_type();
1629 return Tempo (prev_t->tempo_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse()) * note_type, note_type);
1633 TempoMap::pulse_at_beat (const double& beat) const
1635 Glib::Threads::RWLock::ReaderLock lm (lock);
1636 return pulse_at_beat_locked (_metrics, beat);
1640 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1642 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1644 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1648 TempoMap::beat_at_pulse (const double& pulse) const
1650 Glib::Threads::RWLock::ReaderLock lm (lock);
1651 return beat_at_pulse_locked (_metrics, pulse);
1655 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1657 MeterSection* prev_m = 0;
1659 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1661 if (!(*i)->is_tempo()) {
1662 m = static_cast<MeterSection*> (*i);
1663 if (prev_m && m->pulse() > pulse) {
1664 if (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > m->beat()) {
1672 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1677 TempoMap::pulse_at_frame (const framepos_t& frame) const
1679 Glib::Threads::RWLock::ReaderLock lm (lock);
1680 return pulse_at_frame_locked (_metrics, frame);
1683 /* tempo section based */
1685 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framepos_t& frame) const
1687 /* HOLD (at least) THE READER LOCK */
1688 TempoSection* prev_t = 0;
1690 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1692 if ((*i)->is_tempo()) {
1693 t = static_cast<TempoSection*> (*i);
1697 if (prev_t && t->frame() > frame) {
1698 /*the previous ts is the one containing the frame */
1699 const double ret = prev_t->pulse_at_frame (frame, _frame_rate);
1706 /* treated as constant for this ts */
1707 const double pulses_in_section = (frame - prev_t->frame()) / prev_t->frames_per_pulse (_frame_rate);
1709 return pulses_in_section + prev_t->pulse();
1713 TempoMap::frame_at_pulse (const double& pulse) const
1715 Glib::Threads::RWLock::ReaderLock lm (lock);
1716 return frame_at_pulse_locked (_metrics, pulse);
1719 /* tempo section based */
1721 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1723 /* HOLD THE READER LOCK */
1725 const TempoSection* prev_t = 0;
1727 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1730 if ((*i)->is_tempo()) {
1731 t = static_cast<TempoSection*> (*i);
1735 if (prev_t && t->pulse() > pulse) {
1736 return prev_t->frame_at_pulse (pulse, _frame_rate);
1742 /* must be treated as constant, irrespective of _type */
1743 double const dtime = (pulse - prev_t->pulse()) * prev_t->frames_per_pulse (_frame_rate);
1745 return (framecnt_t) floor (dtime) + prev_t->frame();
1749 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
1751 Glib::Threads::RWLock::ReaderLock lm (lock);
1752 return beat_at_bbt_locked (_metrics, bbt);
1757 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1759 /* CALLER HOLDS READ LOCK */
1761 MeterSection* prev_m = 0;
1763 /* because audio-locked meters have 'fake' integral beats,
1764 there is no pulse offset here.
1768 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1769 if (!(*i)->is_tempo()) {
1770 m = static_cast<MeterSection*> (*i);
1772 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1773 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1781 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1782 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1783 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1789 TempoMap::bbt_at_beat (const double& beats)
1791 Glib::Threads::RWLock::ReaderLock lm (lock);
1792 return bbt_at_beat_locked (_metrics, beats);
1796 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
1798 /* CALLER HOLDS READ LOCK */
1799 MeterSection* prev_m = 0;
1800 const double beats = max (0.0, b);
1802 MeterSection* m = 0;
1804 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1805 if (!(*i)->is_tempo()) {
1806 m = static_cast<MeterSection*> (*i);
1808 if (m->beat() > beats) {
1809 /* this is the meter after the one our beat is on*/
1818 const double beats_in_ms = beats - prev_m->beat();
1819 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1820 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1821 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1822 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1826 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1827 ret.beats = (uint32_t) floor (remaining_beats);
1828 ret.bars = total_bars;
1830 /* 0 0 0 to 1 1 0 - based mapping*/
1834 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1836 ret.ticks -= BBT_Time::ticks_per_beat;
1839 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1848 TempoMap::pulse_at_bbt (const Timecode::BBT_Time& bbt)
1850 Glib::Threads::RWLock::ReaderLock lm (lock);
1852 return pulse_at_bbt_locked (_metrics, bbt);
1856 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1858 /* CALLER HOLDS READ LOCK */
1860 MeterSection* prev_m = 0;
1862 /* because audio-locked meters have 'fake' integral beats,
1863 there is no pulse offset here.
1867 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1868 if (!(*i)->is_tempo()) {
1869 m = static_cast<MeterSection*> (*i);
1871 if (m->bbt().bars > bbt.bars) {
1879 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1880 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
1881 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
1887 TempoMap::bbt_at_pulse (const double& pulse)
1889 Glib::Threads::RWLock::ReaderLock lm (lock);
1891 return bbt_at_pulse_locked (_metrics, pulse);
1895 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1897 MeterSection* prev_m = 0;
1899 MeterSection* m = 0;
1901 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1903 if (!(*i)->is_tempo()) {
1904 m = static_cast<MeterSection*> (*i);
1907 double const pulses_to_m = m->pulse() - prev_m->pulse();
1908 if (prev_m->pulse() + pulses_to_m > pulse) {
1909 /* this is the meter after the one our beat is on*/
1918 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1919 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1920 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1921 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1922 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1926 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1927 ret.beats = (uint32_t) floor (remaining_beats);
1928 ret.bars = total_bars;
1930 /* 0 0 0 to 1 1 0 mapping*/
1934 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1936 ret.ticks -= BBT_Time::ticks_per_beat;
1939 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1948 TempoMap::bbt_at_frame (framepos_t frame)
1955 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1958 Glib::Threads::RWLock::ReaderLock lm (lock);
1960 return bbt_at_frame_locked (_metrics, frame);
1964 TempoMap::bbt_at_frame_rt (framepos_t frame)
1966 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
1969 throw std::logic_error ("TempoMap::bbt_time_rt() could not lock tempo map");
1972 return bbt_at_frame_locked (_metrics, frame);
1976 TempoMap::bbt_at_frame_locked (const Metrics& metrics, const framepos_t& frame) const
1983 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1987 const TempoSection& ts = tempo_section_at_frame_locked (metrics, frame);
1988 MeterSection* prev_m = 0;
1989 MeterSection* next_m = 0;
1993 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1994 if (!(*i)->is_tempo()) {
1995 m = static_cast<MeterSection*> (*i);
1996 if (prev_m && m->frame() > frame) {
2004 double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
2006 /* handle frame before first meter */
2007 if (frame < prev_m->frame()) {
2010 /* audio locked meters fake their beat */
2011 if (next_m && next_m->beat() < beat) {
2012 beat = next_m->beat();
2015 beat = max (0.0, beat);
2017 const double beats_in_ms = beat - prev_m->beat();
2018 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2019 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2020 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2021 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2025 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2026 ret.beats = (uint32_t) floor (remaining_beats);
2027 ret.bars = total_bars;
2029 /* 0 0 0 to 1 1 0 - based mapping*/
2033 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2035 ret.ticks -= BBT_Time::ticks_per_beat;
2038 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2047 TempoMap::frame_at_bbt (const BBT_Time& bbt)
2050 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
2054 if (bbt.beats < 1) {
2055 throw std::logic_error ("beats are counted from one");
2057 Glib::Threads::RWLock::ReaderLock lm (lock);
2059 return frame_at_bbt_locked (_metrics, bbt);
2062 /* meter & tempo section based */
2064 TempoMap::frame_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2066 /* HOLD THE READER LOCK */
2068 const framepos_t ret = frame_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2073 TempoMap::check_solved (const Metrics& metrics) const
2075 TempoSection* prev_t = 0;
2076 MeterSection* prev_m = 0;
2078 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2081 if ((*i)->is_tempo()) {
2082 t = static_cast<TempoSection*> (*i);
2087 /* check ordering */
2088 if ((t->frame() <= prev_t->frame()) || (t->pulse() <= prev_t->pulse())) {
2092 /* precision check ensures tempo and frames align.*/
2093 if (t->frame() != prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate)) {
2094 if (!t->locked_to_meter()) {
2099 /* gradient limit - who knows what it should be?
2100 things are also ok (if a little chaotic) without this
2102 if (fabs (prev_t->c_func()) > 1000.0) {
2103 //std::cout << "c : " << prev_t->c_func() << std::endl;
2110 if (!(*i)->is_tempo()) {
2111 m = static_cast<MeterSection*> (*i);
2112 if (prev_m && m->position_lock_style() == AudioTime) {
2113 const TempoSection* t = &tempo_section_at_frame_locked (metrics, m->frame() - 1);
2114 const double nascent_m_pulse = ((m->beat() - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse();
2115 const framepos_t nascent_m_frame = t->frame_at_pulse (nascent_m_pulse, _frame_rate);
2117 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
2131 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
2133 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2135 if ((*i)->is_tempo()) {
2136 t = static_cast<TempoSection*> (*i);
2137 if (!t->movable()) {
2138 t->set_active (true);
2141 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
2142 t->set_active (false);
2144 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
2145 t->set_active (true);
2146 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
2155 TempoMap::solve_map_frame (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
2157 TempoSection* prev_t = 0;
2158 TempoSection* section_prev = 0;
2159 framepos_t first_m_frame = 0;
2161 /* can't move a tempo before the first meter */
2162 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2164 if (!(*i)->is_tempo()) {
2165 m = static_cast<MeterSection*> (*i);
2166 if (!m->movable()) {
2167 first_m_frame = m->frame();
2172 if (section->movable() && frame <= first_m_frame) {
2176 section->set_active (true);
2177 section->set_frame (frame);
2179 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2181 if ((*i)->is_tempo()) {
2182 t = static_cast<TempoSection*> (*i);
2189 section_prev = prev_t;
2190 if (t->locked_to_meter()) {
2195 if (t->position_lock_style() == MusicTime) {
2196 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
2197 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
2199 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
2200 if (!t->locked_to_meter()) {
2201 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
2210 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
2211 if (!section->locked_to_meter()) {
2212 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
2217 recompute_tempos (imaginary);
2219 if (check_solved (imaginary)) {
2222 dunp (imaginary, std::cout);
2226 MetricSectionFrameSorter fcmp;
2227 imaginary.sort (fcmp);
2229 recompute_tempos (imaginary);
2231 if (check_solved (imaginary)) {
2239 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2241 TempoSection* prev_t = 0;
2242 TempoSection* section_prev = 0;
2244 section->set_pulse (pulse);
2246 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2248 if ((*i)->is_tempo()) {
2249 t = static_cast<TempoSection*> (*i);
2253 if (!t->movable()) {
2260 section_prev = prev_t;
2263 if (t->position_lock_style() == MusicTime) {
2264 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
2265 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
2267 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
2268 if (!t->locked_to_meter()) {
2269 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
2278 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
2279 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
2283 recompute_tempos (imaginary);
2285 if (check_solved (imaginary)) {
2288 dunp (imaginary, std::cout);
2292 MetricSectionSorter cmp;
2293 imaginary.sort (cmp);
2295 recompute_tempos (imaginary);
2297 * XX need a restriction here, but only for this case,
2298 * as audio locked tempos don't interact in the same way.
2300 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2302 * |50 bpm |250 bpm |60 bpm
2303 * drag 250 to the pulse after 60->
2304 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2306 if (check_solved (imaginary)) {
2314 TempoMap::solve_map_frame (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
2316 /* disallow moving first meter past any subsequent one, and any movable meter before the first one */
2317 const MeterSection* other = &meter_section_at_frame_locked (imaginary, frame);
2318 if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->frame() >= frame)) {
2322 if (!section->movable()) {
2323 /* lock the first tempo to our first meter */
2324 if (!set_active_tempos (imaginary, frame)) {
2329 TempoSection* meter_locked_tempo = 0;
2331 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2333 if ((*ii)->is_tempo()) {
2334 t = static_cast<TempoSection*> (*ii);
2335 if ((t->locked_to_meter() || !t->movable()) && t->frame() == section->frame()) {
2336 meter_locked_tempo = t;
2342 if (!meter_locked_tempo) {
2346 MeterSection* prev_m = 0;
2348 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2349 bool solved = false;
2351 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2353 if (!(*i)->is_tempo()) {
2354 m = static_cast<MeterSection*> (*i);
2356 if (prev_m && section->movable()) {
2357 const double beats = (pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor();
2358 if (beats + prev_m->beat() < section->beat()) {
2359 /* set the frame/pulse corresponding to its musical position,
2360 * as an earlier time than this has been requested.
2362 const double new_pulse = ((section->beat() - prev_m->beat())
2363 / prev_m->note_divisor()) + prev_m->pulse();
2365 const framepos_t smallest_frame = frame_at_pulse_locked (future_map, new_pulse);
2367 if ((solved = solve_map_frame (future_map, tempo_copy, smallest_frame))) {
2368 meter_locked_tempo->set_pulse (new_pulse);
2369 solve_map_frame (imaginary, meter_locked_tempo, smallest_frame);
2370 section->set_frame (smallest_frame);
2371 section->set_pulse (new_pulse);
2376 Metrics::const_iterator d = future_map.begin();
2377 while (d != future_map.end()) {
2386 /* all is ok. set section's locked tempo if allowed.
2387 possibly disallowed if there is an adjacent audio-locked tempo.
2388 XX this check could possibly go. its never actually happened here.
2390 MeterSection* meter_copy = const_cast<MeterSection*> (&meter_section_at_frame_locked (future_map, section->frame()));
2391 meter_copy->set_frame (frame);
2393 if ((solved = solve_map_frame (future_map, tempo_copy, frame))) {
2394 section->set_frame (frame);
2395 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2396 / prev_m->note_divisor()) + prev_m->pulse());
2397 solve_map_frame (imaginary, meter_locked_tempo, frame);
2402 Metrics::const_iterator d = future_map.begin();
2403 while (d != future_map.end()) {
2413 /* not movable (first meter atm) */
2415 tempo_copy->set_frame (frame);
2416 tempo_copy->set_pulse (0.0);
2418 if ((solved = solve_map_frame (future_map, tempo_copy, frame))) {
2419 section->set_frame (frame);
2420 meter_locked_tempo->set_frame (frame);
2421 meter_locked_tempo->set_pulse (0.0);
2422 solve_map_frame (imaginary, meter_locked_tempo, frame);
2427 Metrics::const_iterator d = future_map.begin();
2428 while (d != future_map.end()) {
2437 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2438 section->set_beat (b_bbt);
2439 section->set_pulse (0.0);
2449 MetricSectionFrameSorter fcmp;
2450 imaginary.sort (fcmp);
2452 recompute_meters (imaginary);
2458 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2460 /* disallow setting section to an existing meter's bbt */
2461 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2463 if (!(*i)->is_tempo()) {
2464 m = static_cast<MeterSection*> (*i);
2465 if (m != section && m->bbt().bars == when.bars) {
2471 MeterSection* prev_m = 0;
2472 MeterSection* section_prev = 0;
2474 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2476 if (!(*i)->is_tempo()) {
2477 m = static_cast<MeterSection*> (*i);
2478 pair<double, BBT_Time> b_bbt;
2479 double new_pulse = 0.0;
2481 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2482 section_prev = prev_m;
2483 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2484 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2485 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2487 section->set_beat (b_bbt);
2488 section->set_pulse (pulse);
2489 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2494 if (m->position_lock_style() == AudioTime) {
2495 TempoSection* meter_locked_tempo = 0;
2497 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2499 if ((*ii)->is_tempo()) {
2500 t = static_cast<TempoSection*> (*ii);
2501 if ((t->locked_to_meter() || !t->movable()) && t->frame() == m->frame()) {
2502 meter_locked_tempo = t;
2508 if (!meter_locked_tempo) {
2513 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2515 if (beats + prev_m->beat() != m->beat()) {
2516 /* tempo/ meter change caused a change in beat (bar). */
2517 b_bbt = make_pair (beats + prev_m->beat()
2518 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2519 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2520 } else if (m->movable()) {
2521 b_bbt = make_pair (m->beat(), m->bbt());
2522 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2525 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2528 meter_locked_tempo->set_pulse (new_pulse);
2529 m->set_beat (b_bbt);
2530 m->set_pulse (new_pulse);
2534 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2535 if (beats + prev_m->beat() != m->beat()) {
2536 /* tempo/ meter change caused a change in beat (bar). */
2537 b_bbt = make_pair (beats + prev_m->beat()
2538 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2540 b_bbt = make_pair (beats + prev_m->beat()
2543 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2544 m->set_beat (b_bbt);
2545 m->set_pulse (new_pulse);
2546 m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2553 if (!section_prev) {
2555 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
2556 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2557 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
2559 section->set_beat (b_bbt);
2560 section->set_pulse (pulse);
2561 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2564 MetricSectionSorter cmp;
2565 imaginary.sort (cmp);
2567 recompute_meters (imaginary);
2572 /** places a copy of _metrics into copy and returns a pointer
2573 * to section's equivalent in copy.
2576 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
2578 TempoSection* ret = 0;
2580 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2583 if ((*i)->is_tempo()) {
2584 t = static_cast<TempoSection*> (*i);
2586 ret = new TempoSection (*t);
2587 copy.push_back (ret);
2591 TempoSection* cp = new TempoSection (*t);
2592 copy.push_back (cp);
2594 if (!(*i)->is_tempo()) {
2595 m = static_cast<MeterSection *> (*i);
2596 MeterSection* cp = new MeterSection (*m);
2597 copy.push_back (cp);
2605 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
2607 MeterSection* ret = 0;
2609 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2612 if ((*i)->is_tempo()) {
2613 t = static_cast<TempoSection*> (*i);
2614 TempoSection* cp = new TempoSection (*t);
2615 copy.push_back (cp);
2618 if (!(*i)->is_tempo()) {
2619 m = static_cast<MeterSection *> (*i);
2621 ret = new MeterSection (*m);
2622 copy.push_back (ret);
2625 MeterSection* cp = new MeterSection (*m);
2626 copy.push_back (cp);
2633 /** answers the question "is this a valid beat position for this tempo section?".
2634 * it returns true if the tempo section can be moved to the requested bbt position,
2635 * leaving the tempo map in a solved state.
2636 * @param section the tempo section to be moved
2637 * @param bbt the requested new position for the tempo section
2638 * @return true if the tempo section can be moved to the position, otherwise false.
2641 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2644 TempoSection* tempo_copy = 0;
2647 Glib::Threads::RWLock::ReaderLock lm (lock);
2648 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
2654 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
2656 Metrics::const_iterator d = copy.begin();
2657 while (d != copy.end()) {
2666 * 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,
2667 * taking any possible reordering as a consequence of this into account.
2668 * @param section - the section to be altered
2669 * @param bbt - the bbt where the altered tempo will fall
2670 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
2672 pair<double, framepos_t>
2673 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
2676 pair<double, framepos_t> ret = make_pair (0.0, 0);
2678 Glib::Threads::RWLock::ReaderLock lm (lock);
2680 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
2682 const double beat = beat_at_bbt_locked (future_map, bbt);
2684 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
2685 ret.first = tempo_copy->pulse();
2686 ret.second = tempo_copy->frame();
2688 ret.first = section->pulse();
2689 ret.second = section->frame();
2692 Metrics::const_iterator d = future_map.begin();
2693 while (d != future_map.end()) {
2701 TempoMap::gui_move_tempo (TempoSection* ts, const framepos_t& frame, const int& sub_num)
2704 bool was_musical = ts->position_lock_style() == MusicTime;
2706 if (sub_num == 0 && was_musical) {
2707 /* if we're not snapping to music,
2708 AudioTime and MusicTime may be treated identically.
2710 ts->set_position_lock_style (AudioTime);
2713 if (ts->position_lock_style() == MusicTime) {
2715 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
2716 Glib::Threads::RWLock::WriterLock lm (lock);
2717 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2718 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
2719 double pulse = pulse_at_beat_locked (future_map, beat);
2721 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
2722 solve_map_pulse (_metrics, ts, pulse);
2723 recompute_meters (_metrics);
2730 Glib::Threads::RWLock::WriterLock lm (lock);
2731 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2732 if (solve_map_frame (future_map, tempo_copy, frame)) {
2733 solve_map_frame (_metrics, ts, frame);
2734 recompute_meters (_metrics);
2739 if (sub_num == 0 && was_musical) {
2740 ts->set_position_lock_style (MusicTime);
2743 Metrics::const_iterator d = future_map.begin();
2744 while (d != future_map.end()) {
2749 MetricPositionChanged (); // Emit Signal
2753 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
2757 if (ms->position_lock_style() == AudioTime) {
2760 Glib::Threads::RWLock::WriterLock lm (lock);
2761 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2763 if (solve_map_frame (future_map, copy, frame)) {
2764 solve_map_frame (_metrics, ms, frame);
2765 recompute_tempos (_metrics);
2770 Glib::Threads::RWLock::WriterLock lm (lock);
2771 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2773 const double beat = beat_at_frame_locked (_metrics, frame);
2774 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
2776 if (solve_map_bbt (future_map, copy, bbt)) {
2777 solve_map_bbt (_metrics, ms, bbt);
2778 recompute_tempos (_metrics);
2783 Metrics::const_iterator d = future_map.begin();
2784 while (d != future_map.end()) {
2789 MetricPositionChanged (); // Emit Signal
2793 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2796 bool can_solve = false;
2798 Glib::Threads::RWLock::WriterLock lm (lock);
2799 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2800 tempo_copy->set_beats_per_minute (bpm.beats_per_minute());
2801 recompute_tempos (future_map);
2803 if (check_solved (future_map)) {
2804 ts->set_beats_per_minute (bpm.beats_per_minute());
2805 recompute_map (_metrics);
2810 Metrics::const_iterator d = future_map.begin();
2811 while (d != future_map.end()) {
2816 MetricPositionChanged (); // Emit Signal
2822 TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame, const double& pulse)
2825 Ts (future prev_t) Tnext
2828 |----------|----------
2835 Glib::Threads::RWLock::WriterLock lm (lock);
2841 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
2842 TempoSection* prev_to_prev_t = 0;
2843 const frameoffset_t fr_off = end_frame - frame;
2845 if (prev_t && prev_t->pulse() > 0.0) {
2846 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_frame_locked (future_map, prev_t->frame() - 1));
2849 TempoSection* next_t = 0;
2850 for (Metrics::iterator i = future_map.begin(); i != future_map.end(); ++i) {
2851 TempoSection* t = 0;
2852 if ((*i)->is_tempo()) {
2853 t = static_cast<TempoSection*> (*i);
2854 if (t->frame() > ts->frame()) {
2860 /* minimum allowed measurement distance in frames */
2861 const framepos_t min_dframe = 2;
2863 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
2864 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
2866 double contribution = 0.0;
2868 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2869 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
2872 const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
2874 const double start_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
2875 const double end_pulse = prev_t->pulse_at_frame (end_frame, _frame_rate);
2879 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
2881 if (prev_t->position_lock_style() == MusicTime) {
2882 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2883 if (frame > prev_to_prev_t->frame() + min_dframe && (frame + prev_t_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
2885 new_bpm = prev_t->beats_per_minute() * ((frame - prev_to_prev_t->frame())
2886 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
2888 new_bpm = prev_t->beats_per_minute();
2891 /* prev to prev is irrelevant */
2893 if (start_pulse > prev_t->pulse() && end_pulse > prev_t->pulse()) {
2894 new_bpm = prev_t->beats_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
2896 new_bpm = prev_t->beats_per_minute();
2901 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2902 if (frame > prev_to_prev_t->frame() + min_dframe && end_frame > prev_to_prev_t->frame() + min_dframe) {
2904 new_bpm = prev_t->beats_per_minute() * ((frame - prev_to_prev_t->frame())
2905 / (double) ((end_frame) - prev_to_prev_t->frame()));
2907 new_bpm = prev_t->beats_per_minute();
2910 /* prev_to_prev_t is irrelevant */
2912 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
2913 new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
2915 new_bpm = prev_t->beats_per_minute();
2921 double frame_ratio = 1.0;
2922 double pulse_ratio = 1.0;
2923 const framepos_t pulse_pos = prev_t->frame_at_pulse (pulse, _frame_rate);
2925 if (prev_to_prev_t) {
2926 if (pulse_pos > prev_to_prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_to_prev_t->frame() + min_dframe) {
2927 frame_ratio = (((pulse_pos - fr_off) - prev_to_prev_t->frame()) / (double) ((pulse_pos) - prev_to_prev_t->frame()));
2929 if (end_pulse > prev_to_prev_t->pulse() && start_pulse > prev_to_prev_t->pulse()) {
2930 pulse_ratio = ((start_pulse - prev_to_prev_t->pulse()) / (end_pulse - prev_to_prev_t->pulse()));
2933 if (pulse_pos > prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_t->frame() + min_dframe) {
2934 frame_ratio = (((pulse_pos - fr_off) - prev_t->frame()) / (double) ((pulse_pos) - prev_t->frame()));
2936 pulse_ratio = (start_pulse / end_pulse);
2938 new_bpm = prev_t->beats_per_minute() * (pulse_ratio * frame_ratio);
2941 /* don't clamp and proceed here.
2942 testing has revealed that this can go negative,
2943 which is an entirely different thing to just being too low.
2945 if (new_bpm < 0.5) {
2948 new_bpm = min (new_bpm, (double) 1000.0);
2949 prev_t->set_beats_per_minute (new_bpm);
2950 recompute_tempos (future_map);
2951 recompute_meters (future_map);
2953 if (check_solved (future_map)) {
2954 ts->set_beats_per_minute (new_bpm);
2955 recompute_tempos (_metrics);
2956 recompute_meters (_metrics);
2960 Metrics::const_iterator d = future_map.begin();
2961 while (d != future_map.end()) {
2966 MetricPositionChanged (); // Emit Signal
2970 TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t& sub_num)
2972 Glib::Threads::RWLock::ReaderLock lm (lock);
2974 return exact_beat_at_frame_locked (_metrics, frame, sub_num);
2978 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t& sub_num)
2980 double beat = beat_at_frame_locked (metrics, frame);
2982 beat = floor (beat) + (floor (((beat - floor (beat)) * (double) sub_num) + 0.5) / sub_num);
2983 } else if (sub_num == 1) {
2985 beat = floor (beat + 0.5);
2986 } else if (sub_num == -1) {
2988 Timecode::BBT_Time bbt = bbt_at_beat_locked (metrics, beat);
2991 beat = beat_at_bbt_locked (metrics, bbt);
2997 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2999 Glib::Threads::RWLock::ReaderLock lm (lock);
3001 const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
3002 const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
3003 const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
3005 return frame_at_beat_locked (_metrics, total_beats);
3009 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
3011 return round_to_type (fr, dir, Bar);
3015 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
3017 return round_to_type (fr, dir, Beat);
3021 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3023 Glib::Threads::RWLock::ReaderLock lm (lock);
3024 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
3025 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3026 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3028 ticks -= beats * BBT_Time::ticks_per_beat;
3031 /* round to next (or same iff dir == RoundUpMaybe) */
3033 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3035 if (mod == 0 && dir == RoundUpMaybe) {
3036 /* right on the subdivision, which is fine, so do nothing */
3038 } else if (mod == 0) {
3039 /* right on the subdivision, so the difference is just the subdivision ticks */
3040 ticks += ticks_one_subdivisions_worth;
3043 /* not on subdivision, compute distance to next subdivision */
3045 ticks += ticks_one_subdivisions_worth - mod;
3048 if (ticks >= BBT_Time::ticks_per_beat) {
3049 ticks -= BBT_Time::ticks_per_beat;
3051 } else if (dir < 0) {
3053 /* round to previous (or same iff dir == RoundDownMaybe) */
3055 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3057 if (difference == 0 && dir == RoundDownAlways) {
3058 /* right on the subdivision, but force-rounding down,
3059 so the difference is just the subdivision ticks */
3060 difference = ticks_one_subdivisions_worth;
3063 if (ticks < difference) {
3064 ticks = BBT_Time::ticks_per_beat - ticks;
3066 ticks -= difference;
3070 /* round to nearest */
3073 /* compute the distance to the previous and next subdivision */
3075 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3077 /* closer to the next subdivision, so shift forward */
3079 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3081 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3083 if (ticks > BBT_Time::ticks_per_beat) {
3085 ticks -= BBT_Time::ticks_per_beat;
3086 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3089 } else if (rem > 0) {
3091 /* closer to previous subdivision, so shift backward */
3095 /* can't go backwards past zero, so ... */
3098 /* step back to previous beat */
3100 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3101 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3103 ticks = lrint (ticks - rem);
3104 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3107 /* on the subdivision, do nothing */
3111 const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
3117 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
3119 Glib::Threads::RWLock::ReaderLock lm (lock);
3121 const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
3122 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
3127 /* find bar previous to 'frame' */
3130 return frame_at_bbt_locked (_metrics, bbt);
3132 } else if (dir > 0) {
3133 /* find bar following 'frame' */
3137 return frame_at_bbt_locked (_metrics, bbt);
3139 /* true rounding: find nearest bar */
3140 framepos_t raw_ft = frame_at_bbt_locked (_metrics, bbt);
3143 framepos_t prev_ft = frame_at_bbt_locked (_metrics, bbt);
3145 framepos_t next_ft = frame_at_bbt_locked (_metrics, bbt);
3147 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
3158 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
3159 } else if (dir > 0) {
3160 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
3162 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
3171 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
3172 framepos_t lower, framepos_t upper)
3174 Glib::Threads::RWLock::ReaderLock lm (lock);
3175 int32_t cnt = ceil (beat_at_frame_locked (_metrics, lower));
3177 /* although the map handles negative beats, bbt doesn't. */
3182 if (frame_at_beat_locked (_metrics, cnt) >= upper) {
3186 while (pos < upper) {
3187 pos = frame_at_beat_locked (_metrics, cnt);
3188 const TempoSection tempo = tempo_section_at_frame_locked (_metrics, pos);
3189 const MeterSection meter = meter_section_at_frame_locked (_metrics, pos);
3190 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
3191 points.push_back (BBTPoint (meter, tempo_at_frame_locked (_metrics, pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
3197 TempoMap::tempo_section_at_frame (framepos_t frame) const
3199 Glib::Threads::RWLock::ReaderLock lm (lock);
3200 return tempo_section_at_frame_locked (_metrics, frame);
3204 TempoMap::tempo_section_at_frame_locked (const Metrics& metrics, framepos_t frame) const
3206 TempoSection* prev = 0;
3210 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3212 if ((*i)->is_tempo()) {
3213 t = static_cast<TempoSection*> (*i);
3217 if (prev && t->frame() > frame) {
3227 abort(); /*NOTREACHED*/
3234 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3236 TempoSection* prev_t = 0;
3237 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
3241 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3242 if ((*i)->is_tempo()) {
3243 t = static_cast<TempoSection*> (*i);
3244 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
3254 /* don't use this to calculate length (the tempo is only correct for this frame).
3255 do that stuff based on the beat_at_frame and frame_at_beat api
3258 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
3260 Glib::Threads::RWLock::ReaderLock lm (lock);
3262 const TempoSection* ts_at = 0;
3263 const TempoSection* ts_after = 0;
3264 Metrics::const_iterator i;
3267 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3269 if ((*i)->is_tempo()) {
3270 t = static_cast<TempoSection*> (*i);
3274 if (ts_at && (*i)->frame() > frame) {
3283 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate) * ts_at->note_type());
3285 /* must be treated as constant tempo */
3286 return ts_at->frames_per_beat (_frame_rate);
3290 TempoMap::meter_section_at_frame_locked (const Metrics& metrics, framepos_t frame) const
3292 Metrics::const_iterator i;
3293 MeterSection* prev = 0;
3297 for (i = metrics.begin(); i != metrics.end(); ++i) {
3299 if (!(*i)->is_tempo()) {
3300 m = static_cast<MeterSection*> (*i);
3302 if (prev && (*i)->frame() > frame) {
3312 abort(); /*NOTREACHED*/
3320 TempoMap::meter_section_at_frame (framepos_t frame) const
3322 Glib::Threads::RWLock::ReaderLock lm (lock);
3323 return meter_section_at_frame_locked (_metrics, frame);
3327 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3329 MeterSection* prev_m = 0;
3331 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3333 if (!(*i)->is_tempo()) {
3334 m = static_cast<MeterSection*> (*i);
3335 if (prev_m && m->beat() > beat) {
3346 TempoMap::meter_section_at_beat (double beat) const
3348 Glib::Threads::RWLock::ReaderLock lm (lock);
3349 return meter_section_at_beat_locked (_metrics, beat);
3353 TempoMap::meter_at_frame (framepos_t frame) const
3355 TempoMetric m (metric_at (frame));
3360 TempoMap::fix_legacy_session ()
3362 MeterSection* prev_m = 0;
3363 TempoSection* prev_t = 0;
3365 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3369 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3370 if (!m->movable()) {
3371 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3375 m->set_position_lock_style (AudioTime);
3380 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
3381 + (m->bbt().beats - 1)
3382 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
3384 m->set_beat (start);
3385 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
3386 + (m->bbt().beats - 1)
3387 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
3388 m->set_pulse (start_beat / prev_m->note_divisor());
3391 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3397 if (!t->movable()) {
3400 t->set_position_lock_style (AudioTime);
3406 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
3407 + (t->legacy_bbt().beats - 1)
3408 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
3410 t->set_pulse (beat / prev_m->note_divisor());
3412 /* really shouldn't happen but.. */
3413 t->set_pulse (beat / 4.0);
3422 TempoMap::get_state ()
3424 Metrics::const_iterator i;
3425 XMLNode *root = new XMLNode ("TempoMap");
3428 Glib::Threads::RWLock::ReaderLock lm (lock);
3429 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3430 root->add_child_nocopy ((*i)->get_state());
3438 TempoMap::set_state (const XMLNode& node, int /*version*/)
3441 Glib::Threads::RWLock::WriterLock lm (lock);
3444 XMLNodeConstIterator niter;
3445 Metrics old_metrics (_metrics);
3448 nlist = node.children();
3450 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
3451 XMLNode* child = *niter;
3453 if (child->name() == TempoSection::xml_state_node_name) {
3456 TempoSection* ts = new TempoSection (*child);
3457 _metrics.push_back (ts);
3460 catch (failed_constructor& err){
3461 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3462 _metrics = old_metrics;
3466 } else if (child->name() == MeterSection::xml_state_node_name) {
3469 MeterSection* ms = new MeterSection (*child);
3470 _metrics.push_back (ms);
3473 catch (failed_constructor& err) {
3474 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3475 _metrics = old_metrics;
3481 if (niter == nlist.end()) {
3482 MetricSectionSorter cmp;
3483 _metrics.sort (cmp);
3486 /* check for legacy sessions where bbt was the base musical unit for tempo */
3487 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3489 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3490 if (t->legacy_bbt().bars != 0) {
3491 fix_legacy_session();
3498 /* check for multiple tempo/meters at the same location, which
3499 ardour2 somehow allowed.
3502 Metrics::iterator prev = _metrics.end();
3503 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3504 if (prev != _metrics.end()) {
3506 MeterSection* prev_m;
3508 TempoSection* prev_t;
3509 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
3510 if (prev_m->pulse() == ms->pulse()) {
3511 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3512 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3515 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
3516 if (prev_t->pulse() == ts->pulse()) {
3517 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3518 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3526 recompute_map (_metrics);
3529 PropertyChanged (PropertyChange ());
3535 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
3537 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
3538 const MeterSection* m;
3539 const TempoSection* t;
3540 const TempoSection* prev_t = 0;
3542 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3544 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
3545 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
3546 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
3547 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
3549 o << "previous : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
3550 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;
3553 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
3554 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
3555 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
3558 o << "------" << std::endl;
3562 TempoMap::n_tempos() const
3564 Glib::Threads::RWLock::ReaderLock lm (lock);
3567 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3568 if ((*i)->is_tempo()) {
3577 TempoMap::n_meters() const
3579 Glib::Threads::RWLock::ReaderLock lm (lock);
3582 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3583 if (!(*i)->is_tempo()) {
3592 TempoMap::insert_time (framepos_t where, framecnt_t amount)
3595 Glib::Threads::RWLock::WriterLock lm (lock);
3596 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3597 if ((*i)->frame() >= where && (*i)->movable ()) {
3598 (*i)->set_frame ((*i)->frame() + amount);
3602 /* now reset the BBT time of all metrics, based on their new
3603 * audio time. This is the only place where we do this reverse
3607 Metrics::iterator i;
3608 const MeterSection* meter;
3609 const TempoSection* tempo;
3613 meter = &first_meter ();
3614 tempo = &first_tempo ();
3619 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
3622 MetricSection* prev = 0;
3624 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3627 //TempoMetric metric (*meter, *tempo);
3628 MeterSection* ms = const_cast<MeterSection*>(meter);
3629 TempoSection* ts = const_cast<TempoSection*>(tempo);
3632 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3636 ts->set_pulse (t->pulse());
3638 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3639 ts->set_pulse (m->pulse());
3641 ts->set_frame (prev->frame());
3645 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3646 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3647 ms->set_beat (start);
3648 ms->set_pulse (m->pulse());
3650 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3654 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3655 pair<double, BBT_Time> start = make_pair (beat, bbt_at_beat_locked (_metrics, beat));
3656 ms->set_beat (start);
3657 ms->set_pulse (t->pulse());
3659 ms->set_frame (prev->frame());
3663 // metric will be at frames=0 bbt=1|1|0 by default
3664 // which is correct for our purpose
3667 // cerr << bbt << endl;
3669 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3673 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3675 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3676 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3677 bbt = bbt_at_frame_locked (_metrics, m->frame());
3679 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3685 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3686 /* round up to next beat */
3692 if (bbt.beats != 1) {
3693 /* round up to next bar */
3698 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3699 m->set_beat (start);
3700 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3702 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3704 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3705 abort(); /*NOTREACHED*/
3711 recompute_map (_metrics);
3715 PropertyChanged (PropertyChange ());
3718 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3722 std::list<MetricSection*> metric_kill_list;
3724 TempoSection* last_tempo = NULL;
3725 MeterSection* last_meter = NULL;
3726 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3727 bool meter_after = false; // is there a meter marker likewise?
3729 Glib::Threads::RWLock::WriterLock lm (lock);
3730 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3731 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3732 metric_kill_list.push_back(*i);
3733 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3736 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3740 else if ((*i)->frame() >= where) {
3741 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3742 (*i)->set_frame ((*i)->frame() - amount);
3743 if ((*i)->frame() == where) {
3744 // marker was immediately after end of range
3745 tempo_after = dynamic_cast<TempoSection*> (*i);
3746 meter_after = dynamic_cast<MeterSection*> (*i);
3752 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3753 if (last_tempo && !tempo_after) {
3754 metric_kill_list.remove(last_tempo);
3755 last_tempo->set_frame(where);
3758 if (last_meter && !meter_after) {
3759 metric_kill_list.remove(last_meter);
3760 last_meter->set_frame(where);
3764 //remove all the remaining metrics
3765 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3766 _metrics.remove(*i);
3771 recompute_map (_metrics);
3774 PropertyChanged (PropertyChange ());
3778 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3779 * pos can be -ve, if required.
3782 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3784 Glib::Threads::RWLock::ReaderLock lm (lock);
3786 return frame_at_beat_locked (_metrics, beat_at_frame_locked (_metrics, pos) + beats.to_double());
3789 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3791 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3793 Glib::Threads::RWLock::ReaderLock lm (lock);
3795 return frame_at_beat_locked (_metrics, beat_at_frame_locked (_metrics, pos) - beats.to_double());
3798 /** Add the BBT interval op to pos and return the result */
3800 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3802 Glib::Threads::RWLock::ReaderLock lm (lock);
3804 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3805 pos_bbt.ticks += op.ticks;
3806 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3808 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3810 pos_bbt.beats += op.beats;
3811 /* the meter in effect will start on the bar */
3812 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();
3813 while (pos_bbt.beats >= divisions_per_bar + 1) {
3815 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3816 pos_bbt.beats -= divisions_per_bar;
3818 pos_bbt.bars += op.bars;
3820 return frame_at_bbt_locked (_metrics, pos_bbt);
3823 /** Count the number of beats that are equivalent to distance when going forward,
3827 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3829 Glib::Threads::RWLock::ReaderLock lm (lock);
3831 return Evoral::Beats (beat_at_frame_locked (_metrics, pos + distance) - beat_at_frame_locked (_metrics, pos));
3835 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3841 operator<< (std::ostream& o, const Meter& m) {
3842 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3846 operator<< (std::ostream& o, const Tempo& t) {
3847 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3851 operator<< (std::ostream& o, const MetricSection& section) {
3853 o << "MetricSection @ " << section.frame() << ' ';
3855 const TempoSection* ts;
3856 const MeterSection* ms;
3858 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3859 o << *((const Tempo*) ts);
3860 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3861 o << *((const Meter*) ms);