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_func_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_func_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 (ts.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);
1111 /* cannot move the first tempo section */
1112 *static_cast<Tempo*>(&first) = tempo;
1113 recompute_map (_metrics);
1118 PropertyChanged (PropertyChange ());
1122 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, double minute
1123 , TempoSection::Type type, PositionLockStyle pls, bool recompute, bool locked_to_meter)
1125 TempoSection* t = new TempoSection (pulse, minute, tempo.note_types_per_minute(), tempo.note_type(), type, pls, _frame_rate);
1126 t->set_locked_to_meter (locked_to_meter);
1127 bool solved = false;
1132 if (pls == AudioTime) {
1133 solved = solve_map_minute (_metrics, t, t->minute());
1135 solved = solve_map_pulse (_metrics, t, t->pulse());
1137 recompute_meters (_metrics);
1140 if (!solved && recompute) {
1141 recompute_map (_metrics);
1148 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1150 MeterSection* m = 0;
1152 Glib::Threads::RWLock::WriterLock lm (lock);
1153 m = add_meter_locked (meter, beat, where, frame, pls, true);
1158 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1163 PropertyChanged (PropertyChange ());
1168 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1171 Glib::Threads::RWLock::WriterLock lm (lock);
1172 const double beat = beat_at_bbt_locked (_metrics, where);
1174 if (!ms.initial()) {
1175 remove_meter_locked (ms);
1176 add_meter_locked (meter, beat, where, frame, pls, true);
1178 MeterSection& first (first_meter());
1179 TempoSection& first_t (first_tempo());
1180 /* cannot move the first meter section */
1181 *static_cast<Meter*>(&first) = meter;
1182 first.set_position_lock_style (AudioTime);
1183 first.set_pulse (0.0);
1184 first.set_minute (minute_at_frame (frame));
1185 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1186 first.set_beat (beat);
1187 first_t.set_minute (first.minute());
1188 first_t.set_pulse (0.0);
1189 first_t.set_position_lock_style (AudioTime);
1190 recompute_map (_metrics);
1194 PropertyChanged (PropertyChange ());
1198 TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute)
1200 const MeterSection& prev_m = meter_section_at_minute_locked (_metrics, minute_at_beat_locked (_metrics, beat) - minute_at_frame (1));
1201 const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1202 const double time_minutes = minute_at_pulse_locked (_metrics, pulse);
1203 TempoSection* mlt = 0;
1205 if (pls == AudioTime) {
1206 /* add meter-locked tempo */
1207 mlt = add_tempo_locked (tempo_at_minute_locked (_metrics, time_minutes), pulse, minute_at_frame (frame), TempoSection::Ramp, AudioTime, true, true);
1215 MeterSection* new_meter = new MeterSection (pulse, minute_at_frame (frame), beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls, _frame_rate);
1217 bool solved = false;
1219 do_insert (new_meter);
1223 if (pls == AudioTime) {
1224 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (frame));
1225 /* we failed, most likely due to some impossible frame requirement wrt audio-locked tempi.
1226 fudge frame so that the meter ends up at its BBT position instead.
1229 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (prev_m.frame() + 1));
1232 solved = solve_map_bbt (_metrics, new_meter, where);
1233 /* required due to resetting the pulse of meter-locked tempi above.
1234 Arguably solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
1235 but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
1237 recompute_map (_metrics);
1241 if (!solved && recompute) {
1242 /* if this has failed to solve, there is little we can do other than to ensure that
1243 the new map is recalculated.
1245 warning << "Adding meter may have left the tempo map unsolved." << endmsg;
1246 recompute_map (_metrics);
1253 TempoMap::change_initial_tempo (double note_types_per_minute, double note_type)
1255 Tempo newtempo (note_types_per_minute, note_type);
1258 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1259 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1264 Glib::Threads::RWLock::WriterLock lm (lock);
1265 *((Tempo*) t) = newtempo;
1266 recompute_map (_metrics);
1268 PropertyChanged (PropertyChange ());
1275 TempoMap::change_existing_tempo_at (framepos_t where, double note_types_per_minute, double note_type)
1277 Tempo newtempo (note_types_per_minute, note_type);
1280 TempoSection* first;
1281 Metrics::iterator i;
1283 /* find the TempoSection immediately preceding "where"
1286 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1288 if ((*i)->frame() > where) {
1294 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1307 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1317 Glib::Threads::RWLock::WriterLock lm (lock);
1318 /* cannot move the first tempo section */
1319 *((Tempo*)prev) = newtempo;
1320 recompute_map (_metrics);
1323 PropertyChanged (PropertyChange ());
1327 TempoMap::first_meter () const
1329 const MeterSection *m = 0;
1331 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1332 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1337 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1338 abort(); /*NOTREACHED*/
1343 TempoMap::first_meter ()
1345 MeterSection *m = 0;
1347 /* CALLER MUST HOLD LOCK */
1349 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1350 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1355 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1356 abort(); /*NOTREACHED*/
1361 TempoMap::first_tempo () const
1363 const TempoSection *t = 0;
1365 /* CALLER MUST HOLD LOCK */
1367 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1368 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1378 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1379 abort(); /*NOTREACHED*/
1384 TempoMap::first_tempo ()
1386 TempoSection *t = 0;
1388 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1389 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1399 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1400 abort(); /*NOTREACHED*/
1404 TempoMap::recompute_tempi (Metrics& metrics)
1406 TempoSection* prev_t = 0;
1408 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1411 if ((*i)->is_tempo()) {
1412 t = static_cast<TempoSection*> (*i);
1424 if (t->position_lock_style() == AudioTime) {
1425 prev_t->set_c (prev_t->compute_c_func_minute (t->note_types_per_minute(), t->minute()));
1426 if (!t->locked_to_meter()) {
1427 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
1431 prev_t->set_c (prev_t->compute_c_func_pulse (t->note_types_per_minute(), t->pulse()));
1432 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
1440 prev_t->set_c (0.0);
1443 /* tempos must be positioned correctly.
1444 the current approach is to use a meter's bbt time as its base position unit.
1445 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1446 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1449 TempoMap::recompute_meters (Metrics& metrics)
1451 MeterSection* meter = 0;
1452 MeterSection* prev_m = 0;
1454 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1455 if (!(*mi)->is_tempo()) {
1456 meter = static_cast<MeterSection*> (*mi);
1457 if (meter->position_lock_style() == AudioTime) {
1459 pair<double, BBT_Time> b_bbt;
1460 TempoSection* meter_locked_tempo = 0;
1461 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1463 if ((*ii)->is_tempo()) {
1464 t = static_cast<TempoSection*> (*ii);
1465 if ((t->locked_to_meter() || t->initial()) && t->frame() == meter->frame()) {
1466 meter_locked_tempo = t;
1473 double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1474 if (beats + prev_m->beat() != meter->beat()) {
1475 /* reordering caused a bbt change */
1477 beats = meter->beat() - prev_m->beat();
1478 b_bbt = make_pair (beats + prev_m->beat()
1479 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1480 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1482 } else if (!meter->initial()) {
1483 b_bbt = make_pair (meter->beat(), meter->bbt());
1484 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1487 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1489 if (meter_locked_tempo) {
1490 meter_locked_tempo->set_pulse (pulse);
1492 meter->set_beat (b_bbt);
1493 meter->set_pulse (pulse);
1498 pair<double, BBT_Time> b_bbt;
1500 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1501 if (beats + prev_m->beat() != meter->beat()) {
1502 /* reordering caused a bbt change */
1503 b_bbt = make_pair (beats + prev_m->beat()
1504 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1506 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1508 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1510 /* shouldn't happen - the first is audio-locked */
1511 pulse = pulse_at_beat_locked (metrics, meter->beat());
1512 b_bbt = make_pair (meter->beat(), meter->bbt());
1515 meter->set_beat (b_bbt);
1516 meter->set_pulse (pulse);
1517 meter->set_minute (minute_at_pulse_locked (metrics, pulse));
1526 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1528 /* CALLER MUST HOLD WRITE LOCK */
1530 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1533 /* silly call from Session::process() during startup
1538 recompute_tempi (metrics);
1539 recompute_meters (metrics);
1543 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1545 Glib::Threads::RWLock::ReaderLock lm (lock);
1546 TempoMetric m (first_meter(), first_tempo());
1548 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1549 at something, because we insert the default tempo and meter during
1550 TempoMap construction.
1552 now see if we can find better candidates.
1555 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1557 if ((*i)->frame() > frame) {
1571 /* XX meters only */
1573 TempoMap::metric_at (BBT_Time bbt) const
1575 Glib::Threads::RWLock::ReaderLock lm (lock);
1576 TempoMetric m (first_meter(), first_tempo());
1578 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1579 at something, because we insert the default tempo and meter during
1580 TempoMap construction.
1582 now see if we can find better candidates.
1585 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1587 if (!(*i)->is_tempo()) {
1588 mw = static_cast<MeterSection*> (*i);
1589 BBT_Time section_start (mw->bbt());
1591 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1602 /** Returns the BBT (meter-based) beat corresponding to the supplied frame, possibly returning a negative value.
1603 * @param frame The session frame position.
1604 * @return The beat duration according to the tempo map at the supplied frame.
1606 * If the supplied frame lies before the first meter, the returned beat duration will be negative.
1607 * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
1609 * This function uses both tempo and meter.
1612 TempoMap::beat_at_frame (const framecnt_t& frame) const
1614 Glib::Threads::RWLock::ReaderLock lm (lock);
1616 return beat_at_minute_locked (_metrics, minute_at_frame (frame));
1619 /* This function uses both tempo and meter.*/
1621 TempoMap::beat_at_minute_locked (const Metrics& metrics, const double& minute) const
1623 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
1624 MeterSection* prev_m = 0;
1625 MeterSection* next_m = 0;
1627 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1628 if (!(*i)->is_tempo()) {
1629 if (prev_m && (*i)->minute() > minute) {
1630 next_m = static_cast<MeterSection*> (*i);
1633 prev_m = static_cast<MeterSection*> (*i);
1637 const double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
1639 /* audio locked meters fake their beat */
1640 if (next_m && next_m->beat() < beat) {
1641 return next_m->beat();
1647 /** Returns the frame corresponding to the supplied BBT (meter-based) beat.
1648 * @param beat The BBT (meter-based) beat.
1649 * @return The frame duration according to the tempo map at the supplied BBT (meter-based) beat.
1651 * This function uses both tempo and meter.
1654 TempoMap::frame_at_beat (const double& beat) const
1656 Glib::Threads::RWLock::ReaderLock lm (lock);
1658 return frame_at_minute (minute_at_beat_locked (_metrics, beat));
1661 /* meter & tempo section based */
1663 TempoMap::minute_at_beat_locked (const Metrics& metrics, const double& beat) const
1665 MeterSection* prev_m = 0;
1666 TempoSection* prev_t = 0;
1670 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1671 if (!(*i)->is_tempo()) {
1672 m = static_cast<MeterSection*> (*i);
1673 if (prev_m && m->beat() > beat) {
1683 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1684 if ((*i)->is_tempo()) {
1685 t = static_cast<TempoSection*> (*i);
1686 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1695 return prev_t->minute_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse());
1698 /** Returns a Tempo corresponding to the supplied frame position.
1699 * @param frame The audio frame.
1700 * @return a Tempo according to the tempo map at the supplied frame.
1704 TempoMap::tempo_at_frame (const framepos_t& frame) const
1706 Glib::Threads::RWLock::ReaderLock lm (lock);
1708 return tempo_at_minute_locked (_metrics, minute_at_frame (frame));
1712 TempoMap::tempo_at_minute_locked (const Metrics& metrics, const double& minute) const
1714 TempoSection* prev_t = 0;
1718 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1719 if ((*i)->is_tempo()) {
1720 t = static_cast<TempoSection*> (*i);
1724 if ((prev_t) && t->minute() > minute) {
1725 /* t is the section past frame */
1726 return prev_t->tempo_at_minute (minute);
1732 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type());
1735 /** returns the frame at which the supplied tempo occurs, or
1736 * the frame of the last tempo section (search exhausted)
1737 * only the position of the first occurence will be returned
1741 TempoMap::frame_at_tempo (const Tempo& tempo) const
1743 Glib::Threads::RWLock::ReaderLock lm (lock);
1745 return frame_at_minute (minute_at_tempo_locked (_metrics, tempo));
1749 TempoMap::minute_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1751 TempoSection* prev_t = 0;
1752 const double tempo_bpm = tempo.note_types_per_minute();
1754 Metrics::const_iterator i;
1756 for (i = metrics.begin(); i != metrics.end(); ++i) {
1758 if ((*i)->is_tempo()) {
1759 t = static_cast<TempoSection*> (*i);
1765 const double t_bpm = t->note_types_per_minute();
1767 if (t_bpm == tempo_bpm) {
1772 const double prev_t_bpm = prev_t->note_types_per_minute();
1774 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1775 return prev_t->minute_at_ntpm (prev_t->note_types_per_minute(), prev_t->pulse());
1782 return prev_t->minute();
1786 TempoMap::tempo_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1788 TempoSection* prev_t = 0;
1792 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1793 if ((*i)->is_tempo()) {
1794 t = static_cast<TempoSection*> (*i);
1798 if ((prev_t) && t->pulse() > pulse) {
1799 /* t is the section past frame */
1800 return prev_t->tempo_at_pulse (pulse);
1806 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type());
1810 TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1812 TempoSection* prev_t = 0;
1813 const double tempo_bpm = tempo.note_types_per_minute();
1815 Metrics::const_iterator i;
1817 for (i = metrics.begin(); i != metrics.end(); ++i) {
1819 if ((*i)->is_tempo()) {
1820 t = static_cast<TempoSection*> (*i);
1826 const double t_bpm = t->note_types_per_minute();
1828 if (t_bpm == tempo_bpm) {
1833 const double prev_t_bpm = prev_t->note_types_per_minute();
1835 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1836 return prev_t->pulse_at_ntpm (prev_t->note_types_per_minute(), prev_t->minute());
1843 return prev_t->pulse();
1846 /** Returns a Tempo corresponding to the supplied position in quarter-note beats.
1847 * @param qn the position in quarter note beats.
1848 * @return the Tempo at the supplied quarter-note.
1851 TempoMap::tempo_at_quarter_note (const double& qn) const
1853 Glib::Threads::RWLock::ReaderLock lm (lock);
1855 return tempo_at_pulse_locked (_metrics, qn / 4.0);
1858 /** Returns the position in quarter-note beats corresponding to the supplied Tempo.
1859 * @param tempo the tempo.
1860 * @return the position in quarter-note beats where the map bpm
1861 * is equal to that of the Tempo. currently ignores note_type.
1864 TempoMap::quarter_note_at_tempo (const Tempo& tempo) const
1866 Glib::Threads::RWLock::ReaderLock lm (lock);
1868 return pulse_at_tempo_locked (_metrics, tempo) * 4.0;;
1871 /** Returns the whole-note pulse corresponding to the supplied BBT (meter-based) beat.
1872 * @param metrics the list of metric sections used to calculate the pulse.
1873 * @param beat The BBT (meter-based) beat.
1874 * @return the whole-note pulse at the supplied BBT (meter-based) beat.
1876 * a pulse or whole note is the base musical position of a MetricSection.
1877 * it is equivalent to four quarter notes.
1881 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1883 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1885 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1888 /** Returns the BBT (meter-based) beat corresponding to the supplied whole-note pulse .
1889 * @param metrics the list of metric sections used to calculate the beat.
1890 * @param pulse the whole-note pulse.
1891 * @return the meter-based beat at the supplied whole-note pulse.
1893 * a pulse or whole note is the base musical position of a MetricSection.
1894 * it is equivalent to four quarter notes.
1897 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1899 MeterSection* prev_m = 0;
1901 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1903 if (!(*i)->is_tempo()) {
1904 m = static_cast<MeterSection*> (*i);
1905 if (prev_m && m->pulse() > pulse) {
1913 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1917 /* tempo section based */
1919 TempoMap::pulse_at_minute_locked (const Metrics& metrics, const double& minute) const
1921 /* HOLD (at least) THE READER LOCK */
1922 TempoSection* prev_t = 0;
1924 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1926 if ((*i)->is_tempo()) {
1927 t = static_cast<TempoSection*> (*i);
1931 if (prev_t && t->minute() > minute) {
1932 /*the previous ts is the one containing the frame */
1933 const double ret = prev_t->pulse_at_minute (minute);
1934 /* audio locked section in new meter*/
1935 if (t->pulse() < ret) {
1944 /* treated as constant for this ts */
1945 const double pulses_in_section = ((minute - prev_t->minute()) * prev_t->note_types_per_minute()) / prev_t->note_type();
1947 return pulses_in_section + prev_t->pulse();
1950 /* tempo section based */
1952 TempoMap::minute_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1954 /* HOLD THE READER LOCK */
1956 const TempoSection* prev_t = 0;
1958 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1961 if ((*i)->is_tempo()) {
1962 t = static_cast<TempoSection*> (*i);
1966 if (prev_t && t->pulse() > pulse) {
1967 return prev_t->minute_at_pulse (pulse);
1973 /* must be treated as constant, irrespective of _type */
1974 double const dtime = ((pulse - prev_t->pulse()) * prev_t->note_type()) / prev_t->note_types_per_minute();
1976 return dtime + prev_t->minute();
1979 /** Returns the BBT (meter-based) beat corresponding to the supplied BBT time.
1980 * @param bbt The BBT time (meter-based).
1981 * @return bbt The BBT beat (meter-based) at the supplied BBT time.
1985 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
1987 Glib::Threads::RWLock::ReaderLock lm (lock);
1988 return beat_at_bbt_locked (_metrics, bbt);
1993 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1995 /* CALLER HOLDS READ LOCK */
1997 MeterSection* prev_m = 0;
1999 /* because audio-locked meters have 'fake' integral beats,
2000 there is no pulse offset here.
2004 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2005 if (!(*i)->is_tempo()) {
2006 m = static_cast<MeterSection*> (*i);
2008 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
2009 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
2017 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2018 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
2019 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
2024 /** Returns the BBT time corresponding to the supplied BBT (meter-based) beat.
2025 * @param beat The BBT (meter-based) beat.
2026 * @return The BBT time (meter-based) at the supplied meter-based beat.
2030 TempoMap::bbt_at_beat (const double& beat)
2032 Glib::Threads::RWLock::ReaderLock lm (lock);
2033 return bbt_at_beat_locked (_metrics, beat);
2037 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
2039 /* CALLER HOLDS READ LOCK */
2040 MeterSection* prev_m = 0;
2041 const double beats = max (0.0, b);
2043 MeterSection* m = 0;
2045 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2046 if (!(*i)->is_tempo()) {
2047 m = static_cast<MeterSection*> (*i);
2049 if (m->beat() > beats) {
2050 /* this is the meter after the one our beat is on*/
2060 const double beats_in_ms = beats - prev_m->beat();
2061 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2062 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2063 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2064 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2068 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2069 ret.beats = (uint32_t) floor (remaining_beats);
2070 ret.bars = total_bars;
2072 /* 0 0 0 to 1 1 0 - based mapping*/
2076 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2078 ret.ticks -= BBT_Time::ticks_per_beat;
2081 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2089 /** Returns the quarter-note beat corresponding to the supplied BBT time (meter-based).
2090 * @param bbt The BBT time (meter-based).
2091 * @return the quarter note beat at the supplied BBT time
2093 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2095 * while the input uses meter, the output does not.
2098 TempoMap::quarter_note_at_bbt (const Timecode::BBT_Time& bbt)
2100 Glib::Threads::RWLock::ReaderLock lm (lock);
2102 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2106 TempoMap::quarter_note_at_bbt_rt (const Timecode::BBT_Time& bbt)
2108 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2111 throw std::logic_error ("TempoMap::quarter_note_at_bbt_rt() could not lock tempo map");
2114 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2118 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2120 /* CALLER HOLDS READ LOCK */
2122 MeterSection* prev_m = 0;
2124 /* because audio-locked meters have 'fake' integral beats,
2125 there is no pulse offset here.
2129 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2130 if (!(*i)->is_tempo()) {
2131 m = static_cast<MeterSection*> (*i);
2133 if (m->bbt().bars > bbt.bars) {
2141 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2142 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
2143 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
2148 /** Returns the BBT time corresponding to the supplied quarter-note beat.
2149 * @param qn the quarter-note beat.
2150 * @return The BBT time (meter-based) at the supplied meter-based beat.
2152 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2156 TempoMap::bbt_at_quarter_note (const double& qn)
2158 Glib::Threads::RWLock::ReaderLock lm (lock);
2160 return bbt_at_pulse_locked (_metrics, qn / 4.0);
2163 /** Returns the BBT time (meter-based) corresponding to the supplied whole-note pulse position.
2164 * @param metrics The list of metric sections used to determine the result.
2165 * @param pulse The whole-note pulse.
2166 * @return The BBT time at the supplied whole-note pulse.
2168 * a pulse or whole note is the basic musical position of a MetricSection.
2169 * it is equivalent to four quarter notes.
2170 * while the output uses meter, the input does not.
2173 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2175 MeterSection* prev_m = 0;
2177 MeterSection* m = 0;
2179 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2181 if (!(*i)->is_tempo()) {
2182 m = static_cast<MeterSection*> (*i);
2185 double const pulses_to_m = m->pulse() - prev_m->pulse();
2186 if (prev_m->pulse() + pulses_to_m > pulse) {
2187 /* this is the meter after the one our beat is on*/
2198 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
2199 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2200 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2201 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2202 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2206 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2207 ret.beats = (uint32_t) floor (remaining_beats);
2208 ret.bars = total_bars;
2210 /* 0 0 0 to 1 1 0 mapping*/
2214 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2216 ret.ticks -= BBT_Time::ticks_per_beat;
2219 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2227 /** Returns the BBT time corresponding to the supplied frame position.
2228 * @param frame the position in audio samples.
2229 * @return the BBT time at the frame position .
2233 TempoMap::bbt_at_frame (framepos_t frame)
2240 warning << string_compose (_("tempo map was asked for BBT time at frame %1\n"), frame) << endmsg;
2243 Glib::Threads::RWLock::ReaderLock lm (lock);
2245 return bbt_at_minute_locked (_metrics, minute_at_frame (frame));
2249 TempoMap::bbt_at_frame_rt (framepos_t frame)
2251 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2254 throw std::logic_error ("TempoMap::bbt_at_frame_rt() could not lock tempo map");
2257 return bbt_at_minute_locked (_metrics, minute_at_frame (frame));
2261 TempoMap::bbt_at_minute_locked (const Metrics& metrics, const double& minute) const
2271 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
2272 MeterSection* prev_m = 0;
2273 MeterSection* next_m = 0;
2277 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2278 if (!(*i)->is_tempo()) {
2279 m = static_cast<MeterSection*> (*i);
2280 if (prev_m && m->minute() > minute) {
2288 double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
2290 /* handle frame before first meter */
2291 if (minute < prev_m->minute()) {
2294 /* audio locked meters fake their beat */
2295 if (next_m && next_m->beat() < beat) {
2296 beat = next_m->beat();
2299 beat = max (0.0, beat);
2301 const double beats_in_ms = beat - prev_m->beat();
2302 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2303 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2304 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2305 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2309 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2310 ret.beats = (uint32_t) floor (remaining_beats);
2311 ret.bars = total_bars;
2313 /* 0 0 0 to 1 1 0 - based mapping*/
2317 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2319 ret.ticks -= BBT_Time::ticks_per_beat;
2322 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2330 /** Returns the frame position corresponding to the supplied BBT time.
2331 * @param bbt the position in BBT time.
2332 * @return the frame position at bbt.
2336 TempoMap::frame_at_bbt (const BBT_Time& bbt)
2339 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
2343 if (bbt.beats < 1) {
2344 throw std::logic_error ("beats are counted from one");
2346 Glib::Threads::RWLock::ReaderLock lm (lock);
2348 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
2351 /* meter & tempo section based */
2353 TempoMap::minute_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2355 /* HOLD THE READER LOCK */
2357 const double ret = minute_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2362 * Returns the quarter-note beat position corresponding to the supplied frame.
2364 * @param frame the position in frames.
2365 * @return The quarter-note position of the supplied frame. Ignores meter.
2369 TempoMap::quarter_note_at_frame (const framepos_t frame) const
2371 Glib::Threads::RWLock::ReaderLock lm (lock);
2373 const double ret = quarter_note_at_minute_locked (_metrics, minute_at_frame (frame));
2379 TempoMap::quarter_note_at_minute_locked (const Metrics& metrics, const double minute) const
2381 const double ret = pulse_at_minute_locked (metrics, minute) * 4.0;
2387 TempoMap::quarter_note_at_frame_rt (const framepos_t frame) const
2389 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2392 throw std::logic_error ("TempoMap::quarter_note_at_frame_rt() could not lock tempo map");
2395 const double ret = pulse_at_minute_locked (_metrics, minute_at_frame (frame)) * 4.0;
2401 * Returns the frame position corresponding to the supplied quarter-note beat.
2403 * @param quarter_note the quarter-note position.
2404 * @return the frame position of the supplied quarter-note. Ignores meter.
2409 TempoMap::frame_at_quarter_note (const double quarter_note) const
2411 Glib::Threads::RWLock::ReaderLock lm (lock);
2413 const framepos_t ret = frame_at_minute (minute_at_quarter_note_locked (_metrics, quarter_note));
2419 TempoMap::minute_at_quarter_note_locked (const Metrics& metrics, const double quarter_note) const
2421 const double ret = minute_at_pulse_locked (metrics, quarter_note / 4.0);
2426 /** Returns the quarter-note beats corresponding to the supplied BBT (meter-based) beat.
2427 * @param beat The BBT (meter-based) beat.
2428 * @return The quarter-note position of the supplied BBT (meter-based) beat.
2430 * a quarter-note may be compared with and assigned to Evoral::Beats.
2434 TempoMap::quarter_note_at_beat (const double beat) const
2436 Glib::Threads::RWLock::ReaderLock lm (lock);
2438 const double ret = quarter_note_at_beat_locked (_metrics, beat);
2444 TempoMap::quarter_note_at_beat_locked (const Metrics& metrics, const double beat) const
2446 const double ret = pulse_at_beat_locked (metrics, beat) * 4.0;
2451 /** Returns the BBT (meter-based) beat position corresponding to the supplied quarter-note beats.
2452 * @param quarter_note The position in quarter-note beats.
2453 * @return the BBT (meter-based) beat position of the supplied quarter-note beats.
2455 * a quarter-note is the musical unit of Evoral::Beats.
2459 TempoMap::beat_at_quarter_note (const double quarter_note) const
2461 Glib::Threads::RWLock::ReaderLock lm (lock);
2463 const double ret = beat_at_quarter_note_locked (_metrics, quarter_note);
2469 TempoMap::beat_at_quarter_note_locked (const Metrics& metrics, const double quarter_note) const
2472 return beat_at_pulse_locked (metrics, quarter_note / 4.0);
2475 /** Returns the duration in frames between two supplied quarter-note beat positions.
2476 * @param start the first position in quarter-note beats.
2477 * @param end the end position in quarter-note beats.
2478 * @return the frame distance ober the quarter-note beats duration.
2480 * use this rather than e.g.
2481 * frame_at-quarter_note (end_beats) - frame_at_quarter_note (start_beats).
2482 * frames_between_quarter_notes() doesn't round to audio frames as an intermediate step,
2486 TempoMap::frames_between_quarter_notes (const double start, const double end) const
2488 Glib::Threads::RWLock::ReaderLock lm (lock);
2490 return frame_at_minute (minutes_between_quarter_notes_locked (_metrics, start, end));
2494 TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end) const
2497 return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
2501 TempoMap::quarter_notes_between_frames (const framecnt_t start, const framecnt_t end) const
2503 Glib::Threads::RWLock::ReaderLock lm (lock);
2505 return quarter_notes_between_frames_locked (_metrics, start, end);
2509 TempoMap::quarter_notes_between_frames_locked (const Metrics& metrics, const framecnt_t start, const framecnt_t end) const
2511 const TempoSection* prev_t = 0;
2513 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2516 if ((*i)->is_tempo()) {
2517 t = static_cast<TempoSection*> (*i);
2521 if (prev_t && t->frame() > start) {
2528 const double start_qn = prev_t->pulse_at_frame (start);
2530 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2533 if ((*i)->is_tempo()) {
2534 t = static_cast<TempoSection*> (*i);
2538 if (prev_t && t->frame() > end) {
2544 const double end_qn = prev_t->pulse_at_frame (end);
2546 return (end_qn - start_qn) * 4.0;
2550 TempoMap::check_solved (const Metrics& metrics) const
2552 TempoSection* prev_t = 0;
2553 MeterSection* prev_m = 0;
2555 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2558 if ((*i)->is_tempo()) {
2559 t = static_cast<TempoSection*> (*i);
2564 /* check ordering */
2565 if ((t->minute() <= prev_t->minute()) || (t->pulse() <= prev_t->pulse())) {
2569 /* precision check ensures tempo and frames align.*/
2570 if (t->frame() != frame_at_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()))) {
2571 if (!t->locked_to_meter()) {
2576 /* gradient limit - who knows what it should be?
2577 things are also ok (if a little chaotic) without this
2579 if (fabs (prev_t->c()) > 1000.0) {
2580 //std::cout << "c : " << prev_t->c() << std::endl;
2587 if (!(*i)->is_tempo()) {
2588 m = static_cast<MeterSection*> (*i);
2589 if (prev_m && m->position_lock_style() == AudioTime) {
2590 const TempoSection* t = &tempo_section_at_minute_locked (metrics, minute_at_frame (m->frame() - 1));
2591 const framepos_t nascent_m_frame = frame_at_minute (t->minute_at_pulse (m->pulse()));
2592 /* Here we check that a preceding section of music doesn't overlap a subsequent one.
2594 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
2608 TempoMap::set_active_tempi (const Metrics& metrics, const framepos_t& frame)
2610 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2612 if ((*i)->is_tempo()) {
2613 t = static_cast<TempoSection*> (*i);
2614 if (t->locked_to_meter()) {
2615 t->set_active (true);
2616 } else if (t->position_lock_style() == AudioTime) {
2617 if (t->frame() < frame) {
2618 t->set_active (false);
2619 t->set_pulse (-1.0);
2620 } else if (t->frame() > frame) {
2621 t->set_active (true);
2622 } else if (t->frame() == frame) {
2632 TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const double& minute)
2634 TempoSection* prev_t = 0;
2635 TempoSection* section_prev = 0;
2636 double first_m_minute = 0.0;
2637 const bool sml = section->locked_to_meter();
2639 /* can't move a tempo before the first meter */
2640 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2642 if (!(*i)->is_tempo()) {
2643 m = static_cast<MeterSection*> (*i);
2645 first_m_minute = m->minute();
2650 if (!section->initial() && minute <= first_m_minute) {
2654 section->set_active (true);
2655 section->set_minute (minute);
2657 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2659 if ((*i)->is_tempo()) {
2660 t = static_cast<TempoSection*> (*i);
2672 if (t->frame() == frame_at_minute (minute)) {
2676 const bool tlm = t->position_lock_style() == MusicTime;
2678 if (prev_t && !section_prev && ((sml && tlm && t->pulse() > section->pulse()) || (!tlm && t->minute() > minute))) {
2679 section_prev = prev_t;
2681 section_prev->set_c (section_prev->compute_c_func_minute (section->note_types_per_minute(), minute));
2682 if (!section->locked_to_meter()) {
2683 section->set_pulse (section_prev->pulse_at_ntpm (section->note_types_per_minute(), minute));
2688 if (t->position_lock_style() == MusicTime) {
2689 prev_t->set_c (prev_t->compute_c_func_pulse (t->note_types_per_minute(), t->pulse()));
2690 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
2692 prev_t->set_c (prev_t->compute_c_func_minute (t->note_types_per_minute(), t->minute()));
2693 if (!t->locked_to_meter()) {
2694 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
2703 recompute_tempi (imaginary);
2705 if (check_solved (imaginary)) {
2708 dunp (imaginary, std::cout);
2712 MetricSectionFrameSorter fcmp;
2713 imaginary.sort (fcmp);
2715 recompute_tempi (imaginary);
2717 if (check_solved (imaginary)) {
2725 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2727 TempoSection* prev_t = 0;
2728 TempoSection* section_prev = 0;
2730 section->set_pulse (pulse);
2732 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2734 if ((*i)->is_tempo()) {
2735 t = static_cast<TempoSection*> (*i);
2746 section_prev = prev_t;
2750 if (t->position_lock_style() == MusicTime) {
2751 prev_t->set_c (prev_t->compute_c_func_pulse (t->note_types_per_minute(), t->pulse()));
2752 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
2754 prev_t->set_c (prev_t->compute_c_func_minute (t->note_types_per_minute(), t->minute()));
2755 if (!t->locked_to_meter()) {
2756 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
2765 section_prev->set_c (section_prev->compute_c_func_pulse (section->note_types_per_minute(), pulse));
2766 section->set_minute (section_prev->minute_at_ntpm (section->note_types_per_minute(), pulse));
2770 recompute_tempi (imaginary);
2772 if (check_solved (imaginary)) {
2775 dunp (imaginary, std::cout);
2779 MetricSectionSorter cmp;
2780 imaginary.sort (cmp);
2782 recompute_tempi (imaginary);
2784 * XX need a restriction here, but only for this case,
2785 * as audio locked tempos don't interact in the same way.
2787 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2789 * |50 bpm |250 bpm |60 bpm
2790 * drag 250 to the pulse after 60->
2791 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2793 if (check_solved (imaginary)) {
2801 TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const double& minute)
2803 /* disallow moving first meter past any subsequent one, and any initial meter before the first one */
2804 const MeterSection* other = &meter_section_at_minute_locked (imaginary, minute);
2805 if ((section->initial() && !other->initial()) || (other->initial() && !section->initial() && other->minute() >= minute)) {
2809 if (section->initial()) {
2810 /* lock the first tempo to our first meter */
2811 if (!set_active_tempi (imaginary, frame_at_minute (minute))) {
2816 TempoSection* meter_locked_tempo = 0;
2818 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2820 if ((*ii)->is_tempo()) {
2821 t = static_cast<TempoSection*> (*ii);
2822 if ((t->locked_to_meter() || t->initial()) && t->frame() == section->frame()) {
2823 meter_locked_tempo = t;
2829 if (!meter_locked_tempo) {
2833 MeterSection* prev_m = 0;
2835 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2836 bool solved = false;
2838 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2840 if (!(*i)->is_tempo()) {
2841 m = static_cast<MeterSection*> (*i);
2843 if (prev_m && !section->initial()) {
2844 const double beats = (pulse_at_minute_locked (imaginary, minute) - prev_m->pulse()) * prev_m->note_divisor();
2845 if (beats + prev_m->beat() < section->beat()) {
2846 /* set the section pulse according to its musical position,
2847 * as an earlier time than this has been requested.
2849 const double new_pulse = ((section->beat() - prev_m->beat())
2850 / prev_m->note_divisor()) + prev_m->pulse();
2852 tempo_copy->set_position_lock_style (MusicTime);
2853 if ((solved = solve_map_pulse (future_map, tempo_copy, new_pulse))) {
2854 meter_locked_tempo->set_position_lock_style (MusicTime);
2855 section->set_position_lock_style (MusicTime);
2856 section->set_pulse (new_pulse);
2857 solve_map_pulse (imaginary, meter_locked_tempo, new_pulse);
2858 meter_locked_tempo->set_position_lock_style (AudioTime);
2859 section->set_position_lock_style (AudioTime);
2860 section->set_minute (meter_locked_tempo->minute());
2866 Metrics::const_iterator d = future_map.begin();
2867 while (d != future_map.end()) {
2876 /* all is ok. set section's locked tempo if allowed.
2877 possibly disallow if there is an adjacent audio-locked tempo.
2878 XX this check could possibly go. its never actually happened here.
2880 MeterSection* meter_copy = const_cast<MeterSection*>
2881 (&meter_section_at_minute_locked (future_map, section->minute()));
2883 meter_copy->set_minute (minute);
2885 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2886 section->set_minute (minute);
2887 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2888 / prev_m->note_divisor()) + prev_m->pulse());
2889 solve_map_minute (imaginary, meter_locked_tempo, minute);
2894 Metrics::const_iterator d = future_map.begin();
2895 while (d != future_map.end()) {
2905 /* initial (first meter atm) */
2907 tempo_copy->set_minute (minute);
2908 tempo_copy->set_pulse (0.0);
2910 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2911 section->set_minute (minute);
2912 meter_locked_tempo->set_minute (minute);
2913 meter_locked_tempo->set_pulse (0.0);
2914 solve_map_minute (imaginary, meter_locked_tempo, minute);
2919 Metrics::const_iterator d = future_map.begin();
2920 while (d != future_map.end()) {
2929 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2930 section->set_beat (b_bbt);
2931 section->set_pulse (0.0);
2941 MetricSectionFrameSorter fcmp;
2942 imaginary.sort (fcmp);
2944 recompute_meters (imaginary);
2950 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2952 /* disallow setting section to an existing meter's bbt */
2953 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2955 if (!(*i)->is_tempo()) {
2956 m = static_cast<MeterSection*> (*i);
2957 if (m != section && m->bbt().bars == when.bars) {
2963 MeterSection* prev_m = 0;
2964 MeterSection* section_prev = 0;
2966 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2968 if (!(*i)->is_tempo()) {
2969 m = static_cast<MeterSection*> (*i);
2975 pair<double, BBT_Time> b_bbt;
2976 double new_pulse = 0.0;
2978 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2979 section_prev = prev_m;
2981 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2982 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2983 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2985 section->set_beat (b_bbt);
2986 section->set_pulse (pulse);
2987 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
2991 if (m->position_lock_style() == AudioTime) {
2992 TempoSection* meter_locked_tempo = 0;
2994 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2996 if ((*ii)->is_tempo()) {
2997 t = static_cast<TempoSection*> (*ii);
2998 if ((t->locked_to_meter() || t->initial()) && t->frame() == m->frame()) {
2999 meter_locked_tempo = t;
3005 if (!meter_locked_tempo) {
3010 double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3012 if (beats + prev_m->beat() != m->beat()) {
3013 /* tempo/ meter change caused a change in beat (bar). */
3015 /* the user has requested that the previous section of music overlaps this one.
3016 we have no choice but to change the bar number here, as being locked to audio means
3017 we must stay where we are on the timeline.
3019 beats = m->beat() - prev_m->beat();
3020 b_bbt = make_pair (beats + prev_m->beat()
3021 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3022 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3024 } else if (!m->initial()) {
3025 b_bbt = make_pair (m->beat(), m->bbt());
3026 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3029 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3032 meter_locked_tempo->set_pulse (new_pulse);
3033 m->set_beat (b_bbt);
3034 m->set_pulse (new_pulse);
3038 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3039 if (beats + prev_m->beat() != m->beat()) {
3040 /* tempo/ meter change caused a change in beat (bar). */
3041 b_bbt = make_pair (beats + prev_m->beat()
3042 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3044 b_bbt = make_pair (beats + prev_m->beat()
3047 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3048 m->set_beat (b_bbt);
3049 m->set_pulse (new_pulse);
3050 m->set_minute (minute_at_pulse_locked (imaginary, new_pulse));
3057 if (!section_prev) {
3059 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
3060 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3061 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
3063 section->set_beat (b_bbt);
3064 section->set_pulse (pulse);
3065 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
3068 MetricSectionSorter cmp;
3069 imaginary.sort (cmp);
3071 recompute_meters (imaginary);
3076 /** places a copy of _metrics into copy and returns a pointer
3077 * to section's equivalent in copy.
3080 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
3082 TempoSection* ret = 0;
3084 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3087 if ((*i)->is_tempo()) {
3088 t = static_cast<TempoSection*> (*i);
3090 ret = new TempoSection (*t);
3091 copy.push_back (ret);
3095 TempoSection* cp = new TempoSection (*t);
3096 copy.push_back (cp);
3098 if (!(*i)->is_tempo()) {
3099 m = static_cast<MeterSection *> (*i);
3100 MeterSection* cp = new MeterSection (*m);
3101 copy.push_back (cp);
3109 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
3111 MeterSection* ret = 0;
3113 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3116 if ((*i)->is_tempo()) {
3117 t = static_cast<TempoSection*> (*i);
3118 TempoSection* cp = new TempoSection (*t);
3119 copy.push_back (cp);
3122 if (!(*i)->is_tempo()) {
3123 m = static_cast<MeterSection *> (*i);
3125 ret = new MeterSection (*m);
3126 copy.push_back (ret);
3129 MeterSection* cp = new MeterSection (*m);
3130 copy.push_back (cp);
3137 /** answers the question "is this a valid beat position for this tempo section?".
3138 * it returns true if the tempo section can be moved to the requested bbt position,
3139 * leaving the tempo map in a solved state.
3140 * @param ts the tempo section to be moved
3141 * @param bbt the requested new position for the tempo section
3142 * @return true if the tempo section can be moved to the position, otherwise false.
3145 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
3148 TempoSection* tempo_copy = 0;
3151 Glib::Threads::RWLock::ReaderLock lm (lock);
3152 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
3158 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
3160 Metrics::const_iterator d = copy.begin();
3161 while (d != copy.end()) {
3170 * 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,
3171 * taking any possible reordering as a consequence of this into account.
3172 * @param section - the section to be altered
3173 * @param bbt - the BBT time where the altered tempo will fall
3174 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
3176 pair<double, framepos_t>
3177 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
3180 pair<double, framepos_t> ret = make_pair (0.0, 0);
3182 Glib::Threads::RWLock::ReaderLock lm (lock);
3184 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
3186 const double beat = beat_at_bbt_locked (future_map, bbt);
3188 if (section->position_lock_style() == AudioTime) {
3189 tempo_copy->set_position_lock_style (MusicTime);
3192 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
3193 ret.first = tempo_copy->pulse();
3194 ret.second = tempo_copy->frame();
3196 ret.first = section->pulse();
3197 ret.second = section->frame();
3200 Metrics::const_iterator d = future_map.begin();
3201 while (d != future_map.end()) {
3208 /** moves a TempoSection to a specified position.
3209 * @param ts - the section to be moved
3210 * @param frame - the new position in frames for the tempo
3211 * @param sub_num - the snap division to use if using musical time.
3213 * if sub_num is non-zero, the frame position is used to calculate an exact
3216 * -1 | snap to bars (meter-based)
3217 * 0 | no snap - use audio frame for musical position
3218 * 1 | snap to meter-based (BBT) beat
3219 * >1 | snap to quarter-note subdivision (i.e. 4 will snap to sixteenth notes)
3221 * this follows the snap convention in the gui.
3222 * if sub_num is zero, the musical position will be taken from the supplied frame.
3225 TempoMap::gui_set_tempo_position (TempoSection* ts, const framepos_t& frame, const int& sub_num)
3229 if (ts->position_lock_style() == MusicTime) {
3231 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
3232 Glib::Threads::RWLock::WriterLock lm (lock);
3233 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3235 tempo_copy->set_position_lock_style (AudioTime);
3237 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3238 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
3239 const double pulse = pulse_at_beat_locked (future_map, beat);
3241 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3242 solve_map_pulse (_metrics, ts, pulse);
3243 recompute_meters (_metrics);
3251 Glib::Threads::RWLock::WriterLock lm (lock);
3252 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3254 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3256 /* We're moving the object that defines the grid while snapping to it...
3257 * Placing the ts at the beat corresponding to the requested frame may shift the
3258 * grid in such a way that the mouse is left hovering over a completerly different division,
3259 * causing jittering when the mouse next moves (esp. large tempo deltas).
3261 * This alters the snap behaviour slightly in that we snap to beat divisions
3262 * in the future map rather than the existing one.
3264 const double qn = exact_qn_at_frame_locked (future_map, frame, sub_num);
3265 const framepos_t snapped_frame = frame_at_minute (minute_at_quarter_note_locked (future_map, qn));
3267 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (snapped_frame))) {
3268 solve_map_minute (_metrics, ts, minute_at_frame (snapped_frame));
3269 ts->set_pulse (qn / 4.0);
3270 recompute_meters (_metrics);
3273 solve_map_minute (_metrics, ts, minute_at_frame (frame));
3274 recompute_meters (_metrics);
3280 Metrics::const_iterator d = future_map.begin();
3281 while (d != future_map.end()) {
3286 MetricPositionChanged (); // Emit Signal
3289 /** moves a MeterSection to a specified position.
3290 * @param ms - the section to be moved
3291 * @param frame - the new position in frames for the meter
3293 * as a meter cannot snap to anything but bars,
3294 * the supplied frame is rounded to the nearest bar, possibly
3295 * leaving the meter position unchanged.
3298 TempoMap::gui_set_meter_position (MeterSection* ms, const framepos_t& frame)
3302 if (ms->position_lock_style() == AudioTime) {
3305 Glib::Threads::RWLock::WriterLock lm (lock);
3306 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3308 if (solve_map_minute (future_map, copy, minute_at_frame (frame))) {
3309 solve_map_minute (_metrics, ms, minute_at_frame (frame));
3310 recompute_tempi (_metrics);
3315 Glib::Threads::RWLock::WriterLock lm (lock);
3316 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3318 const double beat = beat_at_minute_locked (_metrics, minute_at_frame (frame));
3319 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
3321 if (solve_map_bbt (future_map, copy, bbt)) {
3322 solve_map_bbt (_metrics, ms, bbt);
3323 recompute_tempi (_metrics);
3328 Metrics::const_iterator d = future_map.begin();
3329 while (d != future_map.end()) {
3334 MetricPositionChanged (); // Emit Signal
3338 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
3341 bool can_solve = false;
3343 Glib::Threads::RWLock::WriterLock lm (lock);
3344 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3345 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3346 recompute_tempi (future_map);
3348 if (check_solved (future_map)) {
3349 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3350 recompute_map (_metrics);
3355 Metrics::const_iterator d = future_map.begin();
3356 while (d != future_map.end()) {
3361 MetricPositionChanged (); // Emit Signal
3367 TempoMap::gui_stretch_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame)
3370 Ts (future prev_t) Tnext
3373 |----------|----------
3380 Glib::Threads::RWLock::WriterLock lm (lock);
3386 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3387 TempoSection* prev_to_prev_t = 0;
3388 const frameoffset_t fr_off = end_frame - frame;
3392 if (prev_t->pulse() > 0.0) {
3393 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_minute_locked (future_map, minute_at_frame (prev_t->frame() - 1)));
3396 TempoSection* next_t = 0;
3397 for (Metrics::iterator i = future_map.begin(); i != future_map.end(); ++i) {
3398 TempoSection* t = 0;
3399 if ((*i)->is_tempo()) {
3400 t = static_cast<TempoSection*> (*i);
3401 if (t->frame() > ts->frame()) {
3407 /* minimum allowed measurement distance in frames */
3408 const framepos_t min_dframe = 2;
3410 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
3411 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
3413 double contribution = 0.0;
3415 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3416 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3419 const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
3421 const double start_pulse = prev_t->pulse_at_minute (minute_at_frame (frame));
3422 const double end_pulse = prev_t->pulse_at_minute (minute_at_frame (end_frame));
3426 if (prev_t->type() == TempoSection::Constant || prev_t->c() == 0.0) {
3428 if (prev_t->position_lock_style() == MusicTime) {
3429 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3430 if (frame > prev_to_prev_t->frame() + min_dframe && (frame + prev_t_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
3432 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame())
3433 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
3435 new_bpm = prev_t->note_types_per_minute();
3438 /* prev to prev is irrelevant */
3440 if (start_pulse > prev_t->pulse() && end_pulse > prev_t->pulse()) {
3441 new_bpm = prev_t->note_types_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
3443 new_bpm = prev_t->note_types_per_minute();
3448 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3449 if (frame > prev_to_prev_t->frame() + min_dframe && end_frame > prev_to_prev_t->frame() + min_dframe) {
3451 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame())
3452 / (double) ((end_frame) - prev_to_prev_t->frame()));
3454 new_bpm = prev_t->note_types_per_minute();
3457 /* prev_to_prev_t is irrelevant */
3459 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3460 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
3462 new_bpm = prev_t->note_types_per_minute();
3468 double frame_ratio = 1.0;
3469 double pulse_ratio = 1.0;
3470 const double pulse_pos = frame;
3472 if (prev_to_prev_t) {
3473 if (pulse_pos > prev_to_prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_to_prev_t->frame() + min_dframe) {
3474 frame_ratio = (((pulse_pos - fr_off) - prev_to_prev_t->frame()) / (double) ((pulse_pos) - prev_to_prev_t->frame()));
3476 if (end_pulse > prev_to_prev_t->pulse() && start_pulse > prev_to_prev_t->pulse()) {
3477 pulse_ratio = ((start_pulse - prev_to_prev_t->pulse()) / (end_pulse - prev_to_prev_t->pulse()));
3480 if (pulse_pos > prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_t->frame() + min_dframe) {
3481 frame_ratio = (((pulse_pos - fr_off) - prev_t->frame()) / (double) ((pulse_pos) - prev_t->frame()));
3483 pulse_ratio = (start_pulse / end_pulse);
3485 new_bpm = prev_t->note_types_per_minute() * (pulse_ratio * frame_ratio);
3488 /* don't clamp and proceed here.
3489 testing has revealed that this can go negative,
3490 which is an entirely different thing to just being too low.
3492 if (new_bpm < 0.5) {
3495 new_bpm = min (new_bpm, (double) 1000.0);
3496 prev_t->set_note_types_per_minute (new_bpm);
3497 recompute_tempi (future_map);
3498 recompute_meters (future_map);
3500 if (check_solved (future_map)) {
3501 ts->set_note_types_per_minute (new_bpm);
3502 recompute_tempi (_metrics);
3503 recompute_meters (_metrics);
3507 Metrics::const_iterator d = future_map.begin();
3508 while (d != future_map.end()) {
3513 MetricPositionChanged (); // Emit Signal
3516 /** Returns the exact bbt-based beat corresponding to the bar, beat or quarter note subdivision nearest to
3517 * the supplied frame, possibly returning a negative value.
3519 * @param frame The session frame position.
3520 * @param sub_num The subdivision to use when rounding the beat.
3521 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3522 * Positive integers indicate quarter note (non BBT) divisions.
3523 * 0 indicates that the returned beat should not be rounded (equivalent to quarter_note_at_frame()).
3524 * @return The beat position of the supplied frame.
3526 * when working to a musical grid, the use of sub_nom indicates that
3527 * the position should be interpreted musically.
3529 * it effectively snaps to meter bars, meter beats or quarter note divisions
3530 * (as per current gui convention) and returns a musical position independent of frame rate.
3532 * If the supplied frame lies before the first meter, the return will be negative,
3533 * in which case the returned beat uses the first meter (for BBT subdivisions) and
3534 * the continuation of the tempo curve (backwards).
3536 * This function is sensitive to tempo and meter.
3539 TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num) const
3541 Glib::Threads::RWLock::ReaderLock lm (lock);
3543 return exact_beat_at_frame_locked (_metrics, frame, sub_num);
3547 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t divisions) const
3549 return beat_at_pulse_locked (_metrics, exact_qn_at_frame_locked (metrics, frame, divisions) / 4.0);
3552 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
3553 * the supplied frame, possibly returning a negative value.
3555 * @param frame The session frame position.
3556 * @param sub_num The subdivision to use when rounding the quarter note.
3557 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3558 * Positive integers indicate quarter note (non BBT) divisions.
3559 * 0 indicates that the returned quarter note should not be rounded (equivalent to quarter_note_at_frame()).
3560 * @return The quarter note position of the supplied frame.
3562 * When working to a musical grid, the use of sub_nom indicates that
3563 * the frame position should be interpreted musically.
3565 * it effectively snaps to meter bars, meter beats or quarter note divisions
3566 * (as per current gui convention) and returns a musical position independent of frame rate.
3568 * If the supplied frame lies before the first meter, the return will be negative,
3569 * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
3570 * the continuation of the tempo curve (backwards).
3572 * This function is tempo-sensitive.
3575 TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num) const
3577 Glib::Threads::RWLock::ReaderLock lm (lock);
3579 return exact_qn_at_frame_locked (_metrics, frame, sub_num);
3583 TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num) const
3585 double qn = quarter_note_at_minute_locked (metrics, minute_at_frame (frame));
3588 qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
3589 } else if (sub_num == 1) {
3590 /* the gui requested exact musical (BBT) beat */
3591 qn = quarter_note_at_beat_locked (metrics, floor (beat_at_minute_locked (metrics, minute_at_frame (frame)) + 0.5));
3592 } else if (sub_num == -1) {
3594 Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
3598 const double prev_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3600 const double next_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3602 if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
3612 /** returns the frame duration of the supplied BBT time at a specified frame position in the tempo map.
3613 * @param pos the frame position in the tempo map.
3614 * @param bbt the distance in BBT time from pos to calculate.
3615 * @param dir the rounding direction..
3616 * @return the duration in frames between pos and bbt
3619 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
3621 Glib::Threads::RWLock::ReaderLock lm (lock);
3623 BBT_Time pos_bbt = bbt_at_minute_locked (_metrics, minute_at_frame (pos));
3625 const double divisions = meter_section_at_minute_locked (_metrics, minute_at_frame (pos)).divisions_per_bar();
3628 pos_bbt.bars += bbt.bars;
3630 pos_bbt.ticks += bbt.ticks;
3631 if ((double) pos_bbt.ticks > BBT_Time::ticks_per_beat) {
3633 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3636 pos_bbt.beats += bbt.beats;
3637 if ((double) pos_bbt.beats > divisions) {
3639 pos_bbt.beats -= divisions;
3641 const framecnt_t pos_bbt_frame = frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3643 return pos_bbt_frame - pos;
3647 if (pos_bbt.bars <= bbt.bars) {
3650 pos_bbt.bars -= bbt.bars;
3653 if (pos_bbt.ticks < bbt.ticks) {
3654 if (pos_bbt.bars > 1) {
3655 if (pos_bbt.beats == 1) {
3657 pos_bbt.beats = divisions;
3661 pos_bbt.ticks = BBT_Time::ticks_per_beat - (bbt.ticks - pos_bbt.ticks);
3667 pos_bbt.ticks -= bbt.ticks;
3670 if (pos_bbt.beats <= bbt.beats) {
3671 if (pos_bbt.bars > 1) {
3673 pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
3678 pos_bbt.beats -= bbt.beats;
3681 return pos - frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3688 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
3690 return round_to_type (fr, dir, Bar);
3694 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
3696 return round_to_type (fr, dir, Beat);
3700 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3702 Glib::Threads::RWLock::ReaderLock lm (lock);
3703 uint32_t ticks = (uint32_t) floor (max (0.0, beat_at_minute_locked (_metrics, minute_at_frame (fr))) * BBT_Time::ticks_per_beat);
3704 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3705 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3707 ticks -= beats * BBT_Time::ticks_per_beat;
3710 /* round to next (or same iff dir == RoundUpMaybe) */
3712 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3714 if (mod == 0 && dir == RoundUpMaybe) {
3715 /* right on the subdivision, which is fine, so do nothing */
3717 } else if (mod == 0) {
3718 /* right on the subdivision, so the difference is just the subdivision ticks */
3719 ticks += ticks_one_subdivisions_worth;
3722 /* not on subdivision, compute distance to next subdivision */
3724 ticks += ticks_one_subdivisions_worth - mod;
3727 if (ticks >= BBT_Time::ticks_per_beat) {
3728 ticks -= BBT_Time::ticks_per_beat;
3730 } else if (dir < 0) {
3732 /* round to previous (or same iff dir == RoundDownMaybe) */
3734 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3736 if (difference == 0 && dir == RoundDownAlways) {
3737 /* right on the subdivision, but force-rounding down,
3738 so the difference is just the subdivision ticks */
3739 difference = ticks_one_subdivisions_worth;
3742 if (ticks < difference) {
3743 ticks = BBT_Time::ticks_per_beat - ticks;
3745 ticks -= difference;
3749 /* round to nearest */
3752 /* compute the distance to the previous and next subdivision */
3754 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3756 /* closer to the next subdivision, so shift forward */
3758 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3760 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3762 if (ticks > BBT_Time::ticks_per_beat) {
3764 ticks -= BBT_Time::ticks_per_beat;
3765 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3768 } else if (rem > 0) {
3770 /* closer to previous subdivision, so shift backward */
3774 /* can't go backwards past zero, so ... */
3777 /* step back to previous beat */
3779 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3780 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3782 ticks = lrint (ticks - rem);
3783 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3786 /* on the subdivision, do nothing */
3790 const framepos_t ret_frame = frame_at_minute (minute_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat)));
3796 TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3798 Glib::Threads::RWLock::ReaderLock lm (lock);
3799 uint32_t ticks = (uint32_t) floor (max (0.0, quarter_note_at_minute_locked (_metrics, minute_at_frame (fr))) * BBT_Time::ticks_per_beat);
3800 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3801 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3803 ticks -= beats * BBT_Time::ticks_per_beat;
3806 /* round to next (or same iff dir == RoundUpMaybe) */
3808 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3810 if (mod == 0 && dir == RoundUpMaybe) {
3811 /* right on the subdivision, which is fine, so do nothing */
3813 } else if (mod == 0) {
3814 /* right on the subdivision, so the difference is just the subdivision ticks */
3815 ticks += ticks_one_subdivisions_worth;
3818 /* not on subdivision, compute distance to next subdivision */
3820 ticks += ticks_one_subdivisions_worth - mod;
3823 //NOTE: this code intentionally limits the rounding so we don't advance to the next beat.
3824 // For the purposes of "jump-to-next-subdivision", we DO want to advance to the next beat.
3825 // And since the "prev" direction DOES move beats, I assume this code is unintended.
3826 // But I'm keeping it around, until we determine there are no terrible consequences.
3827 // if (ticks >= BBT_Time::ticks_per_beat) {
3828 // ticks -= BBT_Time::ticks_per_beat;
3831 } else if (dir < 0) {
3833 /* round to previous (or same iff dir == RoundDownMaybe) */
3835 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3837 if (difference == 0 && dir == RoundDownAlways) {
3838 /* right on the subdivision, but force-rounding down,
3839 so the difference is just the subdivision ticks */
3840 difference = ticks_one_subdivisions_worth;
3843 if (ticks < difference) {
3844 ticks = BBT_Time::ticks_per_beat - ticks;
3846 ticks -= difference;
3850 /* round to nearest */
3853 /* compute the distance to the previous and next subdivision */
3855 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3857 /* closer to the next subdivision, so shift forward */
3859 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3861 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3863 if (ticks > BBT_Time::ticks_per_beat) {
3865 ticks -= BBT_Time::ticks_per_beat;
3866 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3869 } else if (rem > 0) {
3871 /* closer to previous subdivision, so shift backward */
3875 /* can't go backwards past zero, so ... */
3878 /* step back to previous beat */
3880 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3881 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3883 ticks = lrint (ticks - rem);
3884 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3887 /* on the subdivision, do nothing */
3891 const framepos_t ret_frame = frame_at_minute (minute_at_quarter_note_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat)));
3897 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
3899 Glib::Threads::RWLock::ReaderLock lm (lock);
3901 const double beat_at_framepos = max (0.0, beat_at_minute_locked (_metrics, minute_at_frame (frame)));
3902 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
3907 /* find bar previous to 'frame' */
3912 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3914 } else if (dir > 0) {
3915 /* find bar following 'frame' */
3919 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3921 /* true rounding: find nearest bar */
3922 framepos_t raw_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3925 framepos_t prev_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3927 framepos_t next_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3929 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
3940 return frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos)));
3941 } else if (dir > 0) {
3942 return frame_at_minute (minute_at_beat_locked (_metrics, ceil (beat_at_framepos)));
3944 return frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5)));
3953 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
3954 framepos_t lower, framepos_t upper, uint32_t bar_mod)
3956 Glib::Threads::RWLock::ReaderLock lm (lock);
3957 int32_t cnt = ceil (beat_at_minute_locked (_metrics, minute_at_frame (lower)));
3959 /* although the map handles negative beats, bbt doesn't. */
3964 if (minute_at_beat_locked (_metrics, cnt) >= minute_at_frame (upper)) {
3968 while (pos >= 0 && pos < upper) {
3969 pos = frame_at_minute (minute_at_beat_locked (_metrics, cnt));
3970 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
3971 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
3972 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
3974 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c()));
3978 BBT_Time bbt = bbt_at_minute_locked (_metrics, minute_at_frame (lower));
3983 bbt.bars -= bbt.bars % bar_mod;
3987 while (pos >= 0 && pos < upper) {
3988 pos = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3989 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
3990 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
3991 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c()));
3992 bbt.bars += bar_mod;
3998 TempoMap::tempo_section_at_frame (framepos_t frame) const
4000 Glib::Threads::RWLock::ReaderLock lm (lock);
4002 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4006 TempoMap::tempo_section_at_frame (framepos_t frame)
4008 Glib::Threads::RWLock::ReaderLock lm (lock);
4010 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
4014 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute) const
4016 TempoSection* prev = 0;
4020 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4022 if ((*i)->is_tempo()) {
4023 t = static_cast<TempoSection*> (*i);
4027 if (prev && t->minute() > minute) {
4037 abort(); /*NOTREACHED*/
4043 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute)
4045 TempoSection* prev = 0;
4049 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4051 if ((*i)->is_tempo()) {
4052 t = static_cast<TempoSection*> (*i);
4056 if (prev && t->minute() > minute) {
4066 abort(); /*NOTREACHED*/
4072 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4074 TempoSection* prev_t = 0;
4075 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
4079 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4080 if ((*i)->is_tempo()) {
4081 t = static_cast<TempoSection*> (*i);
4082 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
4092 /* don't use this to calculate length (the tempo is only correct for this frame).
4093 do that stuff based on the beat_at_frame and frame_at_beat api
4096 TempoMap::frames_per_quarter_note_at (const framepos_t& frame, const framecnt_t& sr) const
4098 Glib::Threads::RWLock::ReaderLock lm (lock);
4100 const TempoSection* ts_at = 0;
4101 const TempoSection* ts_after = 0;
4102 Metrics::const_iterator i;
4105 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4107 if ((*i)->is_tempo()) {
4108 t = static_cast<TempoSection*> (*i);
4112 if (ts_at && (*i)->frame() > frame) {
4122 return (60.0 * _frame_rate) / ts_at->tempo_at_minute (minute_at_frame (frame)).quarter_notes_per_minute();
4124 /* must be treated as constant tempo */
4125 return ts_at->frames_per_quarter_note (_frame_rate);
4129 TempoMap::meter_section_at_frame (framepos_t frame) const
4131 Glib::Threads::RWLock::ReaderLock lm (lock);
4132 return meter_section_at_minute_locked (_metrics, minute_at_frame (frame));
4136 TempoMap::meter_section_at_minute_locked (const Metrics& metrics, double minute) const
4138 Metrics::const_iterator i;
4139 MeterSection* prev = 0;
4143 for (i = metrics.begin(); i != metrics.end(); ++i) {
4145 if (!(*i)->is_tempo()) {
4146 m = static_cast<MeterSection*> (*i);
4148 if (prev && (*i)->minute() > minute) {
4158 abort(); /*NOTREACHED*/
4165 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4167 MeterSection* prev_m = 0;
4169 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4171 if (!(*i)->is_tempo()) {
4172 m = static_cast<MeterSection*> (*i);
4173 if (prev_m && m->beat() > beat) {
4184 TempoMap::meter_section_at_beat (double beat) const
4186 Glib::Threads::RWLock::ReaderLock lm (lock);
4187 return meter_section_at_beat_locked (_metrics, beat);
4191 TempoMap::meter_at_frame (framepos_t frame) const
4193 TempoMetric m (metric_at (frame));
4198 TempoMap::fix_legacy_session ()
4200 MeterSection* prev_m = 0;
4201 TempoSection* prev_t = 0;
4203 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4207 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
4209 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
4212 m->set_minute (0.0);
4213 m->set_position_lock_style (AudioTime);
4218 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
4219 + (m->bbt().beats - 1)
4220 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
4222 m->set_beat (start);
4223 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
4224 + (m->bbt().beats - 1)
4225 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
4226 m->set_pulse (start_beat / prev_m->note_divisor());
4229 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4237 t->set_minute (0.0);
4238 t->set_position_lock_style (AudioTime);
4244 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
4245 + (t->legacy_bbt().beats - 1)
4246 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
4248 t->set_pulse (beat / prev_m->note_divisor());
4250 /* really shouldn't happen but.. */
4251 t->set_pulse (beat / 4.0);
4260 TempoMap::get_state ()
4262 Metrics::const_iterator i;
4263 XMLNode *root = new XMLNode ("TempoMap");
4266 Glib::Threads::RWLock::ReaderLock lm (lock);
4267 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4268 root->add_child_nocopy ((*i)->get_state());
4276 TempoMap::set_state (const XMLNode& node, int /*version*/)
4279 Glib::Threads::RWLock::WriterLock lm (lock);
4282 XMLNodeConstIterator niter;
4283 Metrics old_metrics (_metrics);
4286 nlist = node.children();
4288 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
4289 XMLNode* child = *niter;
4291 if (child->name() == TempoSection::xml_state_node_name) {
4294 TempoSection* ts = new TempoSection (*child, _frame_rate);
4295 _metrics.push_back (ts);
4298 catch (failed_constructor& err){
4299 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4300 _metrics = old_metrics;
4301 old_metrics.clear();
4305 } else if (child->name() == MeterSection::xml_state_node_name) {
4308 MeterSection* ms = new MeterSection (*child, _frame_rate);
4309 _metrics.push_back (ms);
4312 catch (failed_constructor& err) {
4313 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4314 _metrics = old_metrics;
4315 old_metrics.clear();
4321 if (niter == nlist.end()) {
4322 MetricSectionSorter cmp;
4323 _metrics.sort (cmp);
4326 /* check for legacy sessions where bbt was the base musical unit for tempo */
4327 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4329 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
4330 if (t->legacy_bbt().bars != 0) {
4331 fix_legacy_session();
4338 /* check for multiple tempo/meters at the same location, which
4339 ardour2 somehow allowed.
4342 Metrics::iterator prev = _metrics.end();
4343 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4344 if (prev != _metrics.end()) {
4346 MeterSection* prev_m;
4348 TempoSection* prev_t;
4349 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
4350 if (prev_m->pulse() == ms->pulse()) {
4351 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4352 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4355 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
4356 if (prev_t->pulse() == ts->pulse()) {
4357 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4358 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4366 recompute_map (_metrics);
4368 Metrics::const_iterator d = old_metrics.begin();
4369 while (d != old_metrics.end()) {
4373 old_metrics.clear ();
4376 PropertyChanged (PropertyChange ());
4382 TempoMap::dump (std::ostream& o) const
4384 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
4385 const MeterSection* m;
4386 const TempoSection* t;
4387 const TempoSection* prev_t = 0;
4389 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4391 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
4392 o << "Tempo @ " << *i << t->note_types_per_minute() << " BPM (pulse = 1/" << t->note_type()
4393 << " type= " << enum_2_string (t->type()) << ") " << " at pulse= " << t->pulse()
4394 << " minute= " << t->minute() << " frame= " << t->frame() << " (initial? " << t->initial() << ')'
4395 << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
4397 o << " current : " << t->note_types_per_minute()
4398 << " | " << t->pulse() << " | " << t->frame() << " | " << t->minute() << std::endl;
4399 o << " previous : " << prev_t->note_types_per_minute()
4400 << " | " << prev_t->pulse() << " | " << prev_t->frame() << " | " << prev_t->minute() << std::endl;
4401 o << " calculated : " << prev_t->tempo_at_pulse (t->pulse())
4402 << " | " << prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute())
4403 << " | " << frame_at_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()))
4404 << " | " << prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()) << std::endl;
4407 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
4408 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt()
4409 << " frame= " << m->frame() << " pulse: " << m->pulse() << " beat : " << m->beat()
4410 << " pos lock: " << enum_2_string (m->position_lock_style()) << " (initial? " << m->initial() << ')' << endl;
4413 o << "------" << std::endl;
4417 TempoMap::n_tempos() const
4419 Glib::Threads::RWLock::ReaderLock lm (lock);
4422 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4423 if ((*i)->is_tempo()) {
4432 TempoMap::n_meters() const
4434 Glib::Threads::RWLock::ReaderLock lm (lock);
4437 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4438 if (!(*i)->is_tempo()) {
4447 TempoMap::insert_time (framepos_t where, framecnt_t amount)
4449 for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
4450 if ((*i)->frame() >= where && !(*i)->initial ()) {
4454 if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
4455 gui_set_meter_position (ms, (*i)->frame() + amount);
4458 if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
4459 gui_set_tempo_position (ts, (*i)->frame() + amount, 0);
4464 PropertyChanged (PropertyChange ());
4468 TempoMap::remove_time (framepos_t where, framecnt_t amount)
4472 std::list<MetricSection*> metric_kill_list;
4474 TempoSection* last_tempo = NULL;
4475 MeterSection* last_meter = NULL;
4476 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
4477 bool meter_after = false; // is there a meter marker likewise?
4479 Glib::Threads::RWLock::WriterLock lm (lock);
4480 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4481 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
4482 metric_kill_list.push_back(*i);
4483 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
4486 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
4490 else if ((*i)->frame() >= where) {
4491 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
4492 (*i)->set_minute ((*i)->minute() - minute_at_frame (amount));
4493 if ((*i)->frame() == where) {
4494 // marker was immediately after end of range
4495 tempo_after = dynamic_cast<TempoSection*> (*i);
4496 meter_after = dynamic_cast<MeterSection*> (*i);
4502 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
4503 if (last_tempo && !tempo_after) {
4504 metric_kill_list.remove(last_tempo);
4505 last_tempo->set_minute (minute_at_frame (where));
4508 if (last_meter && !meter_after) {
4509 metric_kill_list.remove(last_meter);
4510 last_meter->set_minute (minute_at_frame (where));
4514 //remove all the remaining metrics
4515 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
4516 _metrics.remove(*i);
4521 recompute_map (_metrics);
4524 PropertyChanged (PropertyChange ());
4528 /** Add some (fractional) Beats to a session frame position, and return the result in frames.
4529 * pos can be -ve, if required.
4532 TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats beats) const
4534 Glib::Threads::RWLock::ReaderLock lm (lock);
4535 const double frame_qn = quarter_note_at_minute_locked (_metrics, minute_at_frame (frame));
4537 return frame_at_minute (minute_at_quarter_note_locked (_metrics, frame_qn + beats.to_double()));
4541 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
4543 Glib::Threads::RWLock::ReaderLock lm (lock);
4545 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_minute_locked (_metrics, minute_at_frame (pos)));
4546 pos_bbt.ticks += op.ticks;
4547 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
4549 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
4551 pos_bbt.beats += op.beats;
4552 /* the meter in effect will start on the bar */
4553 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();
4554 while (pos_bbt.beats >= divisions_per_bar + 1) {
4556 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4557 pos_bbt.beats -= divisions_per_bar;
4559 pos_bbt.bars += op.bars;
4561 return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
4564 /** Count the number of beats that are equivalent to distance when going forward,
4568 TempoMap::framewalk_to_qn (framepos_t pos, framecnt_t distance) const
4570 Glib::Threads::RWLock::ReaderLock lm (lock);
4572 return Evoral::Beats (quarter_notes_between_frames_locked (_metrics, pos, pos + distance));
4576 bool operator() (const BBT_Time& a, const BBT_Time& b) {
4582 operator<< (std::ostream& o, const Meter& m) {
4583 return o << m.divisions_per_bar() << '/' << m.note_divisor();
4587 operator<< (std::ostream& o, const Tempo& t) {
4588 return o << t.note_types_per_minute() << " 1/" << t.note_type() << "'s per minute";
4592 operator<< (std::ostream& o, const MetricSection& section) {
4594 o << "MetricSection @ " << section.frame() << ' ';
4596 const TempoSection* ts;
4597 const MeterSection* ms;
4599 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
4600 o << *((const Tempo*) ts);
4601 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
4602 o << *((const Meter*) ms);