2 Copyright (C) 2000-2002 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include <glibmm/threads.h>
28 #include "pbd/enumwriter.h"
29 #include "pbd/xml++.h"
30 #include "evoral/Beats.hpp"
32 #include "ardour/debug.h"
33 #include "ardour/lmath.h"
34 #include "ardour/tempo.h"
40 using namespace ARDOUR;
43 using Timecode::BBT_Time;
45 /* _default tempo is 4/4 qtr=120 */
47 Meter TempoMap::_default_meter (4.0, 4.0);
48 Tempo TempoMap::_default_tempo (120.0);
50 /***********************************************************************/
53 Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
55 /* This is tempo- and meter-sensitive. The number it returns
56 is based on the interval between any two lines in the
57 grid that is constructed from tempo and meter sections.
59 The return value IS NOT interpretable in terms of "beats".
62 return (60.0 * sr) / (tempo.beats_per_minute() * (_note_type/tempo.note_type()));
66 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
68 return frames_per_grid (tempo, sr) * _divisions_per_bar;
71 /***********************************************************************/
73 const string TempoSection::xml_state_node_name = "Tempo";
75 TempoSection::TempoSection (const XMLNode& node)
76 : MetricSection (0.0, 0, MusicTime)
77 , Tempo (TempoMap::default_tempo())
80 , _locked_to_meter (false)
82 XMLProperty const * prop;
88 _legacy_bbt = BBT_Time (0, 0, 0);
90 if ((prop = node.property ("start")) != 0) {
91 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
95 /* legacy session - start used to be in bbt*/
98 info << _("Legacy session detected. TempoSection XML node will be altered.") << endmsg;
102 if ((prop = node.property ("pulse")) != 0) {
103 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
104 error << _("TempoSection XML node has an illegal \"pulse\" value") << endmsg;
110 if ((prop = node.property ("frame")) != 0) {
111 if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) {
112 error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg;
118 if ((prop = node.property ("beats-per-minute")) == 0) {
119 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
120 throw failed_constructor();
123 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
124 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
125 throw failed_constructor();
128 if ((prop = node.property ("note-type")) == 0) {
129 /* older session, make note type be quarter by default */
132 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
133 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
134 throw failed_constructor();
138 if ((prop = node.property ("movable")) == 0) {
139 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
140 throw failed_constructor();
143 set_movable (string_is_affirmative (prop->value()));
145 if ((prop = node.property ("active")) == 0) {
146 warning << _("TempoSection XML node has no \"active\" property") << endmsg;
149 set_active (string_is_affirmative (prop->value()));
152 if ((prop = node.property ("tempo-type")) == 0) {
155 _type = Type (string_2_enum (prop->value(), _type));
158 if ((prop = node.property ("lock-style")) == 0) {
160 set_position_lock_style (MusicTime);
162 set_position_lock_style (AudioTime);
165 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
168 if ((prop = node.property ("locked-to-meter")) == 0) {
169 set_locked_to_meter (false);
171 set_locked_to_meter (string_is_affirmative (prop->value()));
176 TempoSection::get_state() const
178 XMLNode *root = new XMLNode (xml_state_node_name);
182 snprintf (buf, sizeof (buf), "%f", pulse());
183 root->add_property ("pulse", buf);
184 snprintf (buf, sizeof (buf), "%li", frame());
185 root->add_property ("frame", buf);
186 snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
187 root->add_property ("beats-per-minute", buf);
188 snprintf (buf, sizeof (buf), "%f", _note_type);
189 root->add_property ("note-type", buf);
190 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
191 root->add_property ("movable", buf);
192 snprintf (buf, sizeof (buf), "%s", active()?"yes":"no");
193 root->add_property ("active", buf);
194 root->add_property ("tempo-type", enum_2_string (_type));
195 root->add_property ("lock-style", enum_2_string (position_lock_style()));
196 root->add_property ("locked-to-meter", locked_to_meter()?"yes":"no");
202 TempoSection::set_type (Type type)
207 /** returns the tempo in whole pulses per minute at the zero-based (relative to session) frame.
210 TempoSection::tempo_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
213 if (_type == Constant || _c_func == 0.0) {
214 return pulses_per_minute();
217 return pulse_tempo_at_time (frame_to_minute (f - frame(), frame_rate));
220 /** returns the zero-based frame (relative to session)
221 where the tempo in whole pulses per minute occurs in this section.
222 pulse p is only used for constant tempos.
223 note that the tempo map may have multiple such values.
226 TempoSection::frame_at_tempo (const double& ppm, const double& p, const framecnt_t& frame_rate) const
228 if (_type == Constant || _c_func == 0.0) {
229 return ((p - pulse()) * frames_per_pulse (frame_rate)) + frame();
232 return minute_to_frame (time_at_pulse_tempo (ppm), frame_rate) + frame();
234 /** returns the tempo in pulses per minute at the zero-based (relative to session) beat.
237 TempoSection::tempo_at_pulse (const double& p) const
240 if (_type == Constant || _c_func == 0.0) {
241 return pulses_per_minute();
243 double const ppm = pulse_tempo_at_pulse (p - pulse());
247 /** returns the zero-based beat (relative to session)
248 where the tempo in whole pulses per minute occurs given frame f. frame f is only used for constant tempos.
249 note that the session tempo map may have multiple beats at a given tempo.
252 TempoSection::pulse_at_tempo (const double& ppm, const framepos_t& f, const framecnt_t& frame_rate) const
254 if (_type == Constant || _c_func == 0.0) {
255 double const pulses = ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
258 return pulse_at_pulse_tempo (ppm) + pulse();
261 /** returns the zero-based pulse (relative to session origin)
262 where the zero-based frame (relative to session)
266 TempoSection::pulse_at_frame (const framepos_t& f, const framecnt_t& frame_rate) const
268 if (_type == Constant || _c_func == 0.0) {
269 return ((f - frame()) / frames_per_pulse (frame_rate)) + pulse();
272 return pulse_at_time (frame_to_minute (f - frame(), frame_rate)) + pulse();
275 /** returns the zero-based frame (relative to session start frame)
276 where the zero-based pulse (relative to session start)
281 TempoSection::frame_at_pulse (const double& p, const framecnt_t& frame_rate) const
283 if (_type == Constant || _c_func == 0.0) {
284 return (framepos_t) floor ((p - pulse()) * frames_per_pulse (frame_rate)) + frame();
287 return minute_to_frame (time_at_pulse (p - pulse()), frame_rate) + frame();
295 Tt----|-----------------*|
296 Ta----|--------------|* |
302 _______________|___|____
303 time a t (next tempo)
306 Duration in beats at time a is the integral of some Tempo function.
307 In our case, the Tempo function (Tempo at time t) is
310 with function constant
315 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
316 b(t) = T0(e^(ct) - 1) / c
318 To find the time t at beat duration b, we use the inverse function of the beat function (the time function) which can be shown to be:
319 t(b) = log((c.b / T0) + 1) / c
321 The time t at which Tempo T occurs is a as above:
322 t(T) = log(T / T0) / c
324 The beat at which a Tempo T occurs is:
327 The Tempo at which beat b occurs is:
330 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
331 Our problem is that we usually don't know t.
332 We almost always know the duration in beats between this and the new section, so we need to find c in terms of the beat function.
333 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
334 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
336 By substituting our expanded t as a in the c function above, our problem is reduced to:
337 c = T0 (e^(log (Ta / T0)) - 1) / b
339 Of course the word 'beat' has been left loosely defined above.
340 In music, a beat is defined by the musical pulse (which comes from the tempo)
341 and the meter in use at a particular time (how many pulse divisions there are in one bar).
342 It would be more accurate to substitute the work 'pulse' for 'beat' above.
346 We can now store c for future time calculations.
347 If the following tempo section (the one that defines c in conjunction with this one)
348 is changed or moved, c is no longer valid.
350 The public methods are session-relative.
352 Most of this stuff is taken from this paper:
355 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
358 Zurich University of Arts
359 Institute for Computer Music and Sound Technology
361 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
366 compute this ramp's function constant using the end tempo (in whole pulses per minute)
367 and duration (pulses into global start) of some later tempo section.
370 TempoSection::compute_c_func_pulse (const double& end_bpm, const double& end_pulse, const framecnt_t& frame_rate)
372 double const log_tempo_ratio = log (end_bpm / pulses_per_minute());
373 return pulses_per_minute() * (expm1 (log_tempo_ratio)) / (end_pulse - pulse());
376 /* compute the function constant from some later tempo section, given tempo (whole pulses/min.) and distance (in frames) from session origin */
378 TempoSection::compute_c_func_frame (const double& end_bpm, const framepos_t& end_frame, const framecnt_t& frame_rate) const
380 return c_func (end_bpm, frame_to_minute (end_frame - frame(), frame_rate));
384 TempoSection::minute_to_frame (const double& time, const framecnt_t& frame_rate) const
386 return (framepos_t) floor ((time * 60.0 * frame_rate) + 0.5);
390 TempoSection::frame_to_minute (const framepos_t& frame, const framecnt_t& frame_rate) const
392 return (frame / (double) frame_rate) / 60.0;
395 /* position function */
397 TempoSection::a_func (double end_ppm, double c_func) const
399 return log (end_ppm / pulses_per_minute()) / c_func;
402 /*function constant*/
404 TempoSection::c_func (double end_ppm, double end_time) const
406 return log (end_ppm / pulses_per_minute()) / end_time;
409 /* tempo in ppm at time in minutes */
411 TempoSection::pulse_tempo_at_time (const double& time) const
413 return exp (_c_func * time) * pulses_per_minute();
416 /* time in minutes at tempo in ppm */
418 TempoSection::time_at_pulse_tempo (const double& pulse_tempo) const
420 return log (pulse_tempo / pulses_per_minute()) / _c_func;
423 /* tick at tempo in ppm */
425 TempoSection::pulse_at_pulse_tempo (const double& pulse_tempo) const
427 return (pulse_tempo - pulses_per_minute()) / _c_func;
430 /* tempo in ppm at tick */
432 TempoSection::pulse_tempo_at_pulse (const double& pulse) const
434 return (pulse * _c_func) + pulses_per_minute();
437 /* pulse at time in minutes */
439 TempoSection::pulse_at_time (const double& time) const
441 return expm1 (_c_func * time) * (pulses_per_minute() / _c_func);
444 /* time in minutes at pulse */
446 TempoSection::time_at_pulse (const double& pulse) const
448 return log1p ((_c_func * pulse) / pulses_per_minute()) / _c_func;
451 /***********************************************************************/
453 const string MeterSection::xml_state_node_name = "Meter";
455 MeterSection::MeterSection (const XMLNode& node)
456 : MetricSection (0.0, 0, MusicTime), Meter (TempoMap::default_meter())
458 XMLProperty const * prop;
463 framepos_t frame = 0;
464 pair<double, BBT_Time> start;
466 if ((prop = node.property ("start")) != 0) {
467 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
471 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
473 /* legacy session - start used to be in bbt*/
474 info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
479 if ((prop = node.property ("pulse")) != 0) {
480 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
481 error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
486 if ((prop = node.property ("beat")) != 0) {
487 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
488 error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
494 if ((prop = node.property ("bbt")) == 0) {
495 warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
496 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
500 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
501 throw failed_constructor();
507 if ((prop = node.property ("frame")) != 0) {
508 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
509 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
515 /* beats-per-bar is old; divisions-per-bar is new */
517 if ((prop = node.property ("divisions-per-bar")) == 0) {
518 if ((prop = node.property ("beats-per-bar")) == 0) {
519 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
520 throw failed_constructor();
523 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
524 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
525 throw failed_constructor();
528 if ((prop = node.property ("note-type")) == 0) {
529 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
530 throw failed_constructor();
532 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
533 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
534 throw failed_constructor();
537 if ((prop = node.property ("movable")) == 0) {
538 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
539 throw failed_constructor();
542 set_movable (string_is_affirmative (prop->value()));
544 if ((prop = node.property ("lock-style")) == 0) {
545 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
547 set_position_lock_style (MusicTime);
549 set_position_lock_style (AudioTime);
552 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
557 MeterSection::get_state() const
559 XMLNode *root = new XMLNode (xml_state_node_name);
563 snprintf (buf, sizeof (buf), "%lf", pulse());
564 root->add_property ("pulse", buf);
565 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
569 root->add_property ("bbt", buf);
570 snprintf (buf, sizeof (buf), "%lf", beat());
571 root->add_property ("beat", buf);
572 snprintf (buf, sizeof (buf), "%f", _note_type);
573 root->add_property ("note-type", buf);
574 snprintf (buf, sizeof (buf), "%li", frame());
575 root->add_property ("frame", buf);
576 root->add_property ("lock-style", enum_2_string (position_lock_style()));
577 snprintf (buf, sizeof (buf), "%f", _divisions_per_bar);
578 root->add_property ("divisions-per-bar", buf);
579 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
580 root->add_property ("movable", buf);
585 /***********************************************************************/
589 The Shaggs - Things I Wonder
590 https://www.youtube.com/watch?v=9wQK6zMJOoQ
592 Tempo is the rate of the musical pulse.
593 Meters divide the pulses into measures and beats.
595 TempoSections - provide pulses in the form of beats_per_minute() and note_type() where note_type is the division of a whole pulse,
596 and beats_per_minute is the number of note_types in one minute (unlike what its name suggests).
597 Note that Tempo::beats_per_minute() has nothing to do with musical beats. It has been left that way because
598 a shorter one hasn't been found yet (pulse_divisions_per_minute()?).
600 MeterSecions - divide pulses into measures (via divisions_per_bar) and beats (via note_divisor).
602 Both tempos and meters have a pulse position and a frame position.
603 Meters also have a beat position, which is always 0.0 for the first meter.
604 TempoSections and MeterSections may be locked to either audio or music (position lock style).
605 The lock style determines the 'true' position of the section wich is used to calculate the other postion parameters of the section.
607 The first tempo and first meter are special. they must move together, and must be locked to audio.
608 Audio locked tempos which lie before the first meter are made inactive.
609 They will be re-activated if the first meter is again placed before them.
611 With tepo sections potentially being ramped, meters provide a way of mapping beats to whole pulses without
612 referring to the tempo function(s) involved as the distance in whole pulses between a meter and a subsequent beat is
613 sb->beat() - meter->beat() / meter->note_divisor().
614 Because every meter falls on a known pulse, (derived from its bar), the rest is easy as the duration in pulses between
615 two meters is of course
616 (meater_b->bar - meter_a->bar) * meter_a->divisions_per_bar / meter_a->note_divisor.
618 Below, beat calculations are based on meter sections and all pulse and tempo calculations are based on tempo sections.
619 Beat to frame conversion of course requires the use of meter and tempo.
621 Remembering that ramped tempo sections interact, it is important to avoid referring to any other tempos when moving tempo sections,
622 Here, beats (meters) are used to determine the new pulse (see predict_tempo_position())
624 Recomputing the map is the process where the 'missing' position
625 (tempo pulse or meter pulse & beat in the case of AudioTime, frame for MusicTime) is calculated.
626 We construct the tempo map by first using the frame or pulse position (depending on position lock style) of each tempo.
627 We then use this tempo map (really just the tempos) to find the pulse or frame position of each meter (again depending on lock style).
629 Having done this, we can now find any musical duration by selecting the tempo and meter covering the position (or tempo) in question
630 and querying its appropriate meter/tempo.
632 It is important to keep the _metrics in an order that makes sense.
633 Because ramped MusicTime and AudioTime tempos can interact with each other,
634 reordering is frequent. Care must be taken to keep _metrics in a solved state.
635 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
639 Music and audio-locked objects may seem interchangeable on the surface, but when translating
640 between audio samples and beats, keep in mind that a sample is only a quantised approximation
641 of the actual time (in minutes) of a beat.
642 Thus if a gui user points to the frame occupying the start of a music-locked object on 1|3|0, it does not
643 mean that this frame is the actual location in time of 1|3|0.
645 You cannot use a frame measurement to determine beat distance except under special circumstances
646 (e.g. where the user has requested that a beat lie on a SMPTE frame or if the tempo is known to be constant over the duration).
648 This means is that a user operating on a musical grid must supply the desired beat position and/or current beat quantization in order for the
649 sample space the user is operating at to be translated correctly to the object.
651 The current approach is to interpret the supplied frame using the grid division the user has currently selected.
652 If the user has no musical grid set, they are actually operating in sample space (even SMPTE frames are rounded to audio frame), so
653 the supplied audio frame is interpreted as the desired musical location (beat_at_frame()).
655 tldr: Beat, being a function of time, has nothing to do with sample rate, but time quantization can get in the way of precision.
657 When frame_at_beat() is called, the position calculation is performed in pulses and minutes.
658 The result is rounded to audio frames.
659 When beat_at_frame() is called, the frame is converted to minutes, with no rounding performed on the result.
662 frame_at_beat (beat_at_frame (frame)) == frame
664 beat_at_frame (frame_at_beat (beat)) != beat due to the time quantization of frame_at_beat().
666 Doing the second one will result in a beat distance error of up to 0.5 audio samples.
667 So instead work in pulses and/or beats and only use beat position to caclulate frame position (e.g. after tempo change).
668 For audio-locked objects, use frame position to calculate beat position.
670 The above pointless example would then do:
671 beat_at_pulse (pulse_at_beat (beat)) to avoid rounding.
674 struct MetricSectionSorter {
675 bool operator() (const MetricSection* a, const MetricSection* b) {
676 return a->pulse() < b->pulse();
680 struct MetricSectionFrameSorter {
681 bool operator() (const MetricSection* a, const MetricSection* b) {
682 return a->frame() < b->frame();
686 TempoMap::TempoMap (framecnt_t fr)
689 BBT_Time start (1, 1, 0);
691 TempoSection *t = new TempoSection (0.0, 0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Ramp, AudioTime);
692 MeterSection *m = new MeterSection (0.0, 0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime);
694 t->set_movable (false);
695 m->set_movable (false);
697 /* note: frame time is correct (zero) for both of these */
699 _metrics.push_back (t);
700 _metrics.push_back (m);
704 TempoMap::~TempoMap ()
706 Metrics::const_iterator d = _metrics.begin();
707 while (d != _metrics.end()) {
715 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
717 bool removed = false;
720 Glib::Threads::RWLock::WriterLock lm (lock);
721 if ((removed = remove_tempo_locked (tempo))) {
722 if (complete_operation) {
723 recompute_map (_metrics);
728 if (removed && complete_operation) {
729 PropertyChanged (PropertyChange ());
734 TempoMap::remove_tempo_locked (const TempoSection& tempo)
738 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
739 if (dynamic_cast<TempoSection*> (*i) != 0) {
740 if (tempo.frame() == (*i)->frame()) {
741 if ((*i)->movable()) {
754 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
756 bool removed = false;
759 Glib::Threads::RWLock::WriterLock lm (lock);
760 if ((removed = remove_meter_locked (tempo))) {
761 if (complete_operation) {
762 recompute_map (_metrics);
767 if (removed && complete_operation) {
768 PropertyChanged (PropertyChange ());
773 TempoMap::remove_meter_locked (const MeterSection& meter)
777 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
779 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
780 if (meter.frame() == (*i)->frame()) {
781 if (t->locked_to_meter()) {
790 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
791 if (dynamic_cast<MeterSection*> (*i) != 0) {
792 if (meter.frame() == (*i)->frame()) {
793 if ((*i)->movable()) {
806 TempoMap::do_insert (MetricSection* section)
808 bool need_add = true;
809 /* we only allow new meters to be inserted on beat 1 of an existing
813 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
815 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
817 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
818 corrected.second.beats = 1;
819 corrected.second.ticks = 0;
820 corrected.first = beat_at_bbt_locked (_metrics, corrected.second);
821 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
822 m->bbt(), corrected.second) << endmsg;
823 //m->set_pulse (corrected);
827 /* Look for any existing MetricSection that is of the same type and
828 in the same bar as the new one, and remove it before adding
829 the new one. Note that this means that if we find a matching,
830 existing section, we can break out of the loop since we're
831 guaranteed that there is only one such match.
834 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
836 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
837 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
838 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
839 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
841 if (tempo && insert_tempo) {
844 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
845 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
847 if (!tempo->movable()) {
849 /* can't (re)move this section, so overwrite
850 * its data content (but not its properties as
854 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
855 (*i)->set_position_lock_style (AudioTime);
857 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
858 t->set_type (insert_tempo->type());
868 } else if (meter && insert_meter) {
872 bool const ipm = insert_meter->position_lock_style() == MusicTime;
874 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
876 if (!meter->movable()) {
878 /* can't (re)move this section, so overwrite
879 * its data content (but not its properties as
883 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
884 (*i)->set_position_lock_style (AudioTime);
894 /* non-matching types, so we don't care */
898 /* Add the given MetricSection, if we didn't just reset an existing
903 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
904 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
907 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
908 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
911 bool const ipm = insert_meter->position_lock_style() == MusicTime;
912 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
917 } else if (insert_tempo) {
918 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
919 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
922 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
923 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
930 _metrics.insert (i, section);
931 //dump (_metrics, std::cout);
936 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, ARDOUR::TempoSection::Type type, PositionLockStyle pls)
938 TempoSection* ts = 0;
940 Glib::Threads::RWLock::WriterLock lm (lock);
941 ts = add_tempo_locked (tempo, pulse, frame, type, pls, true);
945 PropertyChanged (PropertyChange ());
951 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, TempoSection::Type type, PositionLockStyle pls)
953 const bool locked_to_meter = ts.locked_to_meter();
956 Glib::Threads::RWLock::WriterLock lm (lock);
957 TempoSection& first (first_tempo());
958 if (ts.frame() != first.frame()) {
959 remove_tempo_locked (ts);
960 add_tempo_locked (tempo, pulse, frame, type, pls, true, locked_to_meter);
962 first.set_type (type);
963 first.set_pulse (0.0);
964 first.set_frame (frame);
965 first.set_position_lock_style (AudioTime);
967 /* cannot move the first tempo section */
968 *static_cast<Tempo*>(&first) = tempo;
969 recompute_map (_metrics);
974 PropertyChanged (PropertyChange ());
978 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, framepos_t frame
979 , TempoSection::Type type, PositionLockStyle pls, bool recompute, bool locked_to_meter)
981 TempoSection* t = new TempoSection (pulse, frame, tempo.beats_per_minute(), tempo.note_type(), type, pls);
982 t->set_locked_to_meter (locked_to_meter);
987 if (pls == AudioTime) {
988 solve_map_frame (_metrics, t, t->frame());
990 solve_map_pulse (_metrics, t, t->pulse());
992 recompute_meters (_metrics);
999 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, const framepos_t& frame, PositionLockStyle pls)
1001 MeterSection* m = 0;
1003 Glib::Threads::RWLock::WriterLock lm (lock);
1004 m = add_meter_locked (meter, beat, where, frame, pls, true);
1009 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1010 dump (_metrics, std::cerr);
1014 PropertyChanged (PropertyChange ());
1019 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, const framepos_t& frame, PositionLockStyle pls)
1022 Glib::Threads::RWLock::WriterLock lm (lock);
1023 const double beat = beat_at_bbt_locked (_metrics, where);
1026 remove_meter_locked (ms);
1027 add_meter_locked (meter, beat, where, frame, pls, true);
1029 MeterSection& first (first_meter());
1030 TempoSection& first_t (first_tempo());
1031 /* cannot move the first meter section */
1032 *static_cast<Meter*>(&first) = meter;
1033 first.set_position_lock_style (AudioTime);
1034 first.set_pulse (0.0);
1035 first.set_frame (frame);
1036 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1037 first.set_beat (beat);
1038 first_t.set_frame (first.frame());
1039 first_t.set_pulse (0.0);
1040 first_t.set_position_lock_style (AudioTime);
1041 recompute_map (_metrics);
1045 PropertyChanged (PropertyChange ());
1049 TempoMap::add_meter_locked (const Meter& meter, double beat, const Timecode::BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute)
1051 const MeterSection& prev_m = meter_section_at_frame_locked (_metrics, frame - 1);
1052 const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1054 if (pls == AudioTime) {
1055 /* add meter-locked tempo */
1056 add_tempo_locked (tempo_at_frame_locked (_metrics, frame), pulse, frame, TempoSection::Ramp, AudioTime, true, true);
1059 MeterSection* new_meter = new MeterSection (pulse, frame, beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls);
1061 do_insert (new_meter);
1065 if (pls == AudioTime) {
1066 solve_map_frame (_metrics, new_meter, frame);
1068 solve_map_bbt (_metrics, new_meter, where);
1076 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1078 Tempo newtempo (beats_per_minute, note_type);
1081 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1082 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1087 Glib::Threads::RWLock::WriterLock lm (lock);
1088 *((Tempo*) t) = newtempo;
1089 recompute_map (_metrics);
1091 PropertyChanged (PropertyChange ());
1098 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1100 Tempo newtempo (beats_per_minute, note_type);
1103 TempoSection* first;
1104 Metrics::iterator i;
1106 /* find the TempoSection immediately preceding "where"
1109 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1111 if ((*i)->frame() > where) {
1117 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1130 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1140 Glib::Threads::RWLock::WriterLock lm (lock);
1141 /* cannot move the first tempo section */
1142 *((Tempo*)prev) = newtempo;
1143 recompute_map (_metrics);
1146 PropertyChanged (PropertyChange ());
1150 TempoMap::first_meter () const
1152 const MeterSection *m = 0;
1154 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1155 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1160 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1161 abort(); /*NOTREACHED*/
1166 TempoMap::first_meter ()
1168 MeterSection *m = 0;
1170 /* CALLER MUST HOLD LOCK */
1172 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1173 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1178 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1179 abort(); /*NOTREACHED*/
1184 TempoMap::first_tempo () const
1186 const TempoSection *t = 0;
1188 /* CALLER MUST HOLD LOCK */
1190 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1191 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1195 if (!t->movable()) {
1201 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1202 abort(); /*NOTREACHED*/
1207 TempoMap::first_tempo ()
1209 TempoSection *t = 0;
1211 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1212 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1216 if (!t->movable()) {
1222 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1223 abort(); /*NOTREACHED*/
1227 TempoMap::recompute_tempos (Metrics& metrics)
1229 TempoSection* prev_t = 0;
1231 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1234 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1238 if (!t->movable()) {
1246 if (t->position_lock_style() == AudioTime) {
1247 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
1248 if (!t->locked_to_meter()) {
1249 t->set_pulse (prev_t->pulse_at_tempo (t->pulses_per_minute(), t->frame(), _frame_rate));
1253 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
1254 t->set_frame (prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate));
1261 prev_t->set_c_func (0.0);
1264 /* tempos must be positioned correctly.
1265 the current approach is to use a meter's bbt time as its base position unit.
1266 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1267 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1270 TempoMap::recompute_meters (Metrics& metrics)
1272 MeterSection* meter = 0;
1273 MeterSection* prev_m = 0;
1275 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1276 if ((meter = dynamic_cast<MeterSection*> (*mi)) != 0) {
1277 if (meter->position_lock_style() == AudioTime) {
1279 pair<double, BBT_Time> b_bbt;
1280 TempoSection* meter_locked_tempo = 0;
1281 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1283 if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
1284 if ((t->locked_to_meter() || !t->movable()) && t->frame() == meter->frame()) {
1285 meter_locked_tempo = t;
1292 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1293 if (beats + prev_m->beat() != meter->beat()) {
1294 /* reordering caused a bbt change */
1295 b_bbt = make_pair (beats + prev_m->beat()
1296 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1297 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1299 } else if (meter->movable()) {
1300 b_bbt = make_pair (meter->beat(), meter->bbt());
1301 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1304 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1306 if (meter_locked_tempo) {
1307 meter_locked_tempo->set_pulse (pulse);
1309 meter->set_beat (b_bbt);
1310 meter->set_pulse (pulse);
1315 pair<double, BBT_Time> b_bbt;
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));
1323 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1325 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1327 /* shouldn't happen - the first is audio-locked */
1328 pulse = pulse_at_beat_locked (metrics, meter->beat());
1329 b_bbt = make_pair (meter->beat(), meter->bbt());
1332 meter->set_beat (b_bbt);
1333 meter->set_pulse (pulse);
1334 meter->set_frame (frame_at_pulse_locked (metrics, pulse));
1343 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1345 /* CALLER MUST HOLD WRITE LOCK */
1349 /* we will actually stop once we hit
1356 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1359 /* silly call from Session::process() during startup
1364 recompute_tempos (metrics);
1365 recompute_meters (metrics);
1369 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1371 Glib::Threads::RWLock::ReaderLock lm (lock);
1372 TempoMetric m (first_meter(), first_tempo());
1374 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1375 at something, because we insert the default tempo and meter during
1376 TempoMap construction.
1378 now see if we can find better candidates.
1381 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1383 if ((*i)->frame() > frame) {
1397 /* XX meters only */
1399 TempoMap::metric_at (BBT_Time bbt) const
1401 Glib::Threads::RWLock::ReaderLock lm (lock);
1402 TempoMetric m (first_meter(), first_tempo());
1404 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1405 at something, because we insert the default tempo and meter during
1406 TempoMap construction.
1408 now see if we can find better candidates.
1411 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1413 if ((mw = dynamic_cast<MeterSection*> (*i)) != 0) {
1414 BBT_Time section_start (mw->bbt());
1416 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1428 TempoMap::beat_at_frame (const framecnt_t& frame) const
1430 Glib::Threads::RWLock::ReaderLock lm (lock);
1431 return beat_at_frame_locked (_metrics, frame);
1434 /* meter / tempo section based */
1436 TempoMap::beat_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1438 const TempoSection& ts = tempo_section_at_frame_locked (metrics, frame);
1439 MeterSection* prev_m = 0;
1440 MeterSection* next_m = 0;
1442 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1444 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1445 if (prev_m && m->frame() > frame) {
1452 if (frame < prev_m->frame()) {
1455 const double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
1457 /* audio locked meters fake their beat */
1458 if (next_m && next_m->beat() < beat) {
1459 return next_m->beat();
1466 TempoMap::frame_at_beat (const double& beat) const
1468 Glib::Threads::RWLock::ReaderLock lm (lock);
1469 return frame_at_beat_locked (_metrics, beat);
1472 /* meter & tempo section based */
1474 TempoMap::frame_at_beat_locked (const Metrics& metrics, const double& beat) const
1476 MeterSection* prev_m = 0;
1477 TempoSection* prev_t = 0;
1479 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1481 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1482 if (prev_m && m->beat() > beat) {
1489 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1491 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1492 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1500 return prev_t->frame_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse(), _frame_rate);
1504 TempoMap::tempo_at_frame (const framepos_t& frame) const
1506 Glib::Threads::RWLock::ReaderLock lm (lock);
1507 return tempo_at_frame_locked (_metrics, frame);
1511 TempoMap::tempo_at_frame_locked (const Metrics& metrics, const framepos_t& frame) const
1513 TempoSection* prev_t = 0;
1515 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1517 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1521 if ((prev_t) && t->frame() > frame) {
1522 /* t is the section past frame */
1523 const double ret_bpm = prev_t->tempo_at_frame (frame, _frame_rate) * prev_t->note_type();
1524 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
1531 const double ret = prev_t->beats_per_minute();
1532 const Tempo ret_tempo (ret, prev_t->note_type ());
1537 /** returns the frame at which the supplied tempo occurs, or
1538 * the frame of the last tempo section (search exhausted)
1539 * only the position of the first occurence will be returned
1543 TempoMap::frame_at_tempo (const Tempo& tempo) const
1545 Glib::Threads::RWLock::ReaderLock lm (lock);
1546 return frame_at_tempo_locked (_metrics, tempo);
1551 TempoMap::frame_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1553 TempoSection* prev_t = 0;
1554 const double tempo_ppm = tempo.beats_per_minute() / tempo.note_type();
1556 Metrics::const_iterator i;
1558 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1560 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1566 const double t_ppm = t->beats_per_minute() / t->note_type();
1568 if (t_ppm == tempo_ppm) {
1573 const double prev_t_ppm = prev_t->beats_per_minute() / prev_t->note_type();
1575 if ((t_ppm > tempo_ppm && prev_t_ppm < tempo_ppm) || (t_ppm < tempo_ppm && prev_t_ppm > tempo_ppm)) {
1576 const framepos_t ret_frame = prev_t->frame_at_tempo (tempo_ppm, prev_t->pulse(), _frame_rate);
1584 return prev_t->frame();
1587 /** more precise than doing tempo_at_frame (frame_at_beat (b)),
1588 * as there is no intermediate frame rounding.
1591 TempoMap::tempo_at_beat (const double& beat) const
1593 Glib::Threads::RWLock::ReaderLock lm (lock);
1594 const MeterSection* prev_m = &meter_section_at_beat_locked (_metrics, beat);
1595 const TempoSection* prev_t = &tempo_section_at_beat_locked (_metrics, beat);
1596 const double note_type = prev_t->note_type();
1598 return Tempo (prev_t->tempo_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse()) * note_type, note_type);
1602 TempoMap::pulse_at_beat (const double& beat) const
1604 Glib::Threads::RWLock::ReaderLock lm (lock);
1605 return pulse_at_beat_locked (_metrics, beat);
1609 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1611 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1613 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1617 TempoMap::beat_at_pulse (const double& pulse) const
1619 Glib::Threads::RWLock::ReaderLock lm (lock);
1620 return beat_at_pulse_locked (_metrics, pulse);
1624 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1626 MeterSection* prev_m = 0;
1628 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1630 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1631 if (prev_m && m->pulse() > pulse) {
1632 if (((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > m->beat()) {
1640 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1645 TempoMap::pulse_at_frame (const framecnt_t& frame) const
1647 Glib::Threads::RWLock::ReaderLock lm (lock);
1648 return pulse_at_frame_locked (_metrics, frame);
1651 /* tempo section based */
1653 TempoMap::pulse_at_frame_locked (const Metrics& metrics, const framecnt_t& frame) const
1655 /* HOLD (at least) THE READER LOCK */
1656 TempoSection* prev_t = 0;
1658 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1660 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1664 if (prev_t && t->frame() > frame) {
1665 /*the previous ts is the one containing the frame */
1666 const double ret = prev_t->pulse_at_frame (frame, _frame_rate);
1673 /* treated as constant for this ts */
1674 const double pulses_in_section = (frame - prev_t->frame()) / prev_t->frames_per_pulse (_frame_rate);
1676 return pulses_in_section + prev_t->pulse();
1680 TempoMap::frame_at_pulse (const double& pulse) const
1682 Glib::Threads::RWLock::ReaderLock lm (lock);
1683 return frame_at_pulse_locked (_metrics, pulse);
1686 /* tempo section based */
1688 TempoMap::frame_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1690 /* HOLD THE READER LOCK */
1692 const TempoSection* prev_t = 0;
1694 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1697 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1701 if (prev_t && t->pulse() > pulse) {
1702 return prev_t->frame_at_pulse (pulse, _frame_rate);
1708 /* must be treated as constant, irrespective of _type */
1709 double const pulses_in_section = pulse - prev_t->pulse();
1710 double const dtime = pulses_in_section * prev_t->frames_per_pulse (_frame_rate);
1712 framecnt_t const ret = (framecnt_t) floor (dtime) + prev_t->frame();
1718 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
1720 Glib::Threads::RWLock::ReaderLock lm (lock);
1721 return beat_at_bbt_locked (_metrics, bbt);
1726 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1728 /* CALLER HOLDS READ LOCK */
1730 MeterSection* prev_m = 0;
1732 /* because audio-locked meters have 'fake' integral beats,
1733 there is no pulse offset here.
1735 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1737 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1739 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1740 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1748 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1749 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1750 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1756 TempoMap::bbt_at_beat (const double& beats)
1758 Glib::Threads::RWLock::ReaderLock lm (lock);
1759 return bbt_at_beat_locked (_metrics, beats);
1763 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
1765 /* CALLER HOLDS READ LOCK */
1766 MeterSection* prev_m = 0;
1767 const double beats = max (0.0, b);
1769 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1770 MeterSection* m = 0;
1772 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1774 if (m->beat() > beats) {
1775 /* this is the meter after the one our beat is on*/
1784 const double beats_in_ms = beats - prev_m->beat();
1785 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1786 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1787 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1788 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1792 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1793 ret.beats = (uint32_t) floor (remaining_beats);
1794 ret.bars = total_bars;
1796 /* 0 0 0 to 1 1 0 - based mapping*/
1800 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1802 ret.ticks -= BBT_Time::ticks_per_beat;
1805 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1814 TempoMap::pulse_at_bbt (const Timecode::BBT_Time& bbt)
1816 Glib::Threads::RWLock::ReaderLock lm (lock);
1818 return pulse_at_bbt_locked (_metrics, bbt);
1822 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1824 /* CALLER HOLDS READ LOCK */
1826 MeterSection* prev_m = 0;
1828 /* because audio-locked meters have 'fake' integral beats,
1829 there is no pulse offset here.
1831 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1833 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1835 if (m->bbt().bars > bbt.bars) {
1843 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1844 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
1845 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
1851 TempoMap::bbt_at_pulse (const double& pulse)
1853 Glib::Threads::RWLock::ReaderLock lm (lock);
1855 return bbt_at_pulse_locked (_metrics, pulse);
1859 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1861 MeterSection* prev_m = 0;
1863 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1864 MeterSection* m = 0;
1866 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1869 double const pulses_to_m = m->pulse() - prev_m->pulse();
1870 if (prev_m->pulse() + pulses_to_m > pulse) {
1871 /* this is the meter after the one our beat is on*/
1880 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
1881 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1882 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1883 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1884 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1888 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1889 ret.beats = (uint32_t) floor (remaining_beats);
1890 ret.bars = total_bars;
1892 /* 0 0 0 to 1 1 0 mapping*/
1896 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1898 ret.ticks -= BBT_Time::ticks_per_beat;
1901 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1910 TempoMap::bbt_at_frame (framepos_t frame)
1917 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1920 Glib::Threads::RWLock::ReaderLock lm (lock);
1922 return bbt_at_frame_locked (_metrics, frame);
1926 TempoMap::bbt_at_frame_rt (framepos_t frame)
1928 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
1931 throw std::logic_error ("TempoMap::bbt_time_rt() could not lock tempo map");
1934 return bbt_at_frame_locked (_metrics, frame);
1938 TempoMap::bbt_at_frame_locked (const Metrics& metrics, const framepos_t& frame) const
1945 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
1949 const TempoSection& ts = tempo_section_at_frame_locked (metrics, frame);
1950 MeterSection* prev_m = 0;
1951 MeterSection* next_m = 0;
1953 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1955 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
1956 if (prev_m && m->frame() > frame) {
1964 double beat = prev_m->beat() + (ts.pulse_at_frame (frame, _frame_rate) - prev_m->pulse()) * prev_m->note_divisor();
1966 /* handle frame before first meter */
1967 if (frame < prev_m->frame()) {
1970 /* audio locked meters fake their beat */
1971 if (next_m && next_m->beat() < beat) {
1972 beat = next_m->beat();
1975 beat = max (0.0, beat);
1977 const double beats_in_ms = beat - prev_m->beat();
1978 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1979 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1980 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1981 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1985 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1986 ret.beats = (uint32_t) floor (remaining_beats);
1987 ret.bars = total_bars;
1989 /* 0 0 0 to 1 1 0 - based mapping*/
1993 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1995 ret.ticks -= BBT_Time::ticks_per_beat;
1998 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2007 TempoMap::frame_at_bbt (const BBT_Time& bbt)
2010 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
2014 if (bbt.beats < 1) {
2015 throw std::logic_error ("beats are counted from one");
2017 Glib::Threads::RWLock::ReaderLock lm (lock);
2019 return frame_at_bbt_locked (_metrics, bbt);
2022 /* meter & tempo section based */
2024 TempoMap::frame_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2026 /* HOLD THE READER LOCK */
2028 const framepos_t ret = frame_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2033 TempoMap::check_solved (const Metrics& metrics) const
2035 TempoSection* prev_t = 0;
2036 MeterSection* prev_m = 0;
2038 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2041 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2046 /* check ordering */
2047 if ((t->frame() <= prev_t->frame()) || (t->pulse() <= prev_t->pulse())) {
2051 /* precision check ensures tempo and frames align.*/
2052 if (t->frame() != prev_t->frame_at_tempo (t->pulses_per_minute(), t->pulse(), _frame_rate)) {
2053 if (!t->locked_to_meter()) {
2058 /* gradient limit - who knows what it should be?
2059 things are also ok (if a little chaotic) without this
2061 if (fabs (prev_t->c_func()) > 1000.0) {
2062 //std::cout << "c : " << prev_t->c_func() << std::endl;
2069 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2070 if (prev_m && m->position_lock_style() == AudioTime) {
2071 TempoSection* t = const_cast<TempoSection*>(&tempo_section_at_frame_locked (metrics, m->frame() - 1));
2072 const double nascent_m_pulse = ((m->beat() - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse();
2073 const framepos_t nascent_m_frame = t->frame_at_pulse (nascent_m_pulse, _frame_rate);
2075 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
2089 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
2091 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2093 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2094 if (!t->movable()) {
2095 t->set_active (true);
2098 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
2099 t->set_active (false);
2101 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
2102 t->set_active (true);
2103 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
2112 TempoMap::solve_map_frame (Metrics& imaginary, TempoSection* section, const framepos_t& frame)
2114 TempoSection* prev_t = 0;
2115 TempoSection* section_prev = 0;
2116 framepos_t first_m_frame = 0;
2118 /* can't move a tempo before the first meter */
2119 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2121 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2122 if (!m->movable()) {
2123 first_m_frame = m->frame();
2128 if (section->movable() && frame <= first_m_frame) {
2132 section->set_active (true);
2133 section->set_frame (frame);
2135 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2137 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2144 section_prev = prev_t;
2145 if (t->locked_to_meter()) {
2150 if (t->position_lock_style() == MusicTime) {
2151 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
2152 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
2154 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
2155 if (!t->locked_to_meter()) {
2156 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
2165 section_prev->set_c_func (section_prev->compute_c_func_frame (section->pulses_per_minute(), frame, _frame_rate));
2166 if (!section->locked_to_meter()) {
2167 section->set_pulse (section_prev->pulse_at_frame (frame, _frame_rate));
2172 recompute_tempos (imaginary);
2174 if (check_solved (imaginary)) {
2177 dunp (imaginary, std::cout);
2181 MetricSectionFrameSorter fcmp;
2182 imaginary.sort (fcmp);
2184 recompute_tempos (imaginary);
2186 if (check_solved (imaginary)) {
2194 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2196 TempoSection* prev_t = 0;
2197 TempoSection* section_prev = 0;
2199 section->set_pulse (pulse);
2201 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2203 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2207 if (!t->movable()) {
2214 section_prev = prev_t;
2217 if (t->position_lock_style() == MusicTime) {
2218 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->pulses_per_minute(), t->pulse(), _frame_rate));
2219 t->set_frame (prev_t->frame_at_pulse (t->pulse(), _frame_rate));
2221 prev_t->set_c_func (prev_t->compute_c_func_frame (t->pulses_per_minute(), t->frame(), _frame_rate));
2222 if (!t->locked_to_meter()) {
2223 t->set_pulse (prev_t->pulse_at_frame (t->frame(), _frame_rate));
2232 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->pulses_per_minute(), pulse, _frame_rate));
2233 section->set_frame (section_prev->frame_at_pulse (pulse, _frame_rate));
2237 recompute_tempos (imaginary);
2239 if (check_solved (imaginary)) {
2242 dunp (imaginary, std::cout);
2246 MetricSectionSorter cmp;
2247 imaginary.sort (cmp);
2249 recompute_tempos (imaginary);
2251 * XX need a restriction here, but only for this case,
2252 * as audio locked tempos don't interact in the same way.
2254 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2256 * |50 bpm |250 bpm |60 bpm
2257 * drag 250 to the pulse after 60->
2258 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2260 if (check_solved (imaginary)) {
2268 TempoMap::solve_map_frame (Metrics& imaginary, MeterSection* section, const framepos_t& frame)
2270 /* disallow moving first meter past any subsequent one, and any movable meter before the first one */
2271 const MeterSection* other = &meter_section_at_frame_locked (imaginary, frame);
2272 if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->frame() >= frame)) {
2276 if (!section->movable()) {
2277 /* lock the first tempo to our first meter */
2278 if (!set_active_tempos (imaginary, frame)) {
2283 TempoSection* meter_locked_tempo = 0;
2285 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2287 if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
2288 if ((t->locked_to_meter() || !t->movable()) && t->frame() == section->frame()) {
2289 meter_locked_tempo = t;
2295 if (!meter_locked_tempo) {
2299 MeterSection* prev_m = 0;
2301 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2302 bool solved = false;
2304 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2306 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2308 if (prev_m && section->movable()) {
2309 const double beats = (pulse_at_frame_locked (imaginary, frame) - prev_m->pulse()) * prev_m->note_divisor();
2310 if (beats + prev_m->beat() < section->beat()) {
2311 /* set the frame/pulse corresponding to its musical position,
2312 * as an earlier time than this has been requested.
2314 const double new_pulse = ((section->beat() - prev_m->beat())
2315 / prev_m->note_divisor()) + prev_m->pulse();
2317 const framepos_t smallest_frame = frame_at_pulse_locked (future_map, new_pulse);
2319 if ((solved = solve_map_frame (future_map, tempo_copy, smallest_frame))) {
2320 meter_locked_tempo->set_pulse (new_pulse);
2321 solve_map_frame (imaginary, meter_locked_tempo, smallest_frame);
2322 section->set_frame (smallest_frame);
2323 section->set_pulse (new_pulse);
2328 Metrics::const_iterator d = future_map.begin();
2329 while (d != future_map.end()) {
2338 /* all is ok. set section's locked tempo if allowed.
2339 possibly disallowed if there is an adjacent audio-locked tempo.
2340 XX this check could possibly go. its never actually happened here.
2342 MeterSection* meter_copy = const_cast<MeterSection*> (&meter_section_at_frame_locked (future_map, section->frame()));
2343 meter_copy->set_frame (frame);
2345 if ((solved = solve_map_frame (future_map, tempo_copy, frame))) {
2346 section->set_frame (frame);
2347 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2348 / prev_m->note_divisor()) + prev_m->pulse());
2349 solve_map_frame (imaginary, meter_locked_tempo, frame);
2354 Metrics::const_iterator d = future_map.begin();
2355 while (d != future_map.end()) {
2365 /* not movable (first meter atm) */
2367 tempo_copy->set_frame (frame);
2368 tempo_copy->set_pulse (0.0);
2370 if ((solved = solve_map_frame (future_map, tempo_copy, frame))) {
2371 section->set_frame (frame);
2372 meter_locked_tempo->set_frame (frame);
2373 meter_locked_tempo->set_pulse (0.0);
2374 solve_map_frame (imaginary, meter_locked_tempo, frame);
2379 Metrics::const_iterator d = future_map.begin();
2380 while (d != future_map.end()) {
2389 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2390 section->set_beat (b_bbt);
2391 section->set_pulse (0.0);
2401 MetricSectionFrameSorter fcmp;
2402 imaginary.sort (fcmp);
2404 recompute_meters (imaginary);
2410 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2412 /* disallow setting section to an existing meter's bbt */
2413 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2415 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2416 if (m != section && m->bbt().bars == when.bars) {
2422 MeterSection* prev_m = 0;
2423 MeterSection* section_prev = 0;
2425 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2427 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
2428 pair<double, BBT_Time> b_bbt;
2429 double new_pulse = 0.0;
2431 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2432 section_prev = prev_m;
2433 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2434 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2435 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2437 section->set_beat (b_bbt);
2438 section->set_pulse (pulse);
2439 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2444 if (m->position_lock_style() == AudioTime) {
2445 TempoSection* meter_locked_tempo = 0;
2447 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2449 if ((t = dynamic_cast<TempoSection*> (*ii)) != 0) {
2450 if ((t->locked_to_meter() || !t->movable()) && t->frame() == m->frame()) {
2451 meter_locked_tempo = t;
2457 if (!meter_locked_tempo) {
2462 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2464 if (beats + prev_m->beat() != m->beat()) {
2465 /* tempo/ meter change caused a change in beat (bar). */
2466 b_bbt = make_pair (beats + prev_m->beat()
2467 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2468 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2469 } else if (m->movable()) {
2470 b_bbt = make_pair (m->beat(), m->bbt());
2471 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2474 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2477 meter_locked_tempo->set_pulse (new_pulse);
2478 m->set_beat (b_bbt);
2479 m->set_pulse (new_pulse);
2483 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2484 if (beats + prev_m->beat() != m->beat()) {
2485 /* tempo/ meter change caused a change in beat (bar). */
2486 b_bbt = make_pair (beats + prev_m->beat()
2487 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2489 b_bbt = make_pair (beats + prev_m->beat()
2492 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2493 m->set_beat (b_bbt);
2494 m->set_pulse (new_pulse);
2495 m->set_frame (frame_at_pulse_locked (imaginary, new_pulse));
2502 if (!section_prev) {
2504 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
2505 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2506 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
2508 section->set_beat (b_bbt);
2509 section->set_pulse (pulse);
2510 section->set_frame (frame_at_pulse_locked (imaginary, pulse));
2513 MetricSectionSorter cmp;
2514 imaginary.sort (cmp);
2516 recompute_meters (imaginary);
2521 /** places a copy of _metrics into copy and returns a pointer
2522 * to section's equivalent in copy.
2525 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
2527 TempoSection* ret = 0;
2529 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2532 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2534 ret = new TempoSection (*t);
2535 copy.push_back (ret);
2539 TempoSection* cp = new TempoSection (*t);
2540 copy.push_back (cp);
2542 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2543 MeterSection* cp = new MeterSection (*m);
2544 copy.push_back (cp);
2552 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
2554 MeterSection* ret = 0;
2556 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2559 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2560 TempoSection* cp = new TempoSection (*t);
2561 copy.push_back (cp);
2564 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
2566 ret = new MeterSection (*m);
2567 copy.push_back (ret);
2570 MeterSection* cp = new MeterSection (*m);
2571 copy.push_back (cp);
2578 /** answers the question "is this a valid beat position for this tempo section?".
2579 * it returns true if the tempo section can be moved to the requested bbt position,
2580 * leaving the tempo map in a solved state.
2581 * @param section the tempo section to be moved
2582 * @param bbt the requested new position for the tempo section
2583 * @return true if the tempo section can be moved to the position, otherwise false.
2586 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2589 TempoSection* tempo_copy = 0;
2592 Glib::Threads::RWLock::ReaderLock lm (lock);
2593 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
2599 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
2601 Metrics::const_iterator d = copy.begin();
2602 while (d != copy.end()) {
2611 * 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,
2612 * taking any possible reordering as a consequence of this into account.
2613 * @param section - the section to be altered
2614 * @param bbt - the bbt where the altered tempo will fall
2615 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
2617 pair<double, framepos_t>
2618 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
2621 pair<double, framepos_t> ret = make_pair (0.0, 0);
2623 Glib::Threads::RWLock::ReaderLock lm (lock);
2625 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
2627 const double beat = beat_at_bbt_locked (future_map, bbt);
2629 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
2630 ret.first = tempo_copy->pulse();
2631 ret.second = tempo_copy->frame();
2633 ret.first = section->pulse();
2634 ret.second = section->frame();
2637 Metrics::const_iterator d = future_map.begin();
2638 while (d != future_map.end()) {
2646 TempoMap::gui_move_tempo (TempoSection* ts, const framepos_t& frame, const int& sub_num)
2649 bool was_musical = ts->position_lock_style() == MusicTime;
2651 if (sub_num == 0 && was_musical) {
2652 /* if we're not snapping to music,
2653 AudioTime and MusicTime may be treated identically.
2655 ts->set_position_lock_style (AudioTime);
2658 if (ts->position_lock_style() == MusicTime) {
2660 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
2661 Glib::Threads::RWLock::WriterLock lm (lock);
2662 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2663 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
2664 double pulse = pulse_at_beat_locked (future_map, beat);
2666 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
2667 solve_map_pulse (_metrics, ts, pulse);
2668 recompute_meters (_metrics);
2675 Glib::Threads::RWLock::WriterLock lm (lock);
2676 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2677 if (solve_map_frame (future_map, tempo_copy, frame)) {
2678 solve_map_frame (_metrics, ts, frame);
2679 recompute_meters (_metrics);
2684 if (sub_num == 0 && was_musical) {
2685 ts->set_position_lock_style (MusicTime);
2688 Metrics::const_iterator d = future_map.begin();
2689 while (d != future_map.end()) {
2694 MetricPositionChanged (); // Emit Signal
2698 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
2702 if (ms->position_lock_style() == AudioTime) {
2705 Glib::Threads::RWLock::WriterLock lm (lock);
2706 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2708 if (solve_map_frame (future_map, copy, frame)) {
2709 solve_map_frame (_metrics, ms, frame);
2710 recompute_tempos (_metrics);
2715 Glib::Threads::RWLock::WriterLock lm (lock);
2716 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
2718 const double beat = beat_at_frame_locked (_metrics, frame);
2719 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
2721 if (solve_map_bbt (future_map, copy, bbt)) {
2722 solve_map_bbt (_metrics, ms, bbt);
2723 recompute_tempos (_metrics);
2728 Metrics::const_iterator d = future_map.begin();
2729 while (d != future_map.end()) {
2734 MetricPositionChanged (); // Emit Signal
2738 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
2741 bool can_solve = false;
2743 Glib::Threads::RWLock::WriterLock lm (lock);
2744 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
2745 tempo_copy->set_beats_per_minute (bpm.beats_per_minute());
2746 recompute_tempos (future_map);
2748 if (check_solved (future_map)) {
2749 ts->set_beats_per_minute (bpm.beats_per_minute());
2750 recompute_map (_metrics);
2755 Metrics::const_iterator d = future_map.begin();
2756 while (d != future_map.end()) {
2761 MetricPositionChanged (); // Emit Signal
2767 TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame, const double& pulse)
2770 Ts (future prev_t) Tnext
2773 |----------|----------
2780 Glib::Threads::RWLock::WriterLock lm (lock);
2786 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
2787 TempoSection* prev_to_prev_t = 0;
2788 const frameoffset_t fr_off = end_frame - frame;
2790 if (prev_t && prev_t->pulse() > 0.0) {
2791 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_frame_locked (future_map, prev_t->frame() - 1));
2794 TempoSection* next_t = 0;
2795 for (Metrics::iterator i = future_map.begin(); i != future_map.end(); ++i) {
2796 TempoSection* t = 0;
2797 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
2798 if (t->frame() > ts->frame()) {
2804 /* minimum allowed measurement distance in frames */
2805 const framepos_t min_dframe = 2;
2807 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
2808 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
2810 double contribution = 0.0;
2812 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2813 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
2816 const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
2818 const double start_pulse = prev_t->pulse_at_frame (frame, _frame_rate);
2819 const double end_pulse = prev_t->pulse_at_frame (end_frame, _frame_rate);
2823 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
2825 if (prev_t->position_lock_style() == MusicTime) {
2826 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2827 if (frame > prev_to_prev_t->frame() + min_dframe && (frame + prev_t_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
2829 new_bpm = prev_t->beats_per_minute() * ((frame - prev_to_prev_t->frame())
2830 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
2832 new_bpm = prev_t->beats_per_minute();
2835 /* prev to prev is irrelevant */
2837 if (start_pulse > prev_t->pulse() && end_pulse > prev_t->pulse()) {
2838 new_bpm = prev_t->beats_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
2840 new_bpm = prev_t->beats_per_minute();
2845 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
2846 if (frame > prev_to_prev_t->frame() + min_dframe && end_frame > prev_to_prev_t->frame() + min_dframe) {
2848 new_bpm = prev_t->beats_per_minute() * ((frame - prev_to_prev_t->frame())
2849 / (double) ((end_frame) - prev_to_prev_t->frame()));
2851 new_bpm = prev_t->beats_per_minute();
2854 /* prev_to_prev_t is irrelevant */
2856 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
2857 new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
2859 new_bpm = prev_t->beats_per_minute();
2865 double frame_ratio = 1.0;
2866 double pulse_ratio = 1.0;
2867 const framepos_t pulse_pos = prev_t->frame_at_pulse (pulse, _frame_rate);
2869 if (prev_to_prev_t) {
2870 if (pulse_pos > prev_to_prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_to_prev_t->frame() + min_dframe) {
2871 frame_ratio = (((pulse_pos - fr_off) - prev_to_prev_t->frame()) / (double) ((pulse_pos) - prev_to_prev_t->frame()));
2873 if (end_pulse > prev_to_prev_t->pulse() && start_pulse > prev_to_prev_t->pulse()) {
2874 pulse_ratio = ((start_pulse - prev_to_prev_t->pulse()) / (end_pulse - prev_to_prev_t->pulse()));
2877 if (pulse_pos > prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_t->frame() + min_dframe) {
2878 frame_ratio = (((pulse_pos - fr_off) - prev_t->frame()) / (double) ((pulse_pos) - prev_t->frame()));
2880 pulse_ratio = (start_pulse / end_pulse);
2882 new_bpm = prev_t->beats_per_minute() * (pulse_ratio * frame_ratio);
2885 /* don't clamp and proceed here.
2886 testing has revealed that this can go negative,
2887 which is an entirely different thing to just being too low.
2889 if (new_bpm < 0.5) {
2892 new_bpm = min (new_bpm, (double) 1000.0);
2893 prev_t->set_beats_per_minute (new_bpm);
2894 recompute_tempos (future_map);
2895 recompute_meters (future_map);
2897 if (check_solved (future_map)) {
2898 ts->set_beats_per_minute (new_bpm);
2899 recompute_tempos (_metrics);
2900 recompute_meters (_metrics);
2904 Metrics::const_iterator d = future_map.begin();
2905 while (d != future_map.end()) {
2910 MetricPositionChanged (); // Emit Signal
2914 TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t& sub_num)
2916 Glib::Threads::RWLock::ReaderLock lm (lock);
2918 return exact_beat_at_frame_locked (_metrics, frame, sub_num);
2922 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t& sub_num)
2924 double beat = beat_at_frame_locked (metrics, frame);
2926 beat = floor (beat) + (floor (((beat - floor (beat)) * (double) sub_num) + 0.5) / sub_num);
2927 } else if (sub_num == 1) {
2929 beat = floor (beat + 0.5);
2930 } else if (sub_num == -1) {
2932 Timecode::BBT_Time bbt = bbt_at_beat_locked (metrics, beat);
2935 beat = beat_at_bbt_locked (metrics, bbt);
2941 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
2943 Glib::Threads::RWLock::ReaderLock lm (lock);
2945 const double tick_at_time = beat_at_frame_locked (_metrics, pos) * BBT_Time::ticks_per_beat;
2946 const double bbt_ticks = bbt.ticks + (bbt.beats * BBT_Time::ticks_per_beat);
2947 const double total_beats = (tick_at_time + bbt_ticks) / BBT_Time::ticks_per_beat;
2949 return frame_at_beat_locked (_metrics, total_beats);
2953 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
2955 return round_to_type (fr, dir, Bar);
2959 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
2961 return round_to_type (fr, dir, Beat);
2965 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
2967 Glib::Threads::RWLock::ReaderLock lm (lock);
2968 uint32_t ticks = (uint32_t) floor (beat_at_frame_locked (_metrics, fr) * BBT_Time::ticks_per_beat);
2969 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
2970 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
2972 ticks -= beats * BBT_Time::ticks_per_beat;
2975 /* round to next (or same iff dir == RoundUpMaybe) */
2977 uint32_t mod = ticks % ticks_one_subdivisions_worth;
2979 if (mod == 0 && dir == RoundUpMaybe) {
2980 /* right on the subdivision, which is fine, so do nothing */
2982 } else if (mod == 0) {
2983 /* right on the subdivision, so the difference is just the subdivision ticks */
2984 ticks += ticks_one_subdivisions_worth;
2987 /* not on subdivision, compute distance to next subdivision */
2989 ticks += ticks_one_subdivisions_worth - mod;
2992 if (ticks >= BBT_Time::ticks_per_beat) {
2993 ticks -= BBT_Time::ticks_per_beat;
2995 } else if (dir < 0) {
2997 /* round to previous (or same iff dir == RoundDownMaybe) */
2999 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3001 if (difference == 0 && dir == RoundDownAlways) {
3002 /* right on the subdivision, but force-rounding down,
3003 so the difference is just the subdivision ticks */
3004 difference = ticks_one_subdivisions_worth;
3007 if (ticks < difference) {
3008 ticks = BBT_Time::ticks_per_beat - ticks;
3010 ticks -= difference;
3014 /* round to nearest */
3017 /* compute the distance to the previous and next subdivision */
3019 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3021 /* closer to the next subdivision, so shift forward */
3023 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3025 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3027 if (ticks > BBT_Time::ticks_per_beat) {
3029 ticks -= BBT_Time::ticks_per_beat;
3030 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3033 } else if (rem > 0) {
3035 /* closer to previous subdivision, so shift backward */
3039 /* can't go backwards past zero, so ... */
3042 /* step back to previous beat */
3044 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3045 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3047 ticks = lrint (ticks - rem);
3048 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3051 /* on the subdivision, do nothing */
3055 const framepos_t ret_frame = frame_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat));
3061 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
3063 Glib::Threads::RWLock::ReaderLock lm (lock);
3065 const double beat_at_framepos = beat_at_frame_locked (_metrics, frame);
3066 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
3071 /* find bar previous to 'frame' */
3074 return frame_at_bbt_locked (_metrics, bbt);
3076 } else if (dir > 0) {
3077 /* find bar following 'frame' */
3081 return frame_at_bbt_locked (_metrics, bbt);
3083 /* true rounding: find nearest bar */
3084 framepos_t raw_ft = frame_at_bbt_locked (_metrics, bbt);
3087 framepos_t prev_ft = frame_at_bbt_locked (_metrics, bbt);
3089 framepos_t next_ft = frame_at_bbt_locked (_metrics, bbt);
3091 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
3102 return frame_at_beat_locked (_metrics, floor (beat_at_framepos));
3103 } else if (dir > 0) {
3104 return frame_at_beat_locked (_metrics, ceil (beat_at_framepos));
3106 return frame_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5));
3115 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
3116 framepos_t lower, framepos_t upper)
3118 Glib::Threads::RWLock::ReaderLock lm (lock);
3119 int32_t cnt = ceil (beat_at_frame_locked (_metrics, lower));
3121 /* although the map handles negative beats, bbt doesn't. */
3126 if (frame_at_beat_locked (_metrics, cnt) >= upper) {
3130 while (pos < upper) {
3131 pos = frame_at_beat_locked (_metrics, cnt);
3132 const TempoSection tempo = tempo_section_at_frame_locked (_metrics, pos);
3133 const MeterSection meter = meter_section_at_frame_locked (_metrics, pos);
3134 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
3135 points.push_back (BBTPoint (meter, tempo_at_frame_locked (_metrics, pos), pos, bbt.bars, bbt.beats, tempo.c_func()));
3141 TempoMap::tempo_section_at_frame (framepos_t frame) const
3143 Glib::Threads::RWLock::ReaderLock lm (lock);
3144 return tempo_section_at_frame_locked (_metrics, frame);
3148 TempoMap::tempo_section_at_frame_locked (const Metrics& metrics, framepos_t frame) const
3150 Metrics::const_iterator i;
3151 TempoSection* prev = 0;
3153 for (i = metrics.begin(); i != metrics.end(); ++i) {
3156 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3160 if (prev && t->frame() > frame) {
3170 abort(); /*NOTREACHED*/
3177 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3179 TempoSection* prev_t = 0;
3180 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
3182 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3184 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3185 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
3195 /* don't use this to calculate length (the tempo is only correct for this frame).
3196 do that stuff based on the beat_at_frame and frame_at_beat api
3199 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
3201 Glib::Threads::RWLock::ReaderLock lm (lock);
3203 const TempoSection* ts_at = &tempo_section_at_frame_locked (_metrics, frame);
3204 const TempoSection* ts_after = 0;
3205 Metrics::const_iterator i;
3207 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3210 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3214 if ((*i)->frame() > frame) {
3222 return (60.0 * _frame_rate) / (ts_at->tempo_at_frame (frame, _frame_rate) * ts_at->note_type());
3224 /* must be treated as constant tempo */
3225 return ts_at->frames_per_beat (_frame_rate);
3229 TempoMap::meter_section_at_frame_locked (const Metrics& metrics, framepos_t frame) const
3231 Metrics::const_iterator i;
3232 MeterSection* prev = 0;
3234 for (i = metrics.begin(); i != metrics.end(); ++i) {
3237 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
3239 if (prev && (*i)->frame() > frame) {
3249 abort(); /*NOTREACHED*/
3257 TempoMap::meter_section_at_frame (framepos_t frame) const
3259 Glib::Threads::RWLock::ReaderLock lm (lock);
3260 return meter_section_at_frame_locked (_metrics, frame);
3264 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3266 MeterSection* prev_m = 0;
3268 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3270 if ((m = dynamic_cast<MeterSection*> (*i)) != 0) {
3271 if (prev_m && m->beat() > beat) {
3282 TempoMap::meter_section_at_beat (double beat) const
3284 Glib::Threads::RWLock::ReaderLock lm (lock);
3285 return meter_section_at_beat_locked (_metrics, beat);
3289 TempoMap::meter_at_frame (framepos_t frame) const
3291 TempoMetric m (metric_at (frame));
3296 TempoMap::fix_legacy_session ()
3298 MeterSection* prev_m = 0;
3299 TempoSection* prev_t = 0;
3301 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3305 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3306 if (!m->movable()) {
3307 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3311 m->set_position_lock_style (AudioTime);
3316 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
3317 + (m->bbt().beats - 1)
3318 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
3320 m->set_beat (start);
3321 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
3322 + (m->bbt().beats - 1)
3323 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
3324 m->set_pulse (start_beat / prev_m->note_divisor());
3327 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3333 if (!t->movable()) {
3336 t->set_position_lock_style (AudioTime);
3342 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
3343 + (t->legacy_bbt().beats - 1)
3344 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
3346 t->set_pulse (beat / prev_m->note_divisor());
3348 /* really shouldn't happen but.. */
3349 t->set_pulse (beat / 4.0);
3358 TempoMap::get_state ()
3360 Metrics::const_iterator i;
3361 XMLNode *root = new XMLNode ("TempoMap");
3364 Glib::Threads::RWLock::ReaderLock lm (lock);
3365 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3366 root->add_child_nocopy ((*i)->get_state());
3374 TempoMap::set_state (const XMLNode& node, int /*version*/)
3377 Glib::Threads::RWLock::WriterLock lm (lock);
3380 XMLNodeConstIterator niter;
3381 Metrics old_metrics (_metrics);
3384 nlist = node.children();
3386 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
3387 XMLNode* child = *niter;
3389 if (child->name() == TempoSection::xml_state_node_name) {
3392 TempoSection* ts = new TempoSection (*child);
3393 _metrics.push_back (ts);
3396 catch (failed_constructor& err){
3397 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3398 _metrics = old_metrics;
3402 } else if (child->name() == MeterSection::xml_state_node_name) {
3405 MeterSection* ms = new MeterSection (*child);
3406 _metrics.push_back (ms);
3409 catch (failed_constructor& err) {
3410 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
3411 _metrics = old_metrics;
3417 if (niter == nlist.end()) {
3418 MetricSectionSorter cmp;
3419 _metrics.sort (cmp);
3422 /* check for legacy sessions where bbt was the base musical unit for tempo */
3423 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3425 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
3426 if (t->legacy_bbt().bars != 0) {
3427 fix_legacy_session();
3434 /* check for multiple tempo/meters at the same location, which
3435 ardour2 somehow allowed.
3438 Metrics::iterator prev = _metrics.end();
3439 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3440 if (prev != _metrics.end()) {
3442 MeterSection* prev_m;
3444 TempoSection* prev_t;
3445 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
3446 if (prev_m->pulse() == ms->pulse()) {
3447 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3448 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
3451 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
3452 if (prev_t->pulse() == ts->pulse()) {
3453 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3454 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
3462 recompute_map (_metrics);
3465 PropertyChanged (PropertyChange ());
3471 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
3473 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
3474 const MeterSection* m;
3475 const TempoSection* t;
3476 const TempoSection* prev_t = 0;
3478 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3480 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
3481 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type() << ") at " << t->pulse() << " frame= " << t->frame() << " (movable? "
3482 << t->movable() << ')' << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
3483 o << "current : " << t->beats_per_minute() << " | " << t->pulse() << " | " << t->frame() << std::endl;
3485 o << "previous : " << prev_t->beats_per_minute() << " | " << prev_t->pulse() << " | " << prev_t->frame() << std::endl;
3486 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;
3489 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
3490 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt() << " frame= " << m->frame()
3491 << " pulse: " << m->pulse() << " beat : " << m->beat() << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
3494 o << "------" << std::endl;
3498 TempoMap::n_tempos() const
3500 Glib::Threads::RWLock::ReaderLock lm (lock);
3503 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3504 if (dynamic_cast<const TempoSection*>(*i) != 0) {
3513 TempoMap::n_meters() const
3515 Glib::Threads::RWLock::ReaderLock lm (lock);
3518 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3519 if (dynamic_cast<const MeterSection*>(*i) != 0) {
3528 TempoMap::insert_time (framepos_t where, framecnt_t amount)
3531 Glib::Threads::RWLock::WriterLock lm (lock);
3532 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3533 if ((*i)->frame() >= where && (*i)->movable ()) {
3534 (*i)->set_frame ((*i)->frame() + amount);
3538 /* now reset the BBT time of all metrics, based on their new
3539 * audio time. This is the only place where we do this reverse
3543 Metrics::iterator i;
3544 const MeterSection* meter;
3545 const TempoSection* tempo;
3549 meter = &first_meter ();
3550 tempo = &first_tempo ();
3555 // cerr << "\n###################### TIMESTAMP via AUDIO ##############\n" << endl;
3558 MetricSection* prev = 0;
3560 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3563 //TempoMetric metric (*meter, *tempo);
3564 MeterSection* ms = const_cast<MeterSection*>(meter);
3565 TempoSection* ts = const_cast<TempoSection*>(tempo);
3568 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3572 ts->set_pulse (t->pulse());
3574 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3575 ts->set_pulse (m->pulse());
3577 ts->set_frame (prev->frame());
3581 if ((m = dynamic_cast<MeterSection*>(prev)) != 0) {
3582 pair<double, BBT_Time> start = make_pair (m->beat(), m->bbt());
3583 ms->set_beat (start);
3584 ms->set_pulse (m->pulse());
3586 if ((t = dynamic_cast<TempoSection*>(prev)) != 0) {
3590 const double beat = beat_at_pulse_locked (_metrics, t->pulse());
3591 pair<double, BBT_Time> start = make_pair (beat, bbt_at_beat_locked (_metrics, beat));
3592 ms->set_beat (start);
3593 ms->set_pulse (t->pulse());
3595 ms->set_frame (prev->frame());
3599 // metric will be at frames=0 bbt=1|1|0 by default
3600 // which is correct for our purpose
3603 // cerr << bbt << endl;
3605 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3609 t->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3611 // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3612 } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3613 bbt = bbt_at_frame_locked (_metrics, m->frame());
3615 // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
3621 if (bbt.ticks > BBT_Time::ticks_per_beat/2) {
3622 /* round up to next beat */
3628 if (bbt.beats != 1) {
3629 /* round up to next bar */
3634 pair<double, BBT_Time> start = make_pair (beat_at_frame_locked (_metrics, m->frame()), bbt);
3635 m->set_beat (start);
3636 m->set_pulse (pulse_at_frame_locked (_metrics, m->frame()));
3638 // cerr << "NEW METER, frame = " << (*i)->frame() << " beat = " << (*i)->pulse() <<endl;
3640 fatal << _("programming error: unhandled MetricSection type") << endmsg;
3641 abort(); /*NOTREACHED*/
3647 recompute_map (_metrics);
3651 PropertyChanged (PropertyChange ());
3654 TempoMap::remove_time (framepos_t where, framecnt_t amount)
3658 std::list<MetricSection*> metric_kill_list;
3660 TempoSection* last_tempo = NULL;
3661 MeterSection* last_meter = NULL;
3662 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
3663 bool meter_after = false; // is there a meter marker likewise?
3665 Glib::Threads::RWLock::WriterLock lm (lock);
3666 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3667 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
3668 metric_kill_list.push_back(*i);
3669 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
3672 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
3676 else if ((*i)->frame() >= where) {
3677 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
3678 (*i)->set_frame ((*i)->frame() - amount);
3679 if ((*i)->frame() == where) {
3680 // marker was immediately after end of range
3681 tempo_after = dynamic_cast<TempoSection*> (*i);
3682 meter_after = dynamic_cast<MeterSection*> (*i);
3688 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
3689 if (last_tempo && !tempo_after) {
3690 metric_kill_list.remove(last_tempo);
3691 last_tempo->set_frame(where);
3694 if (last_meter && !meter_after) {
3695 metric_kill_list.remove(last_meter);
3696 last_meter->set_frame(where);
3700 //remove all the remaining metrics
3701 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
3702 _metrics.remove(*i);
3707 recompute_map (_metrics);
3710 PropertyChanged (PropertyChange ());
3714 /** Add some (fractional) beats to a session frame position, and return the result in frames.
3715 * pos can be -ve, if required.
3718 TempoMap::framepos_plus_beats (framepos_t pos, Evoral::Beats beats) const
3720 Glib::Threads::RWLock::ReaderLock lm (lock);
3722 return frame_at_beat_locked (_metrics, beat_at_frame_locked (_metrics, pos) + beats.to_double());
3725 /** Subtract some (fractional) beats from a frame position, and return the result in frames */
3727 TempoMap::framepos_minus_beats (framepos_t pos, Evoral::Beats beats) const
3729 Glib::Threads::RWLock::ReaderLock lm (lock);
3731 return frame_at_beat_locked (_metrics, beat_at_frame_locked (_metrics, pos) - beats.to_double());
3734 /** Add the BBT interval op to pos and return the result */
3736 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
3738 Glib::Threads::RWLock::ReaderLock lm (lock);
3740 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_frame_locked (_metrics, pos));
3741 pos_bbt.ticks += op.ticks;
3742 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
3744 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3746 pos_bbt.beats += op.beats;
3747 /* the meter in effect will start on the bar */
3748 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();
3749 while (pos_bbt.beats >= divisions_per_bar + 1) {
3751 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
3752 pos_bbt.beats -= divisions_per_bar;
3754 pos_bbt.bars += op.bars;
3756 return frame_at_bbt_locked (_metrics, pos_bbt);
3759 /** Count the number of beats that are equivalent to distance when going forward,
3763 TempoMap::framewalk_to_beats (framepos_t pos, framecnt_t distance) const
3765 Glib::Threads::RWLock::ReaderLock lm (lock);
3767 return Evoral::Beats (beat_at_frame_locked (_metrics, pos + distance) - beat_at_frame_locked (_metrics, pos));
3771 bool operator() (const BBT_Time& a, const BBT_Time& b) {
3777 operator<< (std::ostream& o, const Meter& m) {
3778 return o << m.divisions_per_bar() << '/' << m.note_divisor();
3782 operator<< (std::ostream& o, const Tempo& t) {
3783 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
3787 operator<< (std::ostream& o, const MetricSection& section) {
3789 o << "MetricSection @ " << section.frame() << ' ';
3791 const TempoSection* ts;
3792 const MeterSection* ms;
3794 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
3795 o << *((const Tempo*) ts);
3796 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
3797 o << *((const Meter*) ms);