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, 4.0);
51 MetricSection::frame_at_minute (const double& time) const
53 return (framepos_t) floor ((time * 60.0 * _sample_rate) + 0.5);
57 MetricSection::minute_at_frame (const framepos_t& frame) const
59 return (frame / (double) _sample_rate) / 60.0;
62 /***********************************************************************/
65 Meter::frames_per_grid (const Tempo& tempo, framecnt_t sr) const
67 /* This is tempo- and meter-sensitive. The number it returns
68 is based on the interval between any two lines in the
69 grid that is constructed from tempo and meter sections.
71 The return value IS NOT interpretable in terms of "beats".
74 return (60.0 * sr) / (tempo.note_types_per_minute() * (_note_type/tempo.note_type()));
78 Meter::frames_per_bar (const Tempo& tempo, framecnt_t sr) const
80 return frames_per_grid (tempo, sr) * _divisions_per_bar;
83 /***********************************************************************/
85 const string TempoSection::xml_state_node_name = "Tempo";
87 TempoSection::TempoSection (const XMLNode& node, framecnt_t sample_rate)
88 : MetricSection (0.0, 0, MusicTime, true, sample_rate)
89 , Tempo (TempoMap::default_tempo())
92 , _locked_to_meter (false)
94 XMLProperty const * prop;
100 _legacy_bbt = BBT_Time (0, 0, 0);
102 if ((prop = node.property ("start")) != 0) {
103 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
107 /* legacy session - start used to be in bbt*/
110 info << _("Legacy session detected. TempoSection XML node will be altered.") << endmsg;
114 if ((prop = node.property ("pulse")) != 0) {
115 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
116 error << _("TempoSection XML node has an illegal \"pulse\" value") << endmsg;
122 if ((prop = node.property ("frame")) != 0) {
123 if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) {
124 error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg;
125 throw failed_constructor();
127 set_minute (minute_at_frame (frame));
131 /* XX replace old beats-per-minute name with note-types-per-minute */
132 if ((prop = node.property ("beats-per-minute")) != 0) {
133 if (sscanf (prop->value().c_str(), "%lf", &_note_types_per_minute) != 1 || _note_types_per_minute < 0.0) {
134 error << _("TempoSection XML node has an illegal \"beats-per-minute\" value") << endmsg;
135 throw failed_constructor();
139 if ((prop = node.property ("note-type")) == 0) {
140 /* older session, make note type be quarter by default */
143 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
144 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
145 throw failed_constructor();
149 if ((prop = node.property ("movable")) == 0) {
150 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
151 throw failed_constructor();
154 set_initial (!string_is_affirmative (prop->value()));
156 if ((prop = node.property ("active")) == 0) {
157 warning << _("TempoSection XML node has no \"active\" property") << endmsg;
160 set_active (string_is_affirmative (prop->value()));
163 if ((prop = node.property ("tempo-type")) == 0) {
166 _type = Type (string_2_enum (prop->value(), _type));
169 if ((prop = node.property ("lock-style")) == 0) {
171 set_position_lock_style (MusicTime);
173 set_position_lock_style (AudioTime);
176 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
179 if ((prop = node.property ("locked-to-meter")) == 0) {
180 set_locked_to_meter (false);
182 set_locked_to_meter (string_is_affirmative (prop->value()));
187 TempoSection::get_state() const
189 XMLNode *root = new XMLNode (xml_state_node_name);
193 snprintf (buf, sizeof (buf), "%lf", pulse());
194 root->add_property ("pulse", buf);
195 snprintf (buf, sizeof (buf), "%li", frame());
196 root->add_property ("frame", buf);
197 snprintf (buf, sizeof (buf), "%lf", _note_types_per_minute);
198 root->add_property ("beats-per-minute", buf);
199 snprintf (buf, sizeof (buf), "%lf", _note_type);
200 root->add_property ("note-type", buf);
201 snprintf (buf, sizeof (buf), "%s", !initial()?"yes":"no");
202 root->add_property ("movable", buf);
203 snprintf (buf, sizeof (buf), "%s", active()?"yes":"no");
204 root->add_property ("active", buf);
205 root->add_property ("tempo-type", enum_2_string (_type));
206 root->add_property ("lock-style", enum_2_string (position_lock_style()));
207 root->add_property ("locked-to-meter", locked_to_meter()?"yes":"no");
213 TempoSection::set_type (Type type)
218 /** returns the Tempo at the session-relative minute.
221 TempoSection::tempo_at_minute (const double& m) const
223 const bool constant = _type == Constant || _c == 0.0 || (initial() && m < minute());
225 return Tempo (note_types_per_minute(), note_type());
228 return Tempo (_tempo_at_time (m - minute()), _note_type);
231 /** returns the session relative minute where the supplied tempo in note types per minute occurs.
232 * @param ntpm the tempo in mote types per minute used to calculate the returned minute
233 * @param p the pulse used to calculate the returned minute for constant tempi
234 * @return the minute at the supplied tempo
236 * note that the note_type is currently ignored in this function. see below.
240 /** if tempoA (120, 4.0) precedes tempoB (120, 8.0),
241 * there should be no ramp between the two even if we are ramped.
242 * in other words a ramp should only place a curve on note_types_per_minute.
243 * we should be able to use Tempo note type here, but the above
244 * complicates things a bit.
247 TempoSection::minute_at_ntpm (const double& ntpm, const double& p) const
249 const bool constant = _type == Constant || _c == 0.0 || (initial() && p < pulse());
251 return ((p - pulse()) / pulses_per_minute()) + minute();
254 return _time_at_tempo (ntpm) + minute();
257 /** returns the Tempo at the supplied whole-note pulse.
260 TempoSection::tempo_at_pulse (const double& p) const
262 const bool constant = _type == Constant || _c == 0.0 || (initial() && p < pulse());
265 return Tempo (note_types_per_minute(), note_type());
268 return Tempo (_tempo_at_pulse (p - pulse()), _note_type);
271 /** returns the whole-note pulse where a tempo in note types per minute occurs.
272 * constant tempi require minute m.
273 * @param ntpm the note types per minute value used to calculate the returned pulse
274 * @param m the minute used to calculate the returned pulse if the tempo is constant
275 * @return the whole-note pulse at the supplied tempo
277 * note that note_type is currently ignored in this function. see minute_at_tempo().
279 * for constant tempi, this is anaologous to pulse_at_minute().
282 TempoSection::pulse_at_ntpm (const double& ntpm, const double& m) const
284 const bool constant = _type == Constant || _c == 0.0 || (initial() && m < minute());
286 return ((m - minute()) * pulses_per_minute()) + pulse();
289 return _pulse_at_tempo (ntpm) + pulse();
292 /** returns the whole-note pulse at the supplied session-relative minute.
295 TempoSection::pulse_at_minute (const double& m) const
297 const bool constant = _type == Constant || _c == 0.0 || (initial() && m < minute());
299 return ((m - minute()) * pulses_per_minute()) + pulse();
302 return _pulse_at_time (m - minute()) + pulse();
305 /** returns the session-relative minute at the supplied whole-note pulse.
308 TempoSection::minute_at_pulse (const double& p) const
310 const bool constant = _type == Constant || _c == 0.0 || (initial() && p < pulse());
312 return ((p - pulse()) / pulses_per_minute()) + minute();
315 return _time_at_pulse (p - pulse()) + minute();
318 /** returns thw whole-note pulse at session frame position f.
319 * @param f the frame position.
320 * @return the position in whole-note pulses corresponding to f
322 * for use with musical units whose granularity is coarser than frames (e.g. ticks)
325 TempoSection::pulse_at_frame (const framepos_t& f) const
327 const bool constant = _type == Constant || _c == 0.0 || (initial() && f < frame());
329 return (minute_at_frame (f - frame()) * pulses_per_minute()) + pulse();
332 return _pulse_at_time (minute_at_frame (f - frame())) + pulse();
336 TempoSection::frame_at_pulse (const double& p) const
338 const bool constant = _type == Constant || _c == 0.0 || (initial() && p < pulse());
340 return frame_at_minute (((p - pulse()) / pulses_per_minute()) + minute());
343 return frame_at_minute (_time_at_pulse (p - pulse()) + minute());
351 Tt----|-----------------*|
352 Ta----|--------------|* |
358 _______________|___|____
359 time a t (next tempo)
362 Duration in beats at time a is the integral of some Tempo function.
363 In our case, the Tempo function (Tempo at time t) is
366 with function constant
371 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
372 b(t) = T0(e^(ct) - 1) / c
374 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:
375 t(b) = log((c.b / T0) + 1) / c
377 The time t at which Tempo T occurs is a as above:
378 t(T) = log(T / T0) / c
380 The beat at which a Tempo T occurs is:
383 The Tempo at which beat b occurs is:
386 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
387 Our problem is that we usually don't know t.
388 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.
389 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
390 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
392 By substituting our expanded t as a in the c function above, our problem is reduced to:
393 c = T0 (e^(log (Ta / T0)) - 1) / b
395 Of course the word 'beat' has been left loosely defined above.
396 In music, a beat is defined by the musical pulse (which comes from the tempo)
397 and the meter in use at a particular time (how many pulse divisions there are in one bar).
398 It would be more accurate to substitute the work 'pulse' for 'beat' above.
402 We can now store c for future time calculations.
403 If the following tempo section (the one that defines c in conjunction with this one)
404 is changed or moved, c is no longer valid.
406 The public methods are session-relative.
408 Most of this stuff is taken from this paper:
411 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
414 Zurich University of Arts
415 Institute for Computer Music and Sound Technology
417 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
421 /** compute this ramp's function constant from some tempo-pulse point
422 * @param end_npm end tempo (in note types per minute)
423 * @param end_pulse duration (pulses into global start) of some other position.
424 * @return the calculated function constant
427 TempoSection::compute_c_pulse (const double& end_npm, const double& end_pulse) const
429 if (note_types_per_minute() == end_npm || _type == Constant) {
433 double const log_tempo_ratio = log (end_npm / note_types_per_minute());
434 return (note_types_per_minute() * expm1 (log_tempo_ratio)) / ((end_pulse - pulse()) * _note_type);
437 /** compute the function constant from some tempo-time point.
438 * @param end_npm tempo (note types/min.)
439 * @param end_minute distance (in minutes) from session origin
440 * @return the calculated function constant
443 TempoSection::compute_c_minute (const double& end_npm, const double& end_minute) const
445 if (note_types_per_minute() == end_npm || _type == Constant) {
449 return c_func (end_npm, end_minute - minute());
452 /* position function */
454 TempoSection::a_func (double end_npm, double c) const
456 return log (end_npm / note_types_per_minute()) / c;
459 /*function constant*/
461 TempoSection::c_func (double end_npm, double end_time) const
463 return log (end_npm / note_types_per_minute()) / end_time;
466 /* tempo in note types per minute at time in minutes */
468 TempoSection::_tempo_at_time (const double& time) const
470 return exp (_c * time) * note_types_per_minute();
473 /* time in minutes at tempo in note types per minute */
475 TempoSection::_time_at_tempo (const double& npm) const
477 return log (npm / note_types_per_minute()) / _c;
480 /* pulse at tempo in note types per minute */
482 TempoSection::_pulse_at_tempo (const double& npm) const
484 return ((npm - note_types_per_minute()) / _c) / _note_type;
487 /* tempo in note types per minute at pulse */
489 TempoSection::_tempo_at_pulse (const double& pulse) const
491 return (pulse * _note_type * _c) + note_types_per_minute();
494 /* pulse at time in minutes */
496 TempoSection::_pulse_at_time (const double& time) const
498 return (expm1 (_c * time) * (note_types_per_minute() / _c)) / _note_type;
501 /* time in minutes at pulse */
503 TempoSection::_time_at_pulse (const double& pulse) const
505 return log1p ((_c * pulse * _note_type) / note_types_per_minute()) / _c;
508 /***********************************************************************/
510 const string MeterSection::xml_state_node_name = "Meter";
512 MeterSection::MeterSection (const XMLNode& node, const framecnt_t sample_rate)
513 : MetricSection (0.0, 0, MusicTime, false, sample_rate), Meter (TempoMap::default_meter())
515 XMLProperty const * prop;
520 framepos_t frame = 0;
521 pair<double, BBT_Time> start;
523 if ((prop = node.property ("start")) != 0) {
524 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
528 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
530 /* legacy session - start used to be in bbt*/
531 info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
536 if ((prop = node.property ("pulse")) != 0) {
537 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
538 error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
543 if ((prop = node.property ("beat")) != 0) {
544 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
545 error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
551 if ((prop = node.property ("bbt")) == 0) {
552 warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
553 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
557 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
558 throw failed_constructor();
564 if ((prop = node.property ("frame")) != 0) {
565 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
566 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
567 throw failed_constructor();
569 set_minute (minute_at_frame (frame));
573 /* beats-per-bar is old; divisions-per-bar is new */
575 if ((prop = node.property ("divisions-per-bar")) == 0) {
576 if ((prop = node.property ("beats-per-bar")) == 0) {
577 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
578 throw failed_constructor();
581 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
582 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
583 throw failed_constructor();
586 if ((prop = node.property ("note-type")) == 0) {
587 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
588 throw failed_constructor();
590 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
591 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
592 throw failed_constructor();
595 if ((prop = node.property ("movable")) == 0) {
596 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
597 throw failed_constructor();
600 set_initial (!string_is_affirmative (prop->value()));
602 if ((prop = node.property ("lock-style")) == 0) {
603 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
605 set_position_lock_style (MusicTime);
607 set_position_lock_style (AudioTime);
610 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
615 MeterSection::get_state() const
617 XMLNode *root = new XMLNode (xml_state_node_name);
621 snprintf (buf, sizeof (buf), "%lf", pulse());
622 root->add_property ("pulse", buf);
623 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
627 root->add_property ("bbt", buf);
628 snprintf (buf, sizeof (buf), "%lf", beat());
629 root->add_property ("beat", buf);
630 snprintf (buf, sizeof (buf), "%lf", _note_type);
631 root->add_property ("note-type", buf);
632 snprintf (buf, sizeof (buf), "%li", frame());
633 root->add_property ("frame", buf);
634 root->add_property ("lock-style", enum_2_string (position_lock_style()));
635 snprintf (buf, sizeof (buf), "%lf", _divisions_per_bar);
636 root->add_property ("divisions-per-bar", buf);
637 snprintf (buf, sizeof (buf), "%s", !initial()?"yes":"no");
638 root->add_property ("movable", buf);
643 /***********************************************************************/
647 Tempo determines the rate of musical pulse determined by its components
648 note types per minute - the rate per minute of the whole note divisor _note_type
649 note type - the division of whole notes (pulses) which occur at the rate of note types per minute.
650 Meter divides the musical pulse into measures and beats according to its components
654 TempoSection - translates between time, musical pulse and tempo.
655 has a musical location in whole notes (pulses).
656 has a time location in minutes.
657 Note that 'beats' in Tempo::note_types_per_minute() are in fact note types per minute.
658 (In the rest of tempo map,'beat' usually refers to accumulated BBT beats (pulse and meter based).
660 MeterSection - translates between BBT, meter-based beat and musical pulse.
661 has a musical location in whole notes (pulses)
662 has a musical location in meter-based beats
663 has a musical location in BBT time
664 has a time location expressed in minutes.
666 TempoSection and MeterSection may be locked to either audio or music (position lock style).
667 The lock style determines the location type to be kept as a reference when location is recalculated.
669 The first tempo and meter are special. they must move together, and are locked to audio.
670 Audio locked tempi which lie before the first meter are made inactive.
672 Recomputing the map is the process where the 'missing' location types are calculated.
673 We construct the tempo map by first using the locked location type of each section
674 to determine non-locked location types (pulse or minute position).
675 We then use this map to find the pulse or minute position of each meter (again depending on lock style).
677 Having done this, we can now traverse the Metrics list by pulse or minute
678 to query its relevant meter/tempo.
680 It is important to keep the _metrics in an order that makes sense.
681 Because ramped MusicTime and AudioTime tempos can interact with each other,
682 reordering is frequent. Care must be taken to keep _metrics in a solved state.
683 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
687 Music and audio-locked objects may seem interchangeable on the surface, but when translating
688 between audio samples and beat, remember that a sample is only a quantised approximation
689 of the actual time (in minutes) of a beat.
690 Thus if a gui user points to the frame occupying the start of a music-locked object on 1|3|0, it does not
691 mean that this frame is the actual location in time of 1|3|0.
693 You cannot use a frame measurement to determine beat distance except under special circumstances
694 (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).
696 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
697 sample space the user is operating at to be translated correctly to the object.
699 The current approach is to interpret the supplied frame using the grid division the user has currently selected.
700 If the user has no musical grid set, they are actually operating in sample space (even SMPTE frames are rounded to audio frame), so
701 the supplied audio frame is interpreted as the desired musical location (beat_at_frame()).
703 tldr: Beat, being a function of time, has nothing to do with sample rate, but time quantization can get in the way of precision.
705 When frame_at_beat() is called, the position calculation is performed in pulses and minutes.
706 The result is rounded to audio frames.
707 When beat_at_frame() is called, the frame is converted to minutes, with no rounding performed on the result.
710 frame_at_beat (beat_at_frame (frame)) == frame
712 beat_at_frame (frame_at_beat (beat)) != beat due to the time quantization of frame_at_beat().
714 Doing the second one will result in a beat distance error of up to 0.5 audio samples.
715 frames_between_quarter_notes () eliminats this effect when determining time duration
716 from Beats distance, or instead work in quarter-notes and/or beats and convert to frames last.
718 The above pointless example could instead do:
719 beat_at_quarter_note (quarter_note_at_beat (beat)) to avoid rounding.
721 The Shaggs - Things I Wonder
722 https://www.youtube.com/watch?v=9wQK6zMJOoQ
725 struct MetricSectionSorter {
726 bool operator() (const MetricSection* a, const MetricSection* b) {
727 return a->pulse() < b->pulse();
731 struct MetricSectionFrameSorter {
732 bool operator() (const MetricSection* a, const MetricSection* b) {
733 return a->frame() < b->frame();
737 TempoMap::TempoMap (framecnt_t fr)
740 BBT_Time start (1, 1, 0);
742 TempoSection *t = new TempoSection (0.0, 0.0, _default_tempo.note_types_per_minute(), _default_tempo.note_type(), TempoSection::Ramp, AudioTime, fr);
743 MeterSection *m = new MeterSection (0.0, 0.0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime, fr);
745 t->set_initial (true);
746 t->set_locked_to_meter (true);
748 m->set_initial (true);
750 /* note: frame time is correct (zero) for both of these */
752 _metrics.push_back (t);
753 _metrics.push_back (m);
757 TempoMap::TempoMap (TempoMap const & other)
759 _frame_rate = other._frame_rate;
760 for (Metrics::const_iterator m = other._metrics.begin(); m != other._metrics.end(); ++m) {
761 TempoSection* ts = dynamic_cast<TempoSection*> (*m);
762 MeterSection* ms = dynamic_cast<MeterSection*> (*m);
765 TempoSection* new_section = new TempoSection (*ts);
766 _metrics.push_back (new_section);
768 MeterSection* new_section = new MeterSection (*ms);
769 _metrics.push_back (new_section);
775 TempoMap::operator= (TempoMap const & other)
777 if (&other != this) {
778 _frame_rate = other._frame_rate;
780 Metrics::const_iterator d = _metrics.begin();
781 while (d != _metrics.end()) {
787 for (Metrics::const_iterator m = other._metrics.begin(); m != other._metrics.end(); ++m) {
788 TempoSection* ts = dynamic_cast<TempoSection*> (*m);
789 MeterSection* ms = dynamic_cast<MeterSection*> (*m);
792 TempoSection* new_section = new TempoSection (*ts);
793 _metrics.push_back (new_section);
795 MeterSection* new_section = new MeterSection (*ms);
796 _metrics.push_back (new_section);
801 PropertyChanged (PropertyChange());
806 TempoMap::~TempoMap ()
808 Metrics::const_iterator d = _metrics.begin();
809 while (d != _metrics.end()) {
817 TempoMap::frame_at_minute (const double time) const
819 return (framepos_t) floor ((time * 60.0 * _frame_rate) + 0.5);
823 TempoMap::minute_at_frame (const framepos_t frame) const
825 return (frame / (double) _frame_rate) / 60.0;
829 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
831 bool removed = false;
834 Glib::Threads::RWLock::WriterLock lm (lock);
835 if ((removed = remove_tempo_locked (tempo))) {
836 if (complete_operation) {
837 recompute_map (_metrics);
842 if (removed && complete_operation) {
843 PropertyChanged (PropertyChange ());
848 TempoMap::remove_tempo_locked (const TempoSection& tempo)
852 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
853 if (dynamic_cast<TempoSection*> (*i) != 0) {
854 if (tempo.frame() == (*i)->frame()) {
855 if (!(*i)->initial()) {
868 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
870 bool removed = false;
873 Glib::Threads::RWLock::WriterLock lm (lock);
874 if ((removed = remove_meter_locked (tempo))) {
875 if (complete_operation) {
876 recompute_map (_metrics);
881 if (removed && complete_operation) {
882 PropertyChanged (PropertyChange ());
887 TempoMap::remove_meter_locked (const MeterSection& meter)
890 if (meter.position_lock_style() == AudioTime) {
891 /* remove meter-locked tempo */
892 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
894 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
895 if (t->locked_to_meter() && meter.frame() == (*i)->frame()) {
904 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
905 if (dynamic_cast<MeterSection*> (*i) != 0) {
906 if (meter.frame() == (*i)->frame()) {
907 if (!(*i)->initial()) {
920 TempoMap::do_insert (MetricSection* section)
922 bool need_add = true;
923 /* we only allow new meters to be inserted on beat 1 of an existing
927 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
929 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
931 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
932 corrected.second.beats = 1;
933 corrected.second.ticks = 0;
934 corrected.first = beat_at_bbt_locked (_metrics, corrected.second);
935 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
936 m->bbt(), corrected.second) << endmsg;
937 //m->set_pulse (corrected);
941 /* Look for any existing MetricSection that is of the same type and
942 in the same bar as the new one, and remove it before adding
943 the new one. Note that this means that if we find a matching,
944 existing section, we can break out of the loop since we're
945 guaranteed that there is only one such match.
948 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
950 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
951 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
952 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
953 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
955 if (tempo && insert_tempo) {
958 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
959 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
961 if (tempo->initial()) {
963 /* can't (re)move this section, so overwrite
964 * its data content (but not its properties as
968 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
969 (*i)->set_position_lock_style (AudioTime);
971 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
972 t->set_type (insert_tempo->type());
982 } else if (meter && insert_meter) {
986 bool const ipm = insert_meter->position_lock_style() == MusicTime;
988 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
990 if (meter->initial()) {
992 /* can't (re)move this section, so overwrite
993 * its data content (but not its properties as
997 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
998 (*i)->set_position_lock_style (AudioTime);
1008 /* non-matching types, so we don't care */
1012 /* Add the given MetricSection, if we didn't just reset an existing
1017 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
1018 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
1019 Metrics::iterator i;
1022 TempoSection* prev_t = 0;
1024 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1025 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
1026 bool const ipm = insert_meter->position_lock_style() == MusicTime;
1029 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
1033 if (prev_t && prev_t->locked_to_meter() && (!ipm && prev_t->frame() == insert_meter->frame())) {
1037 prev_t = dynamic_cast<TempoSection*> (*i);
1040 } else if (insert_tempo) {
1041 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1042 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
1045 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
1046 const bool lm = insert_tempo->locked_to_meter();
1047 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())
1048 || (lm && tempo->pulse() > insert_tempo->pulse())) {
1055 _metrics.insert (i, section);
1059 /* user supplies the exact pulse if pls == MusicTime */
1061 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, ARDOUR::TempoSection::Type type, PositionLockStyle pls)
1063 if (tempo.note_types_per_minute() <= 0.0) {
1064 warning << "Cannot add tempo. note types per minute must be greater than zero." << endmsg;
1068 TempoSection* ts = 0;
1070 Glib::Threads::RWLock::WriterLock lm (lock);
1071 ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), type, pls, true);
1075 PropertyChanged (PropertyChange ());
1081 TempoMap::replace_tempo (TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, TempoSection::Type type, PositionLockStyle pls)
1083 if (tempo.note_types_per_minute() <= 0.0) {
1084 warning << "Cannot replace tempo. note types per minute must be greater than zero." << endmsg;
1088 const bool locked_to_meter = ts.locked_to_meter();
1091 Glib::Threads::RWLock::WriterLock lm (lock);
1092 TempoSection& first (first_tempo());
1093 if (!ts.initial()) {
1094 if (locked_to_meter) {
1097 /* cannot move a meter-locked tempo section */
1098 *static_cast<Tempo*>(&ts) = tempo;
1099 recompute_map (_metrics);
1102 remove_tempo_locked (ts);
1103 add_tempo_locked (tempo, pulse, minute_at_frame (frame), type, pls, true, locked_to_meter);
1106 first.set_type (type);
1107 first.set_pulse (0.0);
1108 first.set_minute (minute_at_frame (frame));
1109 first.set_position_lock_style (AudioTime);
1110 first.set_locked_to_meter (true);
1112 /* cannot move the first tempo section */
1113 *static_cast<Tempo*>(&first) = tempo;
1114 recompute_map (_metrics);
1119 PropertyChanged (PropertyChange ());
1123 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, double minute
1124 , TempoSection::Type type, PositionLockStyle pls, bool recompute, bool locked_to_meter)
1126 TempoSection* t = new TempoSection (pulse, minute, tempo.note_types_per_minute(), tempo.note_type(), type, pls, _frame_rate);
1127 t->set_locked_to_meter (locked_to_meter);
1128 bool solved = false;
1133 if (pls == AudioTime) {
1134 solved = solve_map_minute (_metrics, t, t->minute());
1136 solved = solve_map_pulse (_metrics, t, t->pulse());
1138 recompute_meters (_metrics);
1141 if (!solved && recompute) {
1142 recompute_map (_metrics);
1149 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1151 MeterSection* m = 0;
1153 Glib::Threads::RWLock::WriterLock lm (lock);
1154 m = add_meter_locked (meter, beat, where, frame, pls, true);
1159 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1164 PropertyChanged (PropertyChange ());
1169 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1172 Glib::Threads::RWLock::WriterLock lm (lock);
1173 const double beat = beat_at_bbt_locked (_metrics, where);
1175 if (!ms.initial()) {
1176 remove_meter_locked (ms);
1177 add_meter_locked (meter, beat, where, frame, pls, true);
1179 MeterSection& first (first_meter());
1180 TempoSection& first_t (first_tempo());
1181 /* cannot move the first meter section */
1182 *static_cast<Meter*>(&first) = meter;
1183 first.set_position_lock_style (AudioTime);
1184 first.set_pulse (0.0);
1185 first.set_minute (minute_at_frame (frame));
1186 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1187 first.set_beat (beat);
1188 first_t.set_minute (first.minute());
1189 first_t.set_locked_to_meter (true);
1190 first_t.set_pulse (0.0);
1191 first_t.set_position_lock_style (AudioTime);
1192 recompute_map (_metrics);
1196 PropertyChanged (PropertyChange ());
1200 TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute)
1202 const MeterSection& prev_m = meter_section_at_minute_locked (_metrics, minute_at_beat_locked (_metrics, beat) - minute_at_frame (1));
1203 const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1204 const double time_minutes = minute_at_pulse_locked (_metrics, pulse);
1205 TempoSection* mlt = 0;
1207 if (pls == AudioTime) {
1208 /* add meter-locked tempo */
1209 mlt = add_tempo_locked (tempo_at_minute_locked (_metrics, time_minutes), pulse, minute_at_frame (frame), TempoSection::Ramp, AudioTime, true, true);
1217 MeterSection* new_meter = new MeterSection (pulse, minute_at_frame (frame), beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls, _frame_rate);
1219 bool solved = false;
1221 do_insert (new_meter);
1225 if (pls == AudioTime) {
1226 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (frame));
1227 /* we failed, most likely due to some impossible frame requirement wrt audio-locked tempi.
1228 fudge frame so that the meter ends up at its BBT position instead.
1231 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (prev_m.frame() + 1));
1234 solved = solve_map_bbt (_metrics, new_meter, where);
1235 /* required due to resetting the pulse of meter-locked tempi above.
1236 Arguably solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
1237 but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
1239 recompute_map (_metrics);
1243 if (!solved && recompute) {
1244 /* if this has failed to solve, there is little we can do other than to ensure that
1245 the new map is recalculated.
1247 warning << "Adding meter may have left the tempo map unsolved." << endmsg;
1248 recompute_map (_metrics);
1255 TempoMap::change_initial_tempo (double note_types_per_minute, double note_type)
1257 Tempo newtempo (note_types_per_minute, note_type);
1260 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1261 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1266 Glib::Threads::RWLock::WriterLock lm (lock);
1267 *((Tempo*) t) = newtempo;
1268 recompute_map (_metrics);
1270 PropertyChanged (PropertyChange ());
1277 TempoMap::change_existing_tempo_at (framepos_t where, double note_types_per_minute, double note_type)
1279 Tempo newtempo (note_types_per_minute, note_type);
1282 TempoSection* first;
1283 Metrics::iterator i;
1285 /* find the TempoSection immediately preceding "where"
1288 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1290 if ((*i)->frame() > where) {
1296 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1309 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1319 Glib::Threads::RWLock::WriterLock lm (lock);
1320 /* cannot move the first tempo section */
1321 *((Tempo*)prev) = newtempo;
1322 recompute_map (_metrics);
1325 PropertyChanged (PropertyChange ());
1329 TempoMap::first_meter () const
1331 const MeterSection *m = 0;
1333 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1334 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1339 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1340 abort(); /*NOTREACHED*/
1345 TempoMap::first_meter ()
1347 MeterSection *m = 0;
1349 /* CALLER MUST HOLD LOCK */
1351 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1352 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1357 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1358 abort(); /*NOTREACHED*/
1363 TempoMap::first_tempo () const
1365 const TempoSection *t = 0;
1367 /* CALLER MUST HOLD LOCK */
1369 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1370 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1380 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1381 abort(); /*NOTREACHED*/
1386 TempoMap::first_tempo ()
1388 TempoSection *t = 0;
1390 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1391 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1401 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1402 abort(); /*NOTREACHED*/
1406 TempoMap::recompute_tempi (Metrics& metrics)
1408 TempoSection* prev_t = 0;
1410 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1413 if ((*i)->is_tempo()) {
1414 t = static_cast<TempoSection*> (*i);
1426 if (t->position_lock_style() == AudioTime) {
1427 prev_t->set_c (prev_t->compute_c_minute (t->note_types_per_minute(), t->minute()));
1428 if (!t->locked_to_meter()) {
1429 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
1433 prev_t->set_c (prev_t->compute_c_pulse (t->note_types_per_minute(), t->pulse()));
1434 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
1442 prev_t->set_c (0.0);
1445 /* tempos must be positioned correctly.
1446 the current approach is to use a meter's bbt time as its base position unit.
1447 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1448 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1451 TempoMap::recompute_meters (Metrics& metrics)
1453 MeterSection* meter = 0;
1454 MeterSection* prev_m = 0;
1456 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1457 if (!(*mi)->is_tempo()) {
1458 meter = static_cast<MeterSection*> (*mi);
1459 if (meter->position_lock_style() == AudioTime) {
1461 pair<double, BBT_Time> b_bbt;
1462 TempoSection* meter_locked_tempo = 0;
1463 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1465 if ((*ii)->is_tempo()) {
1466 t = static_cast<TempoSection*> (*ii);
1467 if (t->locked_to_meter() && t->frame() == meter->frame()) {
1468 meter_locked_tempo = t;
1475 double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1476 if (beats + prev_m->beat() != meter->beat()) {
1477 /* reordering caused a bbt change */
1479 beats = meter->beat() - prev_m->beat();
1480 b_bbt = make_pair (beats + prev_m->beat()
1481 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1482 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1484 } else if (!meter->initial()) {
1485 b_bbt = make_pair (meter->beat(), meter->bbt());
1486 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1489 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1491 if (meter_locked_tempo) {
1492 meter_locked_tempo->set_pulse (pulse);
1494 meter->set_beat (b_bbt);
1495 meter->set_pulse (pulse);
1500 pair<double, BBT_Time> b_bbt;
1502 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1503 if (beats + prev_m->beat() != meter->beat()) {
1504 /* reordering caused a bbt change */
1505 b_bbt = make_pair (beats + prev_m->beat()
1506 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1508 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1510 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1512 /* shouldn't happen - the first is audio-locked */
1513 pulse = pulse_at_beat_locked (metrics, meter->beat());
1514 b_bbt = make_pair (meter->beat(), meter->bbt());
1517 meter->set_beat (b_bbt);
1518 meter->set_pulse (pulse);
1519 meter->set_minute (minute_at_pulse_locked (metrics, pulse));
1528 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1530 /* CALLER MUST HOLD WRITE LOCK */
1532 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1535 /* silly call from Session::process() during startup
1540 recompute_tempi (metrics);
1541 recompute_meters (metrics);
1545 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1547 Glib::Threads::RWLock::ReaderLock lm (lock);
1548 TempoMetric m (first_meter(), first_tempo());
1550 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1551 at something, because we insert the default tempo and meter during
1552 TempoMap construction.
1554 now see if we can find better candidates.
1557 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1559 if ((*i)->frame() > frame) {
1573 /* XX meters only */
1575 TempoMap::metric_at (BBT_Time bbt) const
1577 Glib::Threads::RWLock::ReaderLock lm (lock);
1578 TempoMetric m (first_meter(), first_tempo());
1580 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1581 at something, because we insert the default tempo and meter during
1582 TempoMap construction.
1584 now see if we can find better candidates.
1587 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1589 if (!(*i)->is_tempo()) {
1590 mw = static_cast<MeterSection*> (*i);
1591 BBT_Time section_start (mw->bbt());
1593 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1604 /** Returns the BBT (meter-based) beat corresponding to the supplied frame, possibly returning a negative value.
1605 * @param frame The session frame position.
1606 * @return The beat duration according to the tempo map at the supplied frame.
1608 * If the supplied frame lies before the first meter, the returned beat duration will be negative.
1609 * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
1611 * This function uses both tempo and meter.
1614 TempoMap::beat_at_frame (const framecnt_t& frame) const
1616 Glib::Threads::RWLock::ReaderLock lm (lock);
1618 return beat_at_minute_locked (_metrics, minute_at_frame (frame));
1621 /* This function uses both tempo and meter.*/
1623 TempoMap::beat_at_minute_locked (const Metrics& metrics, const double& minute) const
1625 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
1626 MeterSection* prev_m = 0;
1627 MeterSection* next_m = 0;
1629 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1630 if (!(*i)->is_tempo()) {
1631 if (prev_m && (*i)->minute() > minute) {
1632 next_m = static_cast<MeterSection*> (*i);
1635 prev_m = static_cast<MeterSection*> (*i);
1639 const double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
1641 /* audio locked meters fake their beat */
1642 if (next_m && next_m->beat() < beat) {
1643 return next_m->beat();
1649 /** Returns the frame corresponding to the supplied BBT (meter-based) beat.
1650 * @param beat The BBT (meter-based) beat.
1651 * @return The frame duration according to the tempo map at the supplied BBT (meter-based) beat.
1653 * This function uses both tempo and meter.
1656 TempoMap::frame_at_beat (const double& beat) const
1658 Glib::Threads::RWLock::ReaderLock lm (lock);
1660 return frame_at_minute (minute_at_beat_locked (_metrics, beat));
1663 /* meter & tempo section based */
1665 TempoMap::minute_at_beat_locked (const Metrics& metrics, const double& beat) const
1667 MeterSection* prev_m = 0;
1668 TempoSection* prev_t = 0;
1672 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1673 if (!(*i)->is_tempo()) {
1674 m = static_cast<MeterSection*> (*i);
1675 if (prev_m && m->beat() > beat) {
1685 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1686 if ((*i)->is_tempo()) {
1687 t = static_cast<TempoSection*> (*i);
1693 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1702 return prev_t->minute_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse());
1705 /** Returns a Tempo corresponding to the supplied frame position.
1706 * @param frame The audio frame.
1707 * @return a Tempo according to the tempo map at the supplied frame.
1711 TempoMap::tempo_at_frame (const framepos_t& frame) const
1713 Glib::Threads::RWLock::ReaderLock lm (lock);
1715 return tempo_at_minute_locked (_metrics, minute_at_frame (frame));
1719 TempoMap::tempo_at_minute_locked (const Metrics& metrics, const double& minute) const
1721 TempoSection* prev_t = 0;
1725 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1726 if ((*i)->is_tempo()) {
1727 t = static_cast<TempoSection*> (*i);
1731 if ((prev_t) && t->minute() > minute) {
1732 /* t is the section past frame */
1733 return prev_t->tempo_at_minute (minute);
1739 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type());
1742 /** returns the frame at which the supplied tempo occurs, or
1743 * the frame of the last tempo section (search exhausted)
1744 * only the position of the first occurence will be returned
1748 TempoMap::frame_at_tempo (const Tempo& tempo) const
1750 Glib::Threads::RWLock::ReaderLock lm (lock);
1752 return frame_at_minute (minute_at_tempo_locked (_metrics, tempo));
1756 TempoMap::minute_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1758 TempoSection* prev_t = 0;
1759 const double tempo_bpm = tempo.note_types_per_minute();
1761 Metrics::const_iterator i;
1763 for (i = metrics.begin(); i != metrics.end(); ++i) {
1765 if ((*i)->is_tempo()) {
1766 t = static_cast<TempoSection*> (*i);
1772 const double t_bpm = t->note_types_per_minute();
1774 if (t_bpm == tempo_bpm) {
1779 const double prev_t_bpm = prev_t->note_types_per_minute();
1781 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1782 return prev_t->minute_at_ntpm (prev_t->note_types_per_minute(), prev_t->pulse());
1789 return prev_t->minute();
1793 TempoMap::tempo_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1795 TempoSection* prev_t = 0;
1799 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1800 if ((*i)->is_tempo()) {
1801 t = static_cast<TempoSection*> (*i);
1805 if ((prev_t) && t->pulse() > pulse) {
1806 /* t is the section past frame */
1807 return prev_t->tempo_at_pulse (pulse);
1813 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type());
1817 TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1819 TempoSection* prev_t = 0;
1820 const double tempo_bpm = tempo.note_types_per_minute();
1822 Metrics::const_iterator i;
1824 for (i = metrics.begin(); i != metrics.end(); ++i) {
1826 if ((*i)->is_tempo()) {
1827 t = static_cast<TempoSection*> (*i);
1833 const double t_bpm = t->note_types_per_minute();
1835 if (t_bpm == tempo_bpm) {
1840 const double prev_t_bpm = prev_t->note_types_per_minute();
1842 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1843 return prev_t->pulse_at_ntpm (prev_t->note_types_per_minute(), prev_t->minute());
1850 return prev_t->pulse();
1853 /** Returns a Tempo corresponding to the supplied position in quarter-note beats.
1854 * @param qn the position in quarter note beats.
1855 * @return the Tempo at the supplied quarter-note.
1858 TempoMap::tempo_at_quarter_note (const double& qn) const
1860 Glib::Threads::RWLock::ReaderLock lm (lock);
1862 return tempo_at_pulse_locked (_metrics, qn / 4.0);
1865 /** Returns the position in quarter-note beats corresponding to the supplied Tempo.
1866 * @param tempo the tempo.
1867 * @return the position in quarter-note beats where the map bpm
1868 * is equal to that of the Tempo. currently ignores note_type.
1871 TempoMap::quarter_note_at_tempo (const Tempo& tempo) const
1873 Glib::Threads::RWLock::ReaderLock lm (lock);
1875 return pulse_at_tempo_locked (_metrics, tempo) * 4.0;;
1878 /** Returns the whole-note pulse corresponding to the supplied BBT (meter-based) beat.
1879 * @param metrics the list of metric sections used to calculate the pulse.
1880 * @param beat The BBT (meter-based) beat.
1881 * @return the whole-note pulse at the supplied BBT (meter-based) beat.
1883 * a pulse or whole note is the base musical position of a MetricSection.
1884 * it is equivalent to four quarter notes.
1888 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1890 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1892 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1895 /** Returns the BBT (meter-based) beat corresponding to the supplied whole-note pulse .
1896 * @param metrics the list of metric sections used to calculate the beat.
1897 * @param pulse the whole-note pulse.
1898 * @return the meter-based beat at the supplied whole-note pulse.
1900 * a pulse or whole note is the base musical position of a MetricSection.
1901 * it is equivalent to four quarter notes.
1904 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1906 MeterSection* prev_m = 0;
1908 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1910 if (!(*i)->is_tempo()) {
1911 m = static_cast<MeterSection*> (*i);
1912 if (prev_m && m->pulse() > pulse) {
1920 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1924 /* tempo section based */
1926 TempoMap::pulse_at_minute_locked (const Metrics& metrics, const double& minute) const
1928 /* HOLD (at least) THE READER LOCK */
1929 TempoSection* prev_t = 0;
1931 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1933 if ((*i)->is_tempo()) {
1934 t = static_cast<TempoSection*> (*i);
1938 if (prev_t && t->minute() > minute) {
1939 /*the previous ts is the one containing the frame */
1940 const double ret = prev_t->pulse_at_minute (minute);
1941 /* audio locked section in new meter*/
1942 if (t->pulse() < ret) {
1951 /* treated as constant for this ts */
1952 const double pulses_in_section = ((minute - prev_t->minute()) * prev_t->note_types_per_minute()) / prev_t->note_type();
1954 return pulses_in_section + prev_t->pulse();
1957 /* tempo section based */
1959 TempoMap::minute_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1961 /* HOLD THE READER LOCK */
1963 const TempoSection* prev_t = 0;
1965 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1968 if ((*i)->is_tempo()) {
1969 t = static_cast<TempoSection*> (*i);
1973 if (prev_t && t->pulse() > pulse) {
1974 return prev_t->minute_at_pulse (pulse);
1980 /* must be treated as constant, irrespective of _type */
1981 double const dtime = ((pulse - prev_t->pulse()) * prev_t->note_type()) / prev_t->note_types_per_minute();
1983 return dtime + prev_t->minute();
1986 /** Returns the BBT (meter-based) beat corresponding to the supplied BBT time.
1987 * @param bbt The BBT time (meter-based).
1988 * @return bbt The BBT beat (meter-based) at the supplied BBT time.
1992 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
1994 Glib::Threads::RWLock::ReaderLock lm (lock);
1995 return beat_at_bbt_locked (_metrics, bbt);
2000 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2002 /* CALLER HOLDS READ LOCK */
2004 MeterSection* prev_m = 0;
2006 /* because audio-locked meters have 'fake' integral beats,
2007 there is no pulse offset here.
2011 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2012 if (!(*i)->is_tempo()) {
2013 m = static_cast<MeterSection*> (*i);
2015 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
2016 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
2024 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2025 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
2026 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
2031 /** Returns the BBT time corresponding to the supplied BBT (meter-based) beat.
2032 * @param beat The BBT (meter-based) beat.
2033 * @return The BBT time (meter-based) at the supplied meter-based beat.
2037 TempoMap::bbt_at_beat (const double& beat)
2039 Glib::Threads::RWLock::ReaderLock lm (lock);
2040 return bbt_at_beat_locked (_metrics, beat);
2044 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
2046 /* CALLER HOLDS READ LOCK */
2047 MeterSection* prev_m = 0;
2048 const double beats = max (0.0, b);
2050 MeterSection* m = 0;
2052 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2053 if (!(*i)->is_tempo()) {
2054 m = static_cast<MeterSection*> (*i);
2056 if (m->beat() > beats) {
2057 /* this is the meter after the one our beat is on*/
2067 const double beats_in_ms = beats - prev_m->beat();
2068 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2069 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2070 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2071 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2075 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2076 ret.beats = (uint32_t) floor (remaining_beats);
2077 ret.bars = total_bars;
2079 /* 0 0 0 to 1 1 0 - based mapping*/
2083 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2085 ret.ticks -= BBT_Time::ticks_per_beat;
2088 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2096 /** Returns the quarter-note beat corresponding to the supplied BBT time (meter-based).
2097 * @param bbt The BBT time (meter-based).
2098 * @return the quarter note beat at the supplied BBT time
2100 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2102 * while the input uses meter, the output does not.
2105 TempoMap::quarter_note_at_bbt (const Timecode::BBT_Time& bbt)
2107 Glib::Threads::RWLock::ReaderLock lm (lock);
2109 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2113 TempoMap::quarter_note_at_bbt_rt (const Timecode::BBT_Time& bbt)
2115 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2118 throw std::logic_error ("TempoMap::quarter_note_at_bbt_rt() could not lock tempo map");
2121 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2125 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2127 /* CALLER HOLDS READ LOCK */
2129 MeterSection* prev_m = 0;
2131 /* because audio-locked meters have 'fake' integral beats,
2132 there is no pulse offset here.
2136 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2137 if (!(*i)->is_tempo()) {
2138 m = static_cast<MeterSection*> (*i);
2140 if (m->bbt().bars > bbt.bars) {
2148 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2149 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
2150 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
2155 /** Returns the BBT time corresponding to the supplied quarter-note beat.
2156 * @param qn the quarter-note beat.
2157 * @return The BBT time (meter-based) at the supplied meter-based beat.
2159 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2163 TempoMap::bbt_at_quarter_note (const double& qn)
2165 Glib::Threads::RWLock::ReaderLock lm (lock);
2167 return bbt_at_pulse_locked (_metrics, qn / 4.0);
2170 /** Returns the BBT time (meter-based) corresponding to the supplied whole-note pulse position.
2171 * @param metrics The list of metric sections used to determine the result.
2172 * @param pulse The whole-note pulse.
2173 * @return The BBT time at the supplied whole-note pulse.
2175 * a pulse or whole note is the basic musical position of a MetricSection.
2176 * it is equivalent to four quarter notes.
2177 * while the output uses meter, the input does not.
2180 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2182 MeterSection* prev_m = 0;
2184 MeterSection* m = 0;
2186 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2188 if (!(*i)->is_tempo()) {
2189 m = static_cast<MeterSection*> (*i);
2192 double const pulses_to_m = m->pulse() - prev_m->pulse();
2193 if (prev_m->pulse() + pulses_to_m > pulse) {
2194 /* this is the meter after the one our beat is on*/
2205 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
2206 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2207 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2208 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2209 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2213 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2214 ret.beats = (uint32_t) floor (remaining_beats);
2215 ret.bars = total_bars;
2217 /* 0 0 0 to 1 1 0 mapping*/
2221 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2223 ret.ticks -= BBT_Time::ticks_per_beat;
2226 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2234 /** Returns the BBT time corresponding to the supplied frame position.
2235 * @param frame the position in audio samples.
2236 * @return the BBT time at the frame position .
2240 TempoMap::bbt_at_frame (framepos_t frame)
2248 warning << string_compose (_("tempo map was asked for BBT time at frame %1\n"), frame) << endmsg;
2253 const double minute = minute_at_frame (frame);
2255 Glib::Threads::RWLock::ReaderLock lm (lock);
2257 return bbt_at_minute_locked (_metrics, minute);
2261 TempoMap::bbt_at_frame_rt (framepos_t frame)
2263 const double minute = minute_at_frame (frame);
2265 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2268 throw std::logic_error ("TempoMap::bbt_at_frame_rt() could not lock tempo map");
2271 return bbt_at_minute_locked (_metrics, minute);
2275 TempoMap::bbt_at_minute_locked (const Metrics& metrics, const double& minute) const
2285 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
2286 MeterSection* prev_m = 0;
2287 MeterSection* next_m = 0;
2291 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2292 if (!(*i)->is_tempo()) {
2293 m = static_cast<MeterSection*> (*i);
2294 if (prev_m && m->minute() > minute) {
2302 double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
2304 /* handle frame before first meter */
2305 if (minute < prev_m->minute()) {
2308 /* audio locked meters fake their beat */
2309 if (next_m && next_m->beat() < beat) {
2310 beat = next_m->beat();
2313 beat = max (0.0, beat);
2315 const double beats_in_ms = beat - prev_m->beat();
2316 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2317 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2318 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2319 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2323 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2324 ret.beats = (uint32_t) floor (remaining_beats);
2325 ret.bars = total_bars;
2327 /* 0 0 0 to 1 1 0 - based mapping*/
2331 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2333 ret.ticks -= BBT_Time::ticks_per_beat;
2336 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2344 /** Returns the frame position corresponding to the supplied BBT time.
2345 * @param bbt the position in BBT time.
2346 * @return the frame position at bbt.
2350 TempoMap::frame_at_bbt (const BBT_Time& bbt)
2354 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
2359 if (bbt.beats < 1) {
2360 throw std::logic_error ("beats are counted from one");
2365 Glib::Threads::RWLock::ReaderLock lm (lock);
2366 minute = minute_at_bbt_locked (_metrics, bbt);
2369 return frame_at_minute (minute);
2372 /* meter & tempo section based */
2374 TempoMap::minute_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2376 /* HOLD THE READER LOCK */
2378 const double ret = minute_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2383 * Returns the quarter-note beat position corresponding to the supplied frame.
2385 * @param frame the position in frames.
2386 * @return The quarter-note position of the supplied frame. Ignores meter.
2390 TempoMap::quarter_note_at_frame (const framepos_t frame) const
2392 const double minute = minute_at_frame (frame);
2394 Glib::Threads::RWLock::ReaderLock lm (lock);
2396 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2400 TempoMap::quarter_note_at_frame_rt (const framepos_t frame) const
2402 const double minute = minute_at_frame (frame);
2404 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2407 throw std::logic_error ("TempoMap::quarter_note_at_frame_rt() could not lock tempo map");
2410 return pulse_at_minute_locked (_metrics, minute) * 4.0;
2414 * Returns the frame position corresponding to the supplied quarter-note beat.
2416 * @param quarter_note the quarter-note position.
2417 * @return the frame position of the supplied quarter-note. Ignores meter.
2422 TempoMap::frame_at_quarter_note (const double quarter_note) const
2426 Glib::Threads::RWLock::ReaderLock lm (lock);
2428 minute = minute_at_pulse_locked (_metrics, quarter_note / 4.0);
2431 return frame_at_minute (minute);
2434 /** Returns the quarter-note beats corresponding to the supplied BBT (meter-based) beat.
2435 * @param beat The BBT (meter-based) beat.
2436 * @return The quarter-note position of the supplied BBT (meter-based) beat.
2438 * a quarter-note may be compared with and assigned to Evoral::Beats.
2442 TempoMap::quarter_note_at_beat (const double beat) const
2444 Glib::Threads::RWLock::ReaderLock lm (lock);
2446 return pulse_at_beat_locked (_metrics, beat) * 4.0;
2449 /** Returns the BBT (meter-based) beat position corresponding to the supplied quarter-note beats.
2450 * @param quarter_note The position in quarter-note beats.
2451 * @return the BBT (meter-based) beat position of the supplied quarter-note beats.
2453 * a quarter-note is the musical unit of Evoral::Beats.
2457 TempoMap::beat_at_quarter_note (const double quarter_note) const
2459 Glib::Threads::RWLock::ReaderLock lm (lock);
2461 return beat_at_pulse_locked (_metrics, quarter_note / 4.0);
2464 /** Returns the duration in frames between two supplied quarter-note beat positions.
2465 * @param start the first position in quarter-note beats.
2466 * @param end the end position in quarter-note beats.
2467 * @return the frame distance ober the quarter-note beats duration.
2469 * use this rather than e.g.
2470 * frame_at-quarter_note (end_beats) - frame_at_quarter_note (start_beats).
2471 * frames_between_quarter_notes() doesn't round to audio frames as an intermediate step,
2475 TempoMap::frames_between_quarter_notes (const double start, const double end) const
2480 Glib::Threads::RWLock::ReaderLock lm (lock);
2481 minutes = minutes_between_quarter_notes_locked (_metrics, start, end);
2484 return frame_at_minute (minutes);
2488 TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end) const
2491 return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
2495 TempoMap::quarter_notes_between_frames (const framecnt_t start, const framecnt_t end) const
2497 Glib::Threads::RWLock::ReaderLock lm (lock);
2499 return quarter_notes_between_frames_locked (_metrics, start, end);
2503 TempoMap::quarter_notes_between_frames_locked (const Metrics& metrics, const framecnt_t start, const framecnt_t end) const
2505 const TempoSection* prev_t = 0;
2507 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2510 if ((*i)->is_tempo()) {
2511 t = static_cast<TempoSection*> (*i);
2515 if (prev_t && t->frame() > start) {
2522 const double start_qn = prev_t->pulse_at_frame (start);
2524 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2527 if ((*i)->is_tempo()) {
2528 t = static_cast<TempoSection*> (*i);
2532 if (prev_t && t->frame() > end) {
2538 const double end_qn = prev_t->pulse_at_frame (end);
2540 return (end_qn - start_qn) * 4.0;
2544 TempoMap::check_solved (const Metrics& metrics) const
2546 TempoSection* prev_t = 0;
2547 MeterSection* prev_m = 0;
2549 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2552 if ((*i)->is_tempo()) {
2553 t = static_cast<TempoSection*> (*i);
2558 /* check ordering */
2559 if ((t->minute() <= prev_t->minute()) || (t->pulse() <= prev_t->pulse())) {
2563 /* precision check ensures tempo and frames align.*/
2564 if (t->frame() != frame_at_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()))) {
2565 if (!t->locked_to_meter()) {
2570 /* gradient limit - who knows what it should be?
2571 things are also ok (if a little chaotic) without this
2573 if (fabs (prev_t->c()) > 1000.0) {
2574 //std::cout << "c : " << prev_t->c() << std::endl;
2581 if (!(*i)->is_tempo()) {
2582 m = static_cast<MeterSection*> (*i);
2583 if (prev_m && m->position_lock_style() == AudioTime) {
2584 const TempoSection* t = &tempo_section_at_minute_locked (metrics, minute_at_frame (m->frame() - 1));
2585 const framepos_t nascent_m_frame = frame_at_minute (t->minute_at_pulse (m->pulse()));
2586 /* Here we check that a preceding section of music doesn't overlap a subsequent one.
2588 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
2602 TempoMap::set_active_tempi (const Metrics& metrics, const framepos_t& frame)
2604 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2606 if ((*i)->is_tempo()) {
2607 t = static_cast<TempoSection*> (*i);
2608 if (t->locked_to_meter()) {
2609 t->set_active (true);
2610 } else if (t->position_lock_style() == AudioTime) {
2611 if (t->frame() < frame) {
2612 t->set_active (false);
2613 t->set_pulse (-1.0);
2614 } else if (t->frame() > frame) {
2615 t->set_active (true);
2616 } else if (t->frame() == frame) {
2626 TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const double& minute)
2628 TempoSection* prev_t = 0;
2629 TempoSection* section_prev = 0;
2630 double first_m_minute = 0.0;
2631 const bool sml = section->locked_to_meter();
2633 /* can't move a tempo before the first meter */
2634 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2636 if (!(*i)->is_tempo()) {
2637 m = static_cast<MeterSection*> (*i);
2639 first_m_minute = m->minute();
2644 if (!section->initial() && minute <= first_m_minute) {
2648 section->set_active (true);
2649 section->set_minute (minute);
2651 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2653 if ((*i)->is_tempo()) {
2654 t = static_cast<TempoSection*> (*i);
2666 if (t->frame() == frame_at_minute (minute)) {
2670 const bool tlm = t->position_lock_style() == MusicTime;
2672 if (prev_t && !section_prev && ((sml && tlm && t->pulse() > section->pulse()) || (!tlm && t->minute() > minute))) {
2673 section_prev = prev_t;
2675 section_prev->set_c (section_prev->compute_c_minute (section->note_types_per_minute(), minute));
2676 if (!section->locked_to_meter()) {
2677 section->set_pulse (section_prev->pulse_at_ntpm (section->note_types_per_minute(), minute));
2682 if (t->position_lock_style() == MusicTime) {
2683 prev_t->set_c (prev_t->compute_c_pulse (t->note_types_per_minute(), t->pulse()));
2684 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
2686 prev_t->set_c (prev_t->compute_c_minute (t->note_types_per_minute(), t->minute()));
2687 if (!t->locked_to_meter()) {
2688 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
2697 recompute_tempi (imaginary);
2699 if (check_solved (imaginary)) {
2702 dunp (imaginary, std::cout);
2706 MetricSectionFrameSorter fcmp;
2707 imaginary.sort (fcmp);
2709 recompute_tempi (imaginary);
2711 if (check_solved (imaginary)) {
2719 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2721 TempoSection* prev_t = 0;
2722 TempoSection* section_prev = 0;
2724 section->set_pulse (pulse);
2726 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2728 if ((*i)->is_tempo()) {
2729 t = static_cast<TempoSection*> (*i);
2740 section_prev = prev_t;
2744 if (t->position_lock_style() == MusicTime) {
2745 prev_t->set_c (prev_t->compute_c_pulse (t->note_types_per_minute(), t->pulse()));
2746 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
2748 prev_t->set_c (prev_t->compute_c_minute (t->note_types_per_minute(), t->minute()));
2749 if (!t->locked_to_meter()) {
2750 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
2759 section_prev->set_c (section_prev->compute_c_pulse (section->note_types_per_minute(), pulse));
2760 section->set_minute (section_prev->minute_at_ntpm (section->note_types_per_minute(), pulse));
2764 recompute_tempi (imaginary);
2766 if (check_solved (imaginary)) {
2769 dunp (imaginary, std::cout);
2773 MetricSectionSorter cmp;
2774 imaginary.sort (cmp);
2776 recompute_tempi (imaginary);
2778 * XX need a restriction here, but only for this case,
2779 * as audio locked tempos don't interact in the same way.
2781 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2783 * |50 bpm |250 bpm |60 bpm
2784 * drag 250 to the pulse after 60->
2785 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2787 if (check_solved (imaginary)) {
2795 TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const double& minute)
2797 /* disallow moving first meter past any subsequent one, and any initial meter before the first one */
2798 const MeterSection* other = &meter_section_at_minute_locked (imaginary, minute);
2799 if ((section->initial() && !other->initial()) || (other->initial() && !section->initial() && other->minute() >= minute)) {
2803 if (section->initial()) {
2804 /* lock the first tempo to our first meter */
2805 if (!set_active_tempi (imaginary, frame_at_minute (minute))) {
2810 TempoSection* meter_locked_tempo = 0;
2812 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2814 if ((*ii)->is_tempo()) {
2815 t = static_cast<TempoSection*> (*ii);
2816 if (t->locked_to_meter() && t->frame() == section->frame()) {
2817 meter_locked_tempo = t;
2823 if (!meter_locked_tempo) {
2827 MeterSection* prev_m = 0;
2829 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2830 bool solved = false;
2832 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2834 if (!(*i)->is_tempo()) {
2835 m = static_cast<MeterSection*> (*i);
2837 if (prev_m && !section->initial()) {
2838 const double beats = (pulse_at_minute_locked (imaginary, minute) - prev_m->pulse()) * prev_m->note_divisor();
2839 if (beats + prev_m->beat() < section->beat()) {
2840 /* set the section pulse according to its musical position,
2841 * as an earlier time than this has been requested.
2843 const double new_pulse = ((section->beat() - prev_m->beat())
2844 / prev_m->note_divisor()) + prev_m->pulse();
2846 tempo_copy->set_position_lock_style (MusicTime);
2847 if ((solved = solve_map_pulse (future_map, tempo_copy, new_pulse))) {
2848 meter_locked_tempo->set_position_lock_style (MusicTime);
2849 section->set_position_lock_style (MusicTime);
2850 section->set_pulse (new_pulse);
2851 solve_map_pulse (imaginary, meter_locked_tempo, new_pulse);
2852 meter_locked_tempo->set_position_lock_style (AudioTime);
2853 section->set_position_lock_style (AudioTime);
2854 section->set_minute (meter_locked_tempo->minute());
2860 Metrics::const_iterator d = future_map.begin();
2861 while (d != future_map.end()) {
2870 /* all is ok. set section's locked tempo if allowed.
2871 possibly disallow if there is an adjacent audio-locked tempo.
2872 XX this check could possibly go. its never actually happened here.
2874 MeterSection* meter_copy = const_cast<MeterSection*>
2875 (&meter_section_at_minute_locked (future_map, section->minute()));
2877 meter_copy->set_minute (minute);
2879 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2880 section->set_minute (minute);
2881 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2882 / prev_m->note_divisor()) + prev_m->pulse());
2883 solve_map_minute (imaginary, meter_locked_tempo, minute);
2888 Metrics::const_iterator d = future_map.begin();
2889 while (d != future_map.end()) {
2899 /* initial (first meter atm) */
2901 tempo_copy->set_minute (minute);
2902 tempo_copy->set_pulse (0.0);
2904 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2905 section->set_minute (minute);
2906 meter_locked_tempo->set_minute (minute);
2907 meter_locked_tempo->set_pulse (0.0);
2908 solve_map_minute (imaginary, meter_locked_tempo, minute);
2913 Metrics::const_iterator d = future_map.begin();
2914 while (d != future_map.end()) {
2923 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2924 section->set_beat (b_bbt);
2925 section->set_pulse (0.0);
2935 MetricSectionFrameSorter fcmp;
2936 imaginary.sort (fcmp);
2938 recompute_meters (imaginary);
2944 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2946 /* disallow setting section to an existing meter's bbt */
2947 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2949 if (!(*i)->is_tempo()) {
2950 m = static_cast<MeterSection*> (*i);
2951 if (m != section && m->bbt().bars == when.bars) {
2957 MeterSection* prev_m = 0;
2958 MeterSection* section_prev = 0;
2960 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2962 if (!(*i)->is_tempo()) {
2963 m = static_cast<MeterSection*> (*i);
2969 pair<double, BBT_Time> b_bbt;
2970 double new_pulse = 0.0;
2972 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2973 section_prev = prev_m;
2975 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2976 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2977 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2979 section->set_beat (b_bbt);
2980 section->set_pulse (pulse);
2981 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
2985 if (m->position_lock_style() == AudioTime) {
2986 TempoSection* meter_locked_tempo = 0;
2988 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2990 if ((*ii)->is_tempo()) {
2991 t = static_cast<TempoSection*> (*ii);
2992 if (t->locked_to_meter() && t->frame() == m->frame()) {
2993 meter_locked_tempo = t;
2999 if (!meter_locked_tempo) {
3004 double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3006 if (beats + prev_m->beat() != m->beat()) {
3007 /* tempo/ meter change caused a change in beat (bar). */
3009 /* the user has requested that the previous section of music overlaps this one.
3010 we have no choice but to change the bar number here, as being locked to audio means
3011 we must stay where we are on the timeline.
3013 beats = m->beat() - prev_m->beat();
3014 b_bbt = make_pair (beats + prev_m->beat()
3015 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3016 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3018 } else if (!m->initial()) {
3019 b_bbt = make_pair (m->beat(), m->bbt());
3020 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3023 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3026 meter_locked_tempo->set_pulse (new_pulse);
3027 m->set_beat (b_bbt);
3028 m->set_pulse (new_pulse);
3032 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3033 if (beats + prev_m->beat() != m->beat()) {
3034 /* tempo/ meter change caused a change in beat (bar). */
3035 b_bbt = make_pair (beats + prev_m->beat()
3036 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3038 b_bbt = make_pair (beats + prev_m->beat()
3041 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3042 m->set_beat (b_bbt);
3043 m->set_pulse (new_pulse);
3044 m->set_minute (minute_at_pulse_locked (imaginary, new_pulse));
3051 if (!section_prev) {
3053 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
3054 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3055 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
3057 section->set_beat (b_bbt);
3058 section->set_pulse (pulse);
3059 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
3062 MetricSectionSorter cmp;
3063 imaginary.sort (cmp);
3065 recompute_meters (imaginary);
3070 /** places a copy of _metrics into copy and returns a pointer
3071 * to section's equivalent in copy.
3074 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
3076 TempoSection* ret = 0;
3078 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3081 if ((*i)->is_tempo()) {
3082 t = static_cast<TempoSection*> (*i);
3084 ret = new TempoSection (*t);
3085 copy.push_back (ret);
3089 TempoSection* cp = new TempoSection (*t);
3090 copy.push_back (cp);
3092 if (!(*i)->is_tempo()) {
3093 m = static_cast<MeterSection *> (*i);
3094 MeterSection* cp = new MeterSection (*m);
3095 copy.push_back (cp);
3103 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
3105 MeterSection* ret = 0;
3107 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3110 if ((*i)->is_tempo()) {
3111 t = static_cast<TempoSection*> (*i);
3112 TempoSection* cp = new TempoSection (*t);
3113 copy.push_back (cp);
3116 if (!(*i)->is_tempo()) {
3117 m = static_cast<MeterSection *> (*i);
3119 ret = new MeterSection (*m);
3120 copy.push_back (ret);
3123 MeterSection* cp = new MeterSection (*m);
3124 copy.push_back (cp);
3131 /** answers the question "is this a valid beat position for this tempo section?".
3132 * it returns true if the tempo section can be moved to the requested bbt position,
3133 * leaving the tempo map in a solved state.
3134 * @param ts the tempo section to be moved
3135 * @param bbt the requested new position for the tempo section
3136 * @return true if the tempo section can be moved to the position, otherwise false.
3139 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
3142 TempoSection* tempo_copy = 0;
3145 Glib::Threads::RWLock::ReaderLock lm (lock);
3146 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
3152 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
3154 Metrics::const_iterator d = copy.begin();
3155 while (d != copy.end()) {
3164 * 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,
3165 * taking any possible reordering as a consequence of this into account.
3166 * @param section - the section to be altered
3167 * @param bbt - the BBT time where the altered tempo will fall
3168 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
3170 pair<double, framepos_t>
3171 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
3174 pair<double, framepos_t> ret = make_pair (0.0, 0);
3176 Glib::Threads::RWLock::ReaderLock lm (lock);
3178 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
3180 const double beat = beat_at_bbt_locked (future_map, bbt);
3182 if (section->position_lock_style() == AudioTime) {
3183 tempo_copy->set_position_lock_style (MusicTime);
3186 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
3187 ret.first = tempo_copy->pulse();
3188 ret.second = tempo_copy->frame();
3190 ret.first = section->pulse();
3191 ret.second = section->frame();
3194 Metrics::const_iterator d = future_map.begin();
3195 while (d != future_map.end()) {
3202 /** moves a TempoSection to a specified position.
3203 * @param ts - the section to be moved
3204 * @param frame - the new position in frames for the tempo
3205 * @param sub_num - the snap division to use if using musical time.
3207 * if sub_num is non-zero, the frame position is used to calculate an exact
3210 * -1 | snap to bars (meter-based)
3211 * 0 | no snap - use audio frame for musical position
3212 * 1 | snap to meter-based (BBT) beat
3213 * >1 | snap to quarter-note subdivision (i.e. 4 will snap to sixteenth notes)
3215 * this follows the snap convention in the gui.
3216 * if sub_num is zero, the musical position will be taken from the supplied frame.
3219 TempoMap::gui_set_tempo_position (TempoSection* ts, const framepos_t& frame, const int& sub_num)
3223 if (ts->position_lock_style() == MusicTime) {
3225 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
3226 Glib::Threads::RWLock::WriterLock lm (lock);
3227 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3229 tempo_copy->set_position_lock_style (AudioTime);
3231 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3232 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
3233 const double pulse = pulse_at_beat_locked (future_map, beat);
3235 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3236 solve_map_pulse (_metrics, ts, pulse);
3237 recompute_meters (_metrics);
3245 Glib::Threads::RWLock::WriterLock lm (lock);
3246 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3248 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3250 /* We're moving the object that defines the grid while snapping to it...
3251 * Placing the ts at the beat corresponding to the requested frame may shift the
3252 * grid in such a way that the mouse is left hovering over a completerly different division,
3253 * causing jittering when the mouse next moves (esp. large tempo deltas).
3255 * This alters the snap behaviour slightly in that we snap to beat divisions
3256 * in the future map rather than the existing one.
3258 const double qn = exact_qn_at_frame_locked (future_map, frame, sub_num);
3259 const framepos_t snapped_frame = frame_at_minute (minute_at_pulse_locked (future_map, qn / 4.0));
3261 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (snapped_frame))) {
3262 solve_map_minute (_metrics, ts, minute_at_frame (snapped_frame));
3263 ts->set_pulse (qn / 4.0);
3264 recompute_meters (_metrics);
3267 solve_map_minute (_metrics, ts, minute_at_frame (frame));
3268 recompute_meters (_metrics);
3274 Metrics::const_iterator d = future_map.begin();
3275 while (d != future_map.end()) {
3280 MetricPositionChanged (PropertyChange ()); // Emit Signal
3283 /** moves a MeterSection to a specified position.
3284 * @param ms - the section to be moved
3285 * @param frame - the new position in frames for the meter
3287 * as a meter cannot snap to anything but bars,
3288 * the supplied frame is rounded to the nearest bar, possibly
3289 * leaving the meter position unchanged.
3292 TempoMap::gui_set_meter_position (MeterSection* ms, const framepos_t& frame)
3296 if (ms->position_lock_style() == AudioTime) {
3299 Glib::Threads::RWLock::WriterLock lm (lock);
3300 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3302 if (solve_map_minute (future_map, copy, minute_at_frame (frame))) {
3303 solve_map_minute (_metrics, ms, minute_at_frame (frame));
3304 recompute_tempi (_metrics);
3309 Glib::Threads::RWLock::WriterLock lm (lock);
3310 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3312 const double beat = beat_at_minute_locked (_metrics, minute_at_frame (frame));
3313 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
3315 if (solve_map_bbt (future_map, copy, bbt)) {
3316 solve_map_bbt (_metrics, ms, bbt);
3317 recompute_tempi (_metrics);
3322 Metrics::const_iterator d = future_map.begin();
3323 while (d != future_map.end()) {
3328 MetricPositionChanged (PropertyChange ()); // Emit Signal
3332 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
3335 bool can_solve = false;
3337 Glib::Threads::RWLock::WriterLock lm (lock);
3338 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3339 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3340 recompute_tempi (future_map);
3342 if (check_solved (future_map)) {
3343 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3344 recompute_map (_metrics);
3349 Metrics::const_iterator d = future_map.begin();
3350 while (d != future_map.end()) {
3355 MetricPositionChanged (PropertyChange ()); // Emit Signal
3361 TempoMap::gui_stretch_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame)
3364 Ts (future prev_t) Tnext
3367 |----------|----------
3374 Glib::Threads::RWLock::WriterLock lm (lock);
3380 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3381 TempoSection* prev_to_prev_t = 0;
3382 const frameoffset_t fr_off = end_frame - frame;
3386 if (prev_t->pulse() > 0.0) {
3387 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_minute_locked (future_map, minute_at_frame (prev_t->frame() - 1)));
3390 TempoSection* next_t = 0;
3391 for (Metrics::iterator i = future_map.begin(); i != future_map.end(); ++i) {
3392 TempoSection* t = 0;
3393 if ((*i)->is_tempo()) {
3394 t = static_cast<TempoSection*> (*i);
3395 if (t->frame() > ts->frame()) {
3401 /* minimum allowed measurement distance in frames */
3402 const framepos_t min_dframe = 2;
3404 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
3405 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
3407 double contribution = 0.0;
3409 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3410 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3413 const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
3415 const double start_pulse = prev_t->pulse_at_minute (minute_at_frame (frame));
3416 const double end_pulse = prev_t->pulse_at_minute (minute_at_frame (end_frame));
3420 if (prev_t->type() == TempoSection::Constant || prev_t->c() == 0.0) {
3422 if (prev_t->position_lock_style() == MusicTime) {
3423 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3424 if (frame > prev_to_prev_t->frame() + min_dframe && (frame + prev_t_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
3426 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame())
3427 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
3429 new_bpm = prev_t->note_types_per_minute();
3432 /* prev to prev is irrelevant */
3434 if (start_pulse > prev_t->pulse() && end_pulse > prev_t->pulse()) {
3435 new_bpm = prev_t->note_types_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
3437 new_bpm = prev_t->note_types_per_minute();
3442 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3443 if (frame > prev_to_prev_t->frame() + min_dframe && end_frame > prev_to_prev_t->frame() + min_dframe) {
3445 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame())
3446 / (double) ((end_frame) - prev_to_prev_t->frame()));
3448 new_bpm = prev_t->note_types_per_minute();
3451 /* prev_to_prev_t is irrelevant */
3453 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3454 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
3456 new_bpm = prev_t->note_types_per_minute();
3462 double frame_ratio = 1.0;
3463 double pulse_ratio = 1.0;
3464 const double pulse_pos = frame;
3466 if (prev_to_prev_t) {
3467 if (pulse_pos > prev_to_prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_to_prev_t->frame() + min_dframe) {
3468 frame_ratio = (((pulse_pos - fr_off) - prev_to_prev_t->frame()) / (double) ((pulse_pos) - prev_to_prev_t->frame()));
3470 if (end_pulse > prev_to_prev_t->pulse() && start_pulse > prev_to_prev_t->pulse()) {
3471 pulse_ratio = ((start_pulse - prev_to_prev_t->pulse()) / (end_pulse - prev_to_prev_t->pulse()));
3474 if (pulse_pos > prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_t->frame() + min_dframe) {
3475 frame_ratio = (((pulse_pos - fr_off) - prev_t->frame()) / (double) ((pulse_pos) - prev_t->frame()));
3477 pulse_ratio = (start_pulse / end_pulse);
3479 new_bpm = prev_t->note_types_per_minute() * (pulse_ratio * frame_ratio);
3482 /* don't clamp and proceed here.
3483 testing has revealed that this can go negative,
3484 which is an entirely different thing to just being too low.
3486 if (new_bpm < 0.5) {
3489 new_bpm = min (new_bpm, (double) 1000.0);
3490 prev_t->set_note_types_per_minute (new_bpm);
3491 recompute_tempi (future_map);
3492 recompute_meters (future_map);
3494 if (check_solved (future_map)) {
3495 ts->set_note_types_per_minute (new_bpm);
3496 recompute_tempi (_metrics);
3497 recompute_meters (_metrics);
3501 Metrics::const_iterator d = future_map.begin();
3502 while (d != future_map.end()) {
3507 MetricPositionChanged (PropertyChange ()); // Emit Signal
3510 /** Returns the exact bbt-based beat corresponding to the bar, beat or quarter note subdivision nearest to
3511 * the supplied frame, possibly returning a negative value.
3513 * @param frame The session frame position.
3514 * @param sub_num The subdivision to use when rounding the beat.
3515 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3516 * Positive integers indicate quarter note (non BBT) divisions.
3517 * 0 indicates that the returned beat should not be rounded (equivalent to quarter_note_at_frame()).
3518 * @return The beat position of the supplied frame.
3520 * when working to a musical grid, the use of sub_nom indicates that
3521 * the position should be interpreted musically.
3523 * it effectively snaps to meter bars, meter beats or quarter note divisions
3524 * (as per current gui convention) and returns a musical position independent of frame rate.
3526 * If the supplied frame lies before the first meter, the return will be negative,
3527 * in which case the returned beat uses the first meter (for BBT subdivisions) and
3528 * the continuation of the tempo curve (backwards).
3530 * This function is sensitive to tempo and meter.
3533 TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num) const
3535 Glib::Threads::RWLock::ReaderLock lm (lock);
3537 return exact_beat_at_frame_locked (_metrics, frame, sub_num);
3541 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t divisions) const
3543 return beat_at_pulse_locked (_metrics, exact_qn_at_frame_locked (metrics, frame, divisions) / 4.0);
3546 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
3547 * the supplied frame, possibly returning a negative value.
3549 * @param frame The session frame position.
3550 * @param sub_num The subdivision to use when rounding the quarter note.
3551 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3552 * Positive integers indicate quarter note (non BBT) divisions.
3553 * 0 indicates that the returned quarter note should not be rounded (equivalent to quarter_note_at_frame()).
3554 * @return The quarter note position of the supplied frame.
3556 * When working to a musical grid, the use of sub_nom indicates that
3557 * the frame position should be interpreted musically.
3559 * it effectively snaps to meter bars, meter beats or quarter note divisions
3560 * (as per current gui convention) and returns a musical position independent of frame rate.
3562 * If the supplied frame lies before the first meter, the return will be negative,
3563 * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
3564 * the continuation of the tempo curve (backwards).
3566 * This function is tempo-sensitive.
3569 TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num) const
3571 Glib::Threads::RWLock::ReaderLock lm (lock);
3573 return exact_qn_at_frame_locked (_metrics, frame, sub_num);
3577 TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num) const
3579 double qn = pulse_at_minute_locked (metrics, minute_at_frame (frame)) * 4.0;
3582 qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
3583 } else if (sub_num == 1) {
3584 /* the gui requested exact musical (BBT) beat */
3585 qn = pulse_at_beat_locked (metrics, (floor (beat_at_minute_locked (metrics, minute_at_frame (frame)) + 0.5)) * 4.0);
3586 } else if (sub_num == -1) {
3588 Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
3592 const double prev_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3594 const double next_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3596 if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
3606 /** returns the frame duration of the supplied BBT time at a specified frame position in the tempo map.
3607 * @param pos the frame position in the tempo map.
3608 * @param bbt the distance in BBT time from pos to calculate.
3609 * @param dir the rounding direction..
3610 * @return the duration in frames between pos and bbt
3613 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
3615 Glib::Threads::RWLock::ReaderLock lm (lock);
3617 BBT_Time pos_bbt = bbt_at_minute_locked (_metrics, minute_at_frame (pos));
3619 const double divisions = meter_section_at_minute_locked (_metrics, minute_at_frame (pos)).divisions_per_bar();
3622 pos_bbt.bars += bbt.bars;
3624 pos_bbt.ticks += bbt.ticks;
3625 if ((double) pos_bbt.ticks > BBT_Time::ticks_per_beat) {
3627 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3630 pos_bbt.beats += bbt.beats;
3631 if ((double) pos_bbt.beats > divisions) {
3633 pos_bbt.beats -= divisions;
3635 const framecnt_t pos_bbt_frame = frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3637 return pos_bbt_frame - pos;
3641 if (pos_bbt.bars <= bbt.bars) {
3644 pos_bbt.bars -= bbt.bars;
3647 if (pos_bbt.ticks < bbt.ticks) {
3648 if (pos_bbt.bars > 1) {
3649 if (pos_bbt.beats == 1) {
3651 pos_bbt.beats = divisions;
3655 pos_bbt.ticks = BBT_Time::ticks_per_beat - (bbt.ticks - pos_bbt.ticks);
3661 pos_bbt.ticks -= bbt.ticks;
3664 if (pos_bbt.beats <= bbt.beats) {
3665 if (pos_bbt.bars > 1) {
3667 pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
3672 pos_bbt.beats -= bbt.beats;
3675 return pos - frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3682 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
3684 return round_to_type (fr, dir, Bar);
3688 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
3690 return round_to_type (fr, dir, Beat);
3694 TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3696 Glib::Threads::RWLock::ReaderLock lm (lock);
3697 uint32_t ticks = (uint32_t) floor (max (0.0, pulse_at_minute_locked (_metrics, minute_at_frame (fr))) * BBT_Time::ticks_per_beat * 4.0);
3698 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3699 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3701 ticks -= beats * BBT_Time::ticks_per_beat;
3704 /* round to next (or same iff dir == RoundUpMaybe) */
3706 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3708 if (mod == 0 && dir == RoundUpMaybe) {
3709 /* right on the subdivision, which is fine, so do nothing */
3711 } else if (mod == 0) {
3712 /* right on the subdivision, so the difference is just the subdivision ticks */
3713 ticks += ticks_one_subdivisions_worth;
3716 /* not on subdivision, compute distance to next subdivision */
3718 ticks += ticks_one_subdivisions_worth - mod;
3721 //NOTE: this code intentionally limits the rounding so we don't advance to the next beat.
3722 // For the purposes of "jump-to-next-subdivision", we DO want to advance to the next beat.
3723 // And since the "prev" direction DOES move beats, I assume this code is unintended.
3724 // But I'm keeping it around, until we determine there are no terrible consequences.
3725 // if (ticks >= BBT_Time::ticks_per_beat) {
3726 // ticks -= BBT_Time::ticks_per_beat;
3729 } else if (dir < 0) {
3731 /* round to previous (or same iff dir == RoundDownMaybe) */
3733 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3735 if (difference == 0 && dir == RoundDownAlways) {
3736 /* right on the subdivision, but force-rounding down,
3737 so the difference is just the subdivision ticks */
3738 difference = ticks_one_subdivisions_worth;
3741 if (ticks < difference) {
3742 ticks = BBT_Time::ticks_per_beat - ticks;
3744 ticks -= difference;
3748 /* round to nearest */
3751 /* compute the distance to the previous and next subdivision */
3753 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3755 /* closer to the next subdivision, so shift forward */
3757 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3759 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3761 if (ticks > BBT_Time::ticks_per_beat) {
3763 ticks -= BBT_Time::ticks_per_beat;
3764 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3767 } else if (rem > 0) {
3769 /* closer to previous subdivision, so shift backward */
3773 /* can't go backwards past zero, so ... */
3774 return MusicFrame (0, 0);
3776 /* step back to previous beat */
3778 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3779 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3781 ticks = lrint (ticks - rem);
3782 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3785 /* on the subdivision, do nothing */
3789 MusicFrame ret (0, 0);
3790 ret.frame = frame_at_minute (minute_at_pulse_locked (_metrics, (beats + (ticks / BBT_Time::ticks_per_beat)) / 4.0));
3791 ret.division = sub_num;
3797 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
3799 Glib::Threads::RWLock::ReaderLock lm (lock);
3800 const double minute = minute_at_frame (frame);
3801 const double beat_at_framepos = max (0.0, beat_at_minute_locked (_metrics, minute));
3802 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
3803 MusicFrame ret (0, 0);
3810 /* find bar previous to 'frame' */
3816 ret.frame = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3820 } else if (dir > 0) {
3821 /* find bar following 'frame' */
3826 ret.frame = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3830 /* true rounding: find nearest bar */
3831 framepos_t raw_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3834 framepos_t prev_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3836 framepos_t next_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3838 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
3839 ret.frame = next_ft;
3844 ret.frame = prev_ft;
3856 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos)));
3859 } else if (dir > 0) {
3860 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, ceil (beat_at_framepos)));
3864 ret.frame = frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5)));
3871 return MusicFrame (0, 0);
3875 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
3876 framepos_t lower, framepos_t upper, uint32_t bar_mod)
3878 Glib::Threads::RWLock::ReaderLock lm (lock);
3879 int32_t cnt = ceil (beat_at_minute_locked (_metrics, minute_at_frame (lower)));
3881 /* although the map handles negative beats, bbt doesn't. */
3886 if (minute_at_beat_locked (_metrics, cnt) >= minute_at_frame (upper)) {
3890 while (pos >= 0 && pos < upper) {
3891 pos = frame_at_minute (minute_at_beat_locked (_metrics, cnt));
3892 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
3893 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
3894 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
3896 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c()));
3900 BBT_Time bbt = bbt_at_minute_locked (_metrics, minute_at_frame (lower));
3905 bbt.bars -= bbt.bars % bar_mod;
3909 while (pos >= 0 && pos < upper) {
3910 pos = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3911 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
3912 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
3913 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c()));
3914 bbt.bars += bar_mod;
3920 TempoMap::tempo_section_at_frame (framepos_t frame) const
3922 Glib::Threads::RWLock::ReaderLock lm (lock);
3924 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
3928 TempoMap::tempo_section_at_frame (framepos_t frame)
3930 Glib::Threads::RWLock::ReaderLock lm (lock);
3932 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
3936 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute) const
3938 TempoSection* prev = 0;
3942 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3944 if ((*i)->is_tempo()) {
3945 t = static_cast<TempoSection*> (*i);
3949 if (prev && t->minute() > minute) {
3959 abort(); /*NOTREACHED*/
3965 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute)
3967 TempoSection* prev = 0;
3971 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3973 if ((*i)->is_tempo()) {
3974 t = static_cast<TempoSection*> (*i);
3978 if (prev && t->minute() > minute) {
3988 abort(); /*NOTREACHED*/
3994 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3996 TempoSection* prev_t = 0;
3997 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
4001 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4002 if ((*i)->is_tempo()) {
4003 t = static_cast<TempoSection*> (*i);
4009 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
4019 /* don't use this to calculate length (the tempo is only correct for this frame).
4020 do that stuff based on the beat_at_frame and frame_at_beat api
4023 TempoMap::frames_per_quarter_note_at (const framepos_t& frame, const framecnt_t& sr) const
4025 Glib::Threads::RWLock::ReaderLock lm (lock);
4027 const TempoSection* ts_at = 0;
4028 const TempoSection* ts_after = 0;
4029 Metrics::const_iterator i;
4032 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4034 if ((*i)->is_tempo()) {
4035 t = static_cast<TempoSection*> (*i);
4039 if (ts_at && (*i)->frame() > frame) {
4049 return (60.0 * _frame_rate) / ts_at->tempo_at_minute (minute_at_frame (frame)).quarter_notes_per_minute();
4051 /* must be treated as constant tempo */
4052 return ts_at->frames_per_quarter_note (_frame_rate);
4056 TempoMap::meter_section_at_frame (framepos_t frame) const
4058 Glib::Threads::RWLock::ReaderLock lm (lock);
4059 return meter_section_at_minute_locked (_metrics, minute_at_frame (frame));
4063 TempoMap::meter_section_at_minute_locked (const Metrics& metrics, double minute) const
4065 Metrics::const_iterator i;
4066 MeterSection* prev = 0;
4070 for (i = metrics.begin(); i != metrics.end(); ++i) {
4072 if (!(*i)->is_tempo()) {
4073 m = static_cast<MeterSection*> (*i);
4075 if (prev && (*i)->minute() > minute) {
4085 abort(); /*NOTREACHED*/
4092 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4094 MeterSection* prev_m = 0;
4096 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4098 if (!(*i)->is_tempo()) {
4099 m = static_cast<MeterSection*> (*i);
4100 if (prev_m && m->beat() > beat) {
4111 TempoMap::meter_section_at_beat (double beat) const
4113 Glib::Threads::RWLock::ReaderLock lm (lock);
4114 return meter_section_at_beat_locked (_metrics, beat);
4118 TempoMap::meter_at_frame (framepos_t frame) const
4120 TempoMetric m (metric_at (frame));
4125 TempoMap::fix_legacy_session ()
4127 MeterSection* prev_m = 0;
4128 TempoSection* prev_t = 0;
4130 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4134 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
4136 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
4139 m->set_minute (0.0);
4140 m->set_position_lock_style (AudioTime);
4145 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
4146 + (m->bbt().beats - 1)
4147 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
4149 m->set_beat (start);
4150 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
4151 + (m->bbt().beats - 1)
4152 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
4153 m->set_pulse (start_beat / prev_m->note_divisor());
4156 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4164 t->set_minute (0.0);
4165 t->set_position_lock_style (AudioTime);
4171 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
4172 + (t->legacy_bbt().beats - 1)
4173 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
4175 t->set_pulse (beat / prev_m->note_divisor());
4177 /* really shouldn't happen but.. */
4178 t->set_pulse (beat / 4.0);
4187 TempoMap::get_state ()
4189 Metrics::const_iterator i;
4190 XMLNode *root = new XMLNode ("TempoMap");
4193 Glib::Threads::RWLock::ReaderLock lm (lock);
4194 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4195 root->add_child_nocopy ((*i)->get_state());
4203 TempoMap::set_state (const XMLNode& node, int /*version*/)
4206 Glib::Threads::RWLock::WriterLock lm (lock);
4209 XMLNodeConstIterator niter;
4210 Metrics old_metrics (_metrics);
4213 nlist = node.children();
4215 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
4216 XMLNode* child = *niter;
4218 if (child->name() == TempoSection::xml_state_node_name) {
4221 TempoSection* ts = new TempoSection (*child, _frame_rate);
4222 _metrics.push_back (ts);
4225 catch (failed_constructor& err){
4226 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4227 _metrics = old_metrics;
4228 old_metrics.clear();
4232 } else if (child->name() == MeterSection::xml_state_node_name) {
4235 MeterSection* ms = new MeterSection (*child, _frame_rate);
4236 _metrics.push_back (ms);
4239 catch (failed_constructor& err) {
4240 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4241 _metrics = old_metrics;
4242 old_metrics.clear();
4248 if (niter == nlist.end()) {
4249 MetricSectionSorter cmp;
4250 _metrics.sort (cmp);
4253 /* check for legacy sessions where bbt was the base musical unit for tempo */
4254 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4256 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
4257 if (t->legacy_bbt().bars != 0) {
4258 fix_legacy_session();
4265 /* check for multiple tempo/meters at the same location, which
4266 ardour2 somehow allowed.
4269 Metrics::iterator prev = _metrics.end();
4270 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4271 if (prev != _metrics.end()) {
4273 MeterSection* prev_m;
4275 TempoSection* prev_t;
4276 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
4277 if (prev_m->pulse() == ms->pulse()) {
4278 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4279 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4282 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
4283 if (prev_t->pulse() == ts->pulse()) {
4284 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4285 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4293 recompute_map (_metrics);
4295 Metrics::const_iterator d = old_metrics.begin();
4296 while (d != old_metrics.end()) {
4300 old_metrics.clear ();
4303 PropertyChanged (PropertyChange ());
4309 TempoMap::dump (std::ostream& o) const
4311 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
4312 const MeterSection* m;
4313 const TempoSection* t;
4314 const TempoSection* prev_t = 0;
4316 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4318 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
4319 o << "Tempo @ " << *i << t->note_types_per_minute() << " BPM (pulse = 1/" << t->note_type()
4320 << " type= " << enum_2_string (t->type()) << ") " << " at pulse= " << t->pulse()
4321 << " minute= " << t->minute() << " frame= " << t->frame() << " (initial? " << t->initial() << ')'
4322 << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
4324 o << " current : " << t->note_types_per_minute()
4325 << " | " << t->pulse() << " | " << t->frame() << " | " << t->minute() << std::endl;
4326 o << " previous : " << prev_t->note_types_per_minute()
4327 << " | " << prev_t->pulse() << " | " << prev_t->frame() << " | " << prev_t->minute() << std::endl;
4328 o << " calculated : " << prev_t->tempo_at_pulse (t->pulse())
4329 << " | " << prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute())
4330 << " | " << frame_at_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()))
4331 << " | " << prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()) << std::endl;
4334 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
4335 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt()
4336 << " frame= " << m->frame() << " pulse: " << m->pulse() << " beat : " << m->beat()
4337 << " pos lock: " << enum_2_string (m->position_lock_style()) << " (initial? " << m->initial() << ')' << endl;
4340 o << "------" << std::endl;
4344 TempoMap::n_tempos() const
4346 Glib::Threads::RWLock::ReaderLock lm (lock);
4349 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4350 if ((*i)->is_tempo()) {
4359 TempoMap::n_meters() const
4361 Glib::Threads::RWLock::ReaderLock lm (lock);
4364 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4365 if (!(*i)->is_tempo()) {
4374 TempoMap::insert_time (framepos_t where, framecnt_t amount)
4376 for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
4377 if ((*i)->frame() >= where && !(*i)->initial ()) {
4381 if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
4382 gui_set_meter_position (ms, (*i)->frame() + amount);
4385 if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
4386 gui_set_tempo_position (ts, (*i)->frame() + amount, 0);
4391 PropertyChanged (PropertyChange ());
4395 TempoMap::remove_time (framepos_t where, framecnt_t amount)
4399 std::list<MetricSection*> metric_kill_list;
4401 TempoSection* last_tempo = NULL;
4402 MeterSection* last_meter = NULL;
4403 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
4404 bool meter_after = false; // is there a meter marker likewise?
4406 Glib::Threads::RWLock::WriterLock lm (lock);
4407 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4408 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
4409 metric_kill_list.push_back(*i);
4410 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
4413 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
4417 else if ((*i)->frame() >= where) {
4418 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
4419 (*i)->set_minute ((*i)->minute() - minute_at_frame (amount));
4420 if ((*i)->frame() == where) {
4421 // marker was immediately after end of range
4422 tempo_after = dynamic_cast<TempoSection*> (*i);
4423 meter_after = dynamic_cast<MeterSection*> (*i);
4429 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
4430 if (last_tempo && !tempo_after) {
4431 metric_kill_list.remove(last_tempo);
4432 last_tempo->set_minute (minute_at_frame (where));
4435 if (last_meter && !meter_after) {
4436 metric_kill_list.remove(last_meter);
4437 last_meter->set_minute (minute_at_frame (where));
4441 //remove all the remaining metrics
4442 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
4443 _metrics.remove(*i);
4448 recompute_map (_metrics);
4451 PropertyChanged (PropertyChange ());
4455 /** Add some (fractional) Beats to a session frame position, and return the result in frames.
4456 * pos can be -ve, if required.
4459 TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats beats) const
4461 Glib::Threads::RWLock::ReaderLock lm (lock);
4462 const double frame_qn = pulse_at_minute_locked (_metrics, minute_at_frame (frame)) * 4.0;
4464 return frame_at_minute (minute_at_pulse_locked (_metrics, (frame_qn + beats.to_double()) / 4.0));
4468 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
4470 Glib::Threads::RWLock::ReaderLock lm (lock);
4472 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_minute_locked (_metrics, minute_at_frame (pos)));
4473 pos_bbt.ticks += op.ticks;
4474 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
4476 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
4478 pos_bbt.beats += op.beats;
4479 /* the meter in effect will start on the bar */
4480 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();
4481 while (pos_bbt.beats >= divisions_per_bar + 1) {
4483 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4484 pos_bbt.beats -= divisions_per_bar;
4486 pos_bbt.bars += op.bars;
4488 return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
4491 /** Count the number of beats that are equivalent to distance when going forward,
4495 TempoMap::framewalk_to_qn (framepos_t pos, framecnt_t distance) const
4497 Glib::Threads::RWLock::ReaderLock lm (lock);
4499 return Evoral::Beats (quarter_notes_between_frames_locked (_metrics, pos, pos + distance));
4503 bool operator() (const BBT_Time& a, const BBT_Time& b) {
4509 operator<< (std::ostream& o, const Meter& m) {
4510 return o << m.divisions_per_bar() << '/' << m.note_divisor();
4514 operator<< (std::ostream& o, const Tempo& t) {
4515 return o << t.note_types_per_minute() << " 1/" << t.note_type() << "'s per minute";
4519 operator<< (std::ostream& o, const MetricSection& section) {
4521 o << "MetricSection @ " << section.frame() << ' ';
4523 const TempoSection* ts;
4524 const MeterSection* ms;
4526 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
4527 o << *((const Tempo*) ts);
4528 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
4529 o << *((const Meter*) ms);