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_func == 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_func == 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_func == 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_func == 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_func == 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_func == 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_func == 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_func == 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) {
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) {
449 return c_func (end_npm, end_minute - minute());
452 /* position function */
454 TempoSection::a_func (double end_npm, double c_func) const
456 return log (end_npm / note_types_per_minute()) / c_func;
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_func * 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_func;
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_func) / _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_func) + note_types_per_minute();
494 /* pulse at time in minutes */
496 TempoSection::_pulse_at_time (const double& time) const
498 return (expm1 (_c_func * time) * (note_types_per_minute() / _c_func)) / _note_type;
501 /* time in minutes at pulse */
503 TempoSection::_time_at_pulse (const double& pulse) const
505 return log1p ((_c_func * pulse * _note_type) / note_types_per_minute()) / _c_func;
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 m->set_initial (true);
748 /* note: frame time is correct (zero) for both of these */
750 _metrics.push_back (t);
751 _metrics.push_back (m);
755 TempoMap::TempoMap (TempoMap const & other)
757 _frame_rate = other._frame_rate;
758 for (Metrics::const_iterator m = other._metrics.begin(); m != other._metrics.end(); ++m) {
759 TempoSection* ts = dynamic_cast<TempoSection*> (*m);
760 MeterSection* ms = dynamic_cast<MeterSection*> (*m);
763 TempoSection* new_section = new TempoSection (*ts);
764 _metrics.push_back (new_section);
766 MeterSection* new_section = new MeterSection (*ms);
767 _metrics.push_back (new_section);
773 TempoMap::operator= (TempoMap const & other)
775 if (&other != this) {
776 _frame_rate = other._frame_rate;
778 Metrics::const_iterator d = _metrics.begin();
779 while (d != _metrics.end()) {
785 for (Metrics::const_iterator m = other._metrics.begin(); m != other._metrics.end(); ++m) {
786 TempoSection* ts = dynamic_cast<TempoSection*> (*m);
787 MeterSection* ms = dynamic_cast<MeterSection*> (*m);
790 TempoSection* new_section = new TempoSection (*ts);
791 _metrics.push_back (new_section);
793 MeterSection* new_section = new MeterSection (*ms);
794 _metrics.push_back (new_section);
799 PropertyChanged (PropertyChange());
804 TempoMap::~TempoMap ()
806 Metrics::const_iterator d = _metrics.begin();
807 while (d != _metrics.end()) {
815 TempoMap::frame_at_minute (const double time) const
817 return (framepos_t) floor ((time * 60.0 * _frame_rate) + 0.5);
821 TempoMap::minute_at_frame (const framepos_t frame) const
823 return (frame / (double) _frame_rate) / 60.0;
827 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
829 bool removed = false;
832 Glib::Threads::RWLock::WriterLock lm (lock);
833 if ((removed = remove_tempo_locked (tempo))) {
834 if (complete_operation) {
835 recompute_map (_metrics);
840 if (removed && complete_operation) {
841 PropertyChanged (PropertyChange ());
846 TempoMap::remove_tempo_locked (const TempoSection& tempo)
850 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
851 if (dynamic_cast<TempoSection*> (*i) != 0) {
852 if (tempo.frame() == (*i)->frame()) {
853 if (!(*i)->initial()) {
866 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
868 bool removed = false;
871 Glib::Threads::RWLock::WriterLock lm (lock);
872 if ((removed = remove_meter_locked (tempo))) {
873 if (complete_operation) {
874 recompute_map (_metrics);
879 if (removed && complete_operation) {
880 PropertyChanged (PropertyChange ());
885 TempoMap::remove_meter_locked (const MeterSection& meter)
888 if (meter.position_lock_style() == AudioTime) {
889 /* remove meter-locked tempo */
890 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
892 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
893 if (t->locked_to_meter() && meter.frame() == (*i)->frame()) {
902 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
903 if (dynamic_cast<MeterSection*> (*i) != 0) {
904 if (meter.frame() == (*i)->frame()) {
905 if (!(*i)->initial()) {
918 TempoMap::do_insert (MetricSection* section)
920 bool need_add = true;
921 /* we only allow new meters to be inserted on beat 1 of an existing
925 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
927 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
929 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
930 corrected.second.beats = 1;
931 corrected.second.ticks = 0;
932 corrected.first = beat_at_bbt_locked (_metrics, corrected.second);
933 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
934 m->bbt(), corrected.second) << endmsg;
935 //m->set_pulse (corrected);
939 /* Look for any existing MetricSection that is of the same type and
940 in the same bar as the new one, and remove it before adding
941 the new one. Note that this means that if we find a matching,
942 existing section, we can break out of the loop since we're
943 guaranteed that there is only one such match.
946 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
948 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
949 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
950 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
951 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
953 if (tempo && insert_tempo) {
956 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
957 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
959 if (tempo->initial()) {
961 /* can't (re)move this section, so overwrite
962 * its data content (but not its properties as
966 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
967 (*i)->set_position_lock_style (AudioTime);
969 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
970 t->set_type (insert_tempo->type());
980 } else if (meter && insert_meter) {
984 bool const ipm = insert_meter->position_lock_style() == MusicTime;
986 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
988 if (meter->initial()) {
990 /* can't (re)move this section, so overwrite
991 * its data content (but not its properties as
995 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
996 (*i)->set_position_lock_style (AudioTime);
1006 /* non-matching types, so we don't care */
1010 /* Add the given MetricSection, if we didn't just reset an existing
1015 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
1016 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
1017 Metrics::iterator i;
1020 TempoSection* prev_t = 0;
1022 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1023 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
1024 bool const ipm = insert_meter->position_lock_style() == MusicTime;
1027 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
1031 if (prev_t && prev_t->locked_to_meter() && (!ipm && prev_t->frame() == insert_meter->frame())) {
1035 prev_t = dynamic_cast<TempoSection*> (*i);
1038 } else if (insert_tempo) {
1039 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1040 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
1043 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
1044 const bool lm = insert_tempo->locked_to_meter();
1045 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())
1046 || (lm && tempo->pulse() > insert_tempo->pulse())) {
1053 _metrics.insert (i, section);
1057 /* user supplies the exact pulse if pls == MusicTime */
1059 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, ARDOUR::TempoSection::Type type, PositionLockStyle pls)
1061 TempoSection* ts = 0;
1063 Glib::Threads::RWLock::WriterLock lm (lock);
1064 ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), type, pls, true);
1068 PropertyChanged (PropertyChange ());
1074 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, TempoSection::Type type, PositionLockStyle pls)
1076 const bool locked_to_meter = ts.locked_to_meter();
1079 Glib::Threads::RWLock::WriterLock lm (lock);
1080 TempoSection& first (first_tempo());
1081 if (ts.frame() != first.frame()) {
1082 remove_tempo_locked (ts);
1083 add_tempo_locked (tempo, pulse, minute_at_frame (frame), type, pls, true, locked_to_meter);
1085 first.set_type (type);
1086 first.set_pulse (0.0);
1087 first.set_minute (minute_at_frame (frame));
1088 first.set_position_lock_style (AudioTime);
1090 /* cannot move the first tempo section */
1091 *static_cast<Tempo*>(&first) = tempo;
1092 recompute_map (_metrics);
1097 PropertyChanged (PropertyChange ());
1101 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, double minute
1102 , TempoSection::Type type, PositionLockStyle pls, bool recompute, bool locked_to_meter)
1104 TempoSection* t = new TempoSection (pulse, minute, tempo.note_types_per_minute(), tempo.note_type(), type, pls, _frame_rate);
1105 t->set_locked_to_meter (locked_to_meter);
1106 bool solved = false;
1111 if (pls == AudioTime) {
1112 solved = solve_map_minute (_metrics, t, t->minute());
1114 solved = solve_map_pulse (_metrics, t, t->pulse());
1116 recompute_meters (_metrics);
1119 if (!solved && recompute) {
1120 recompute_map (_metrics);
1127 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1129 MeterSection* m = 0;
1131 Glib::Threads::RWLock::WriterLock lm (lock);
1132 m = add_meter_locked (meter, beat, where, frame, pls, true);
1137 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1142 PropertyChanged (PropertyChange ());
1147 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1150 Glib::Threads::RWLock::WriterLock lm (lock);
1151 const double beat = beat_at_bbt_locked (_metrics, where);
1153 if (!ms.initial()) {
1154 remove_meter_locked (ms);
1155 add_meter_locked (meter, beat, where, frame, pls, true);
1157 MeterSection& first (first_meter());
1158 TempoSection& first_t (first_tempo());
1159 /* cannot move the first meter section */
1160 *static_cast<Meter*>(&first) = meter;
1161 first.set_position_lock_style (AudioTime);
1162 first.set_pulse (0.0);
1163 //first.set_minute (minute_at_frame (frame));
1164 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1165 first.set_beat (beat);
1166 first_t.set_minute (first.minute());
1167 first_t.set_pulse (0.0);
1168 first_t.set_position_lock_style (AudioTime);
1169 recompute_map (_metrics);
1173 PropertyChanged (PropertyChange ());
1177 TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute)
1179 const MeterSection& prev_m = meter_section_at_minute_locked (_metrics, minute_at_beat_locked (_metrics, beat) - minute_at_frame (1));
1180 const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1181 const double time_minutes = minute_at_pulse_locked (_metrics, pulse);
1182 TempoSection* mlt = 0;
1184 if (pls == AudioTime) {
1185 /* add meter-locked tempo */
1186 mlt = add_tempo_locked (tempo_at_minute_locked (_metrics, time_minutes), pulse, minute_at_frame (frame), TempoSection::Ramp, AudioTime, true, true);
1194 MeterSection* new_meter = new MeterSection (pulse, minute_at_frame (frame), beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls, _frame_rate);
1196 bool solved = false;
1198 do_insert (new_meter);
1202 if (pls == AudioTime) {
1203 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (frame));
1204 /* we failed, most likely due to some impossible frame requirement wrt audio-locked tempi.
1205 fudge frame so that the meter ends up at its BBT position instead.
1208 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (prev_m.frame() + 1));
1211 solved = solve_map_bbt (_metrics, new_meter, where);
1212 /* required due to resetting the pulse of meter-locked tempi above.
1213 Arguably solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
1214 but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
1216 recompute_map (_metrics);
1220 if (!solved && recompute) {
1221 /* if this has failed to solve, there is little we can do other than to ensure that
1222 the new map is recalculated.
1224 warning << "Adding meter may have left the tempo map unsolved." << endmsg;
1225 recompute_map (_metrics);
1232 TempoMap::change_initial_tempo (double note_types_per_minute, double note_type)
1234 Tempo newtempo (note_types_per_minute, note_type);
1237 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1238 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1243 Glib::Threads::RWLock::WriterLock lm (lock);
1244 *((Tempo*) t) = newtempo;
1245 recompute_map (_metrics);
1247 PropertyChanged (PropertyChange ());
1254 TempoMap::change_existing_tempo_at (framepos_t where, double note_types_per_minute, double note_type)
1256 Tempo newtempo (note_types_per_minute, note_type);
1259 TempoSection* first;
1260 Metrics::iterator i;
1262 /* find the TempoSection immediately preceding "where"
1265 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1267 if ((*i)->frame() > where) {
1273 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1286 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1296 Glib::Threads::RWLock::WriterLock lm (lock);
1297 /* cannot move the first tempo section */
1298 *((Tempo*)prev) = newtempo;
1299 recompute_map (_metrics);
1302 PropertyChanged (PropertyChange ());
1306 TempoMap::first_meter () const
1308 const MeterSection *m = 0;
1310 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1311 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1316 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1317 abort(); /*NOTREACHED*/
1322 TempoMap::first_meter ()
1324 MeterSection *m = 0;
1326 /* CALLER MUST HOLD LOCK */
1328 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1329 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1334 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1335 abort(); /*NOTREACHED*/
1340 TempoMap::first_tempo () const
1342 const TempoSection *t = 0;
1344 /* CALLER MUST HOLD LOCK */
1346 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1347 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1357 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1358 abort(); /*NOTREACHED*/
1363 TempoMap::first_tempo ()
1365 TempoSection *t = 0;
1367 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1368 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1378 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1379 abort(); /*NOTREACHED*/
1383 TempoMap::recompute_tempi (Metrics& metrics)
1385 TempoSection* prev_t = 0;
1387 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1390 if ((*i)->is_tempo()) {
1391 t = static_cast<TempoSection*> (*i);
1403 if (t->position_lock_style() == AudioTime) {
1404 prev_t->set_c_func (prev_t->compute_c_func_minute (t->note_types_per_minute(), t->minute()));
1405 if (!t->locked_to_meter()) {
1406 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
1410 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->note_types_per_minute(), t->pulse()));
1411 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
1419 prev_t->set_c_func (0.0);
1422 /* tempos must be positioned correctly.
1423 the current approach is to use a meter's bbt time as its base position unit.
1424 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1425 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1428 TempoMap::recompute_meters (Metrics& metrics)
1430 MeterSection* meter = 0;
1431 MeterSection* prev_m = 0;
1433 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1434 if (!(*mi)->is_tempo()) {
1435 meter = static_cast<MeterSection*> (*mi);
1436 if (meter->position_lock_style() == AudioTime) {
1438 pair<double, BBT_Time> b_bbt;
1439 TempoSection* meter_locked_tempo = 0;
1440 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1442 if ((*ii)->is_tempo()) {
1443 t = static_cast<TempoSection*> (*ii);
1444 if ((t->locked_to_meter() || t->initial()) && t->frame() == meter->frame()) {
1445 meter_locked_tempo = t;
1452 double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1453 if (beats + prev_m->beat() != meter->beat()) {
1454 /* reordering caused a bbt change */
1456 beats = meter->beat() - prev_m->beat();
1457 b_bbt = make_pair (beats + prev_m->beat()
1458 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1459 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1461 } else if (!meter->initial()) {
1462 b_bbt = make_pair (meter->beat(), meter->bbt());
1463 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1466 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1468 if (meter_locked_tempo) {
1469 meter_locked_tempo->set_pulse (pulse);
1471 meter->set_beat (b_bbt);
1472 meter->set_pulse (pulse);
1477 pair<double, BBT_Time> b_bbt;
1479 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1480 if (beats + prev_m->beat() != meter->beat()) {
1481 /* reordering caused a bbt change */
1482 b_bbt = make_pair (beats + prev_m->beat()
1483 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1485 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1487 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1489 /* shouldn't happen - the first is audio-locked */
1490 pulse = pulse_at_beat_locked (metrics, meter->beat());
1491 b_bbt = make_pair (meter->beat(), meter->bbt());
1494 meter->set_beat (b_bbt);
1495 meter->set_pulse (pulse);
1496 meter->set_minute (minute_at_pulse_locked (metrics, pulse));
1505 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1507 /* CALLER MUST HOLD WRITE LOCK */
1509 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1512 /* silly call from Session::process() during startup
1517 recompute_tempi (metrics);
1518 recompute_meters (metrics);
1522 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1524 Glib::Threads::RWLock::ReaderLock lm (lock);
1525 TempoMetric m (first_meter(), first_tempo());
1527 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1528 at something, because we insert the default tempo and meter during
1529 TempoMap construction.
1531 now see if we can find better candidates.
1534 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1536 if ((*i)->frame() > frame) {
1550 /* XX meters only */
1552 TempoMap::metric_at (BBT_Time bbt) const
1554 Glib::Threads::RWLock::ReaderLock lm (lock);
1555 TempoMetric m (first_meter(), first_tempo());
1557 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1558 at something, because we insert the default tempo and meter during
1559 TempoMap construction.
1561 now see if we can find better candidates.
1564 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1566 if (!(*i)->is_tempo()) {
1567 mw = static_cast<MeterSection*> (*i);
1568 BBT_Time section_start (mw->bbt());
1570 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1581 /** Returns the BBT (meter-based) beat corresponding to the supplied frame, possibly returning a negative value.
1582 * @param frame The session frame position.
1583 * @return The beat duration according to the tempo map at the supplied frame.
1585 * If the supplied frame lies before the first meter, the returned beat duration will be negative.
1586 * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
1588 * This function uses both tempo and meter.
1591 TempoMap::beat_at_frame (const framecnt_t& frame) const
1593 Glib::Threads::RWLock::ReaderLock lm (lock);
1595 return beat_at_minute_locked (_metrics, minute_at_frame (frame));
1598 /* This function uses both tempo and meter.*/
1600 TempoMap::beat_at_minute_locked (const Metrics& metrics, const double& minute) const
1602 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
1603 MeterSection* prev_m = 0;
1604 MeterSection* next_m = 0;
1606 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1607 if (!(*i)->is_tempo()) {
1608 if (prev_m && (*i)->minute() > minute) {
1609 next_m = static_cast<MeterSection*> (*i);
1612 prev_m = static_cast<MeterSection*> (*i);
1616 const double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
1618 /* audio locked meters fake their beat */
1619 if (next_m && next_m->beat() < beat) {
1620 return next_m->beat();
1626 /** Returns the frame corresponding to the supplied BBT (meter-based) beat.
1627 * @param beat The BBT (meter-based) beat.
1628 * @return The frame duration according to the tempo map at the supplied BBT (meter-based) beat.
1630 * This function uses both tempo and meter.
1633 TempoMap::frame_at_beat (const double& beat) const
1635 Glib::Threads::RWLock::ReaderLock lm (lock);
1637 return frame_at_minute (minute_at_beat_locked (_metrics, beat));
1640 /* meter & tempo section based */
1642 TempoMap::minute_at_beat_locked (const Metrics& metrics, const double& beat) const
1644 MeterSection* prev_m = 0;
1645 TempoSection* prev_t = 0;
1649 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1650 if (!(*i)->is_tempo()) {
1651 m = static_cast<MeterSection*> (*i);
1652 if (prev_m && m->beat() > beat) {
1662 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1663 if ((*i)->is_tempo()) {
1664 t = static_cast<TempoSection*> (*i);
1665 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1674 return prev_t->minute_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse());
1677 /** Returns a Tempo corresponding to the supplied frame position.
1678 * @param frame The audio frame.
1679 * @return a Tempo according to the tempo map at the supplied frame.
1683 TempoMap::tempo_at_frame (const framepos_t& frame) const
1685 Glib::Threads::RWLock::ReaderLock lm (lock);
1687 return tempo_at_minute_locked (_metrics, minute_at_frame (frame));
1691 TempoMap::tempo_at_minute_locked (const Metrics& metrics, const double& minute) const
1693 TempoSection* prev_t = 0;
1697 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1698 if ((*i)->is_tempo()) {
1699 t = static_cast<TempoSection*> (*i);
1703 if ((prev_t) && t->minute() > minute) {
1704 /* t is the section past frame */
1705 return prev_t->tempo_at_minute (minute);
1711 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type());
1714 /** returns the frame at which the supplied tempo occurs, or
1715 * the frame of the last tempo section (search exhausted)
1716 * only the position of the first occurence will be returned
1720 TempoMap::frame_at_tempo (const Tempo& tempo) const
1722 Glib::Threads::RWLock::ReaderLock lm (lock);
1724 return frame_at_minute (minute_at_tempo_locked (_metrics, tempo));
1728 TempoMap::minute_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1730 TempoSection* prev_t = 0;
1731 const double tempo_bpm = tempo.note_types_per_minute();
1733 Metrics::const_iterator i;
1735 for (i = metrics.begin(); i != metrics.end(); ++i) {
1737 if ((*i)->is_tempo()) {
1738 t = static_cast<TempoSection*> (*i);
1744 const double t_bpm = t->note_types_per_minute();
1746 if (t_bpm == tempo_bpm) {
1751 const double prev_t_bpm = prev_t->note_types_per_minute();
1753 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1754 return prev_t->minute_at_ntpm (prev_t->note_types_per_minute(), prev_t->pulse());
1761 return prev_t->minute();
1765 TempoMap::tempo_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1767 TempoSection* prev_t = 0;
1771 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1772 if ((*i)->is_tempo()) {
1773 t = static_cast<TempoSection*> (*i);
1777 if ((prev_t) && t->pulse() > pulse) {
1778 /* t is the section past frame */
1779 return prev_t->tempo_at_pulse (pulse);
1785 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type());
1789 TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1791 TempoSection* prev_t = 0;
1792 const double tempo_bpm = tempo.note_types_per_minute();
1794 Metrics::const_iterator i;
1796 for (i = metrics.begin(); i != metrics.end(); ++i) {
1798 if ((*i)->is_tempo()) {
1799 t = static_cast<TempoSection*> (*i);
1805 const double t_bpm = t->note_types_per_minute();
1807 if (t_bpm == tempo_bpm) {
1812 const double prev_t_bpm = prev_t->note_types_per_minute();
1814 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1815 return prev_t->pulse_at_ntpm (prev_t->note_types_per_minute(), prev_t->minute());
1822 return prev_t->pulse();
1825 /** Returns a Tempo corresponding to the supplied position in quarter-note beats.
1826 * @param qn the position in quarter note beats.
1827 * @return the Tempo at the supplied quarter-note.
1830 TempoMap::tempo_at_quarter_note (const double& qn) const
1832 Glib::Threads::RWLock::ReaderLock lm (lock);
1834 return tempo_at_pulse_locked (_metrics, qn / 4.0);
1837 /** Returns the position in quarter-note beats corresponding to the supplied Tempo.
1838 * @param tempo the tempo.
1839 * @return the position in quarter-note beats where the map bpm
1840 * is equal to that of the Tempo. currently ignores note_type.
1843 TempoMap::quarter_note_at_tempo (const Tempo& tempo) const
1845 Glib::Threads::RWLock::ReaderLock lm (lock);
1847 return pulse_at_tempo_locked (_metrics, tempo) * 4.0;;
1850 /** Returns the whole-note pulse corresponding to the supplied BBT (meter-based) beat.
1851 * @param metrics the list of metric sections used to calculate the pulse.
1852 * @param beat The BBT (meter-based) beat.
1853 * @return the whole-note pulse at the supplied BBT (meter-based) beat.
1855 * a pulse or whole note is the base musical position of a MetricSection.
1856 * it is equivalent to four quarter notes.
1860 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1862 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1864 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1867 /** Returns the BBT (meter-based) beat corresponding to the supplied whole-note pulse .
1868 * @param metrics the list of metric sections used to calculate the beat.
1869 * @param pulse the whole-note pulse.
1870 * @return the meter-based beat at the supplied whole-note pulse.
1872 * a pulse or whole note is the base musical position of a MetricSection.
1873 * it is equivalent to four quarter notes.
1876 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1878 MeterSection* prev_m = 0;
1880 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1882 if (!(*i)->is_tempo()) {
1883 m = static_cast<MeterSection*> (*i);
1884 if (prev_m && m->pulse() > pulse) {
1892 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1896 /* tempo section based */
1898 TempoMap::pulse_at_minute_locked (const Metrics& metrics, const double& minute) const
1900 /* HOLD (at least) THE READER LOCK */
1901 TempoSection* prev_t = 0;
1903 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1905 if ((*i)->is_tempo()) {
1906 t = static_cast<TempoSection*> (*i);
1910 if (prev_t && t->minute() > minute) {
1911 /*the previous ts is the one containing the frame */
1912 const double ret = prev_t->pulse_at_minute (minute);
1913 /* audio locked section in new meter*/
1914 if (t->pulse() < ret) {
1923 /* treated as constant for this ts */
1924 const double pulses_in_section = ((minute - prev_t->minute()) * prev_t->note_types_per_minute()) / prev_t->note_type();
1926 return pulses_in_section + prev_t->pulse();
1929 /* tempo section based */
1931 TempoMap::minute_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1933 /* HOLD THE READER LOCK */
1935 const TempoSection* prev_t = 0;
1937 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1940 if ((*i)->is_tempo()) {
1941 t = static_cast<TempoSection*> (*i);
1945 if (prev_t && t->pulse() > pulse) {
1946 return prev_t->minute_at_pulse (pulse);
1952 /* must be treated as constant, irrespective of _type */
1953 double const dtime = ((pulse - prev_t->pulse()) * prev_t->note_type()) / prev_t->note_types_per_minute();
1955 return dtime + prev_t->minute();
1958 /** Returns the BBT (meter-based) beat corresponding to the supplied BBT time.
1959 * @param bbt The BBT time (meter-based).
1960 * @return bbt The BBT beat (meter-based) at the supplied BBT time.
1964 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
1966 Glib::Threads::RWLock::ReaderLock lm (lock);
1967 return beat_at_bbt_locked (_metrics, bbt);
1972 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1974 /* CALLER HOLDS READ LOCK */
1976 MeterSection* prev_m = 0;
1978 /* because audio-locked meters have 'fake' integral beats,
1979 there is no pulse offset here.
1983 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1984 if (!(*i)->is_tempo()) {
1985 m = static_cast<MeterSection*> (*i);
1987 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1988 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1996 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1997 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1998 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
2003 /** Returns the BBT time corresponding to the supplied BBT (meter-based) beat.
2004 * @param beat The BBT (meter-based) beat.
2005 * @return The BBT time (meter-based) at the supplied meter-based beat.
2009 TempoMap::bbt_at_beat (const double& beat)
2011 Glib::Threads::RWLock::ReaderLock lm (lock);
2012 return bbt_at_beat_locked (_metrics, beat);
2016 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
2018 /* CALLER HOLDS READ LOCK */
2019 MeterSection* prev_m = 0;
2020 const double beats = max (0.0, b);
2022 MeterSection* m = 0;
2024 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2025 if (!(*i)->is_tempo()) {
2026 m = static_cast<MeterSection*> (*i);
2028 if (m->beat() > beats) {
2029 /* this is the meter after the one our beat is on*/
2039 const double beats_in_ms = beats - prev_m->beat();
2040 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2041 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2042 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2043 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2047 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2048 ret.beats = (uint32_t) floor (remaining_beats);
2049 ret.bars = total_bars;
2051 /* 0 0 0 to 1 1 0 - based mapping*/
2055 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2057 ret.ticks -= BBT_Time::ticks_per_beat;
2060 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2068 /** Returns the quarter-note beat corresponding to the supplied BBT time (meter-based).
2069 * @param bbt The BBT time (meter-based).
2070 * @return the quarter note beat at the supplied BBT time
2072 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2074 * while the input uses meter, the output does not.
2077 TempoMap::quarter_note_at_bbt (const Timecode::BBT_Time& bbt)
2079 Glib::Threads::RWLock::ReaderLock lm (lock);
2081 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2085 TempoMap::quarter_note_at_bbt_rt (const Timecode::BBT_Time& bbt)
2087 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2090 throw std::logic_error ("TempoMap::quarter_note_at_bbt_rt() could not lock tempo map");
2093 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2097 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2099 /* CALLER HOLDS READ LOCK */
2101 MeterSection* prev_m = 0;
2103 /* because audio-locked meters have 'fake' integral beats,
2104 there is no pulse offset here.
2108 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2109 if (!(*i)->is_tempo()) {
2110 m = static_cast<MeterSection*> (*i);
2112 if (m->bbt().bars > bbt.bars) {
2120 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2121 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
2122 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
2127 /** Returns the BBT time corresponding to the supplied quarter-note beat.
2128 * @param qn the quarter-note beat.
2129 * @return The BBT time (meter-based) at the supplied meter-based beat.
2131 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2135 TempoMap::bbt_at_quarter_note (const double& qn)
2137 Glib::Threads::RWLock::ReaderLock lm (lock);
2139 return bbt_at_pulse_locked (_metrics, qn / 4.0);
2142 /** Returns the BBT time (meter-based) corresponding to the supplied whole-note pulse position.
2143 * @param metrics The list of metric sections used to determine the result.
2144 * @param pulse The whole-note pulse.
2145 * @return The BBT time at the supplied whole-note pulse.
2147 * a pulse or whole note is the basic musical position of a MetricSection.
2148 * it is equivalent to four quarter notes.
2149 * while the output uses meter, the input does not.
2152 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2154 MeterSection* prev_m = 0;
2156 MeterSection* m = 0;
2158 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2160 if (!(*i)->is_tempo()) {
2161 m = static_cast<MeterSection*> (*i);
2164 double const pulses_to_m = m->pulse() - prev_m->pulse();
2165 if (prev_m->pulse() + pulses_to_m > pulse) {
2166 /* this is the meter after the one our beat is on*/
2177 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
2178 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2179 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2180 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2181 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2185 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2186 ret.beats = (uint32_t) floor (remaining_beats);
2187 ret.bars = total_bars;
2189 /* 0 0 0 to 1 1 0 mapping*/
2193 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2195 ret.ticks -= BBT_Time::ticks_per_beat;
2198 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2206 /** Returns the BBT time corresponding to the supplied frame position.
2207 * @param frame the position in audio samples.
2208 * @return the BBT time at the frame position .
2212 TempoMap::bbt_at_frame (framepos_t frame)
2219 warning << string_compose (_("tempo map was asked for BBT time at frame %1\n"), frame) << endmsg;
2222 Glib::Threads::RWLock::ReaderLock lm (lock);
2224 return bbt_at_minute_locked (_metrics, minute_at_frame (frame));
2228 TempoMap::bbt_at_frame_rt (framepos_t frame)
2230 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2233 throw std::logic_error ("TempoMap::bbt_at_frame_rt() could not lock tempo map");
2236 return bbt_at_minute_locked (_metrics, minute_at_frame (frame));
2240 TempoMap::bbt_at_minute_locked (const Metrics& metrics, const double& minute) const
2250 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
2251 MeterSection* prev_m = 0;
2252 MeterSection* next_m = 0;
2256 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2257 if (!(*i)->is_tempo()) {
2258 m = static_cast<MeterSection*> (*i);
2259 if (prev_m && m->minute() > minute) {
2267 double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
2269 /* handle frame before first meter */
2270 if (minute < prev_m->minute()) {
2273 /* audio locked meters fake their beat */
2274 if (next_m && next_m->beat() < beat) {
2275 beat = next_m->beat();
2278 beat = max (0.0, beat);
2280 const double beats_in_ms = beat - prev_m->beat();
2281 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2282 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2283 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2284 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2288 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2289 ret.beats = (uint32_t) floor (remaining_beats);
2290 ret.bars = total_bars;
2292 /* 0 0 0 to 1 1 0 - based mapping*/
2296 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2298 ret.ticks -= BBT_Time::ticks_per_beat;
2301 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2309 /** Returns the frame position corresponding to the supplied BBT time.
2310 * @param bbt the position in BBT time.
2311 * @return the frame position at bbt.
2315 TempoMap::frame_at_bbt (const BBT_Time& bbt)
2318 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
2322 if (bbt.beats < 1) {
2323 throw std::logic_error ("beats are counted from one");
2325 Glib::Threads::RWLock::ReaderLock lm (lock);
2327 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
2330 /* meter & tempo section based */
2332 TempoMap::minute_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2334 /* HOLD THE READER LOCK */
2336 const double ret = minute_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2341 * Returns the quarter-note beat position corresponding to the supplied frame.
2343 * @param frame the position in frames.
2344 * @return The quarter-note position of the supplied frame. Ignores meter.
2348 TempoMap::quarter_note_at_frame (const framepos_t frame) const
2350 Glib::Threads::RWLock::ReaderLock lm (lock);
2352 const double ret = quarter_note_at_minute_locked (_metrics, minute_at_frame (frame));
2358 TempoMap::quarter_note_at_minute_locked (const Metrics& metrics, const double minute) const
2360 const double ret = pulse_at_minute_locked (metrics, minute) * 4.0;
2366 TempoMap::quarter_note_at_frame_rt (const framepos_t frame) const
2368 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2371 throw std::logic_error ("TempoMap::quarter_note_at_frame_rt() could not lock tempo map");
2374 const double ret = pulse_at_minute_locked (_metrics, minute_at_frame (frame)) * 4.0;
2380 * Returns the frame position corresponding to the supplied quarter-note beat.
2382 * @param quarter_note the quarter-note position.
2383 * @return the frame position of the supplied quarter-note. Ignores meter.
2388 TempoMap::frame_at_quarter_note (const double quarter_note) const
2390 Glib::Threads::RWLock::ReaderLock lm (lock);
2392 const framepos_t ret = frame_at_minute (minute_at_quarter_note_locked (_metrics, quarter_note));
2398 TempoMap::minute_at_quarter_note_locked (const Metrics& metrics, const double quarter_note) const
2400 const double ret = minute_at_pulse_locked (metrics, quarter_note / 4.0);
2405 /** Returns the quarter-note beats corresponding to the supplied BBT (meter-based) beat.
2406 * @param beat The BBT (meter-based) beat.
2407 * @return The quarter-note position of the supplied BBT (meter-based) beat.
2409 * a quarter-note may be compared with and assigned to Evoral::Beats.
2413 TempoMap::quarter_note_at_beat (const double beat) const
2415 Glib::Threads::RWLock::ReaderLock lm (lock);
2417 const double ret = quarter_note_at_beat_locked (_metrics, beat);
2423 TempoMap::quarter_note_at_beat_locked (const Metrics& metrics, const double beat) const
2425 const double ret = pulse_at_beat_locked (metrics, beat) * 4.0;
2430 /** Returns the BBT (meter-based) beat position corresponding to the supplied quarter-note beats.
2431 * @param quarter_note The position in quarter-note beats.
2432 * @return the BBT (meter-based) beat position of the supplied quarter-note beats.
2434 * a quarter-note is the musical unit of Evoral::Beats.
2438 TempoMap::beat_at_quarter_note (const double quarter_note) const
2440 Glib::Threads::RWLock::ReaderLock lm (lock);
2442 const double ret = beat_at_quarter_note_locked (_metrics, quarter_note);
2448 TempoMap::beat_at_quarter_note_locked (const Metrics& metrics, const double quarter_note) const
2451 return beat_at_pulse_locked (metrics, quarter_note / 4.0);
2454 /** Returns the duration in frames between two supplied quarter-note beat positions.
2455 * @param start the first position in quarter-note beats.
2456 * @param end the end position in quarter-note beats.
2457 * @return the frame distance ober the quarter-note beats duration.
2459 * use this rather than e.g.
2460 * frame_at-quarter_note (end_beats) - frame_at_quarter_note (start_beats).
2461 * frames_between_quarter_notes() doesn't round to audio frames as an intermediate step,
2465 TempoMap::frames_between_quarter_notes (const double start, const double end) const
2467 Glib::Threads::RWLock::ReaderLock lm (lock);
2469 return frame_at_minute (minutes_between_quarter_notes_locked (_metrics, start, end));
2473 TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end) const
2476 return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
2480 TempoMap::quarter_notes_between_frames (const framecnt_t start, const framecnt_t end) const
2482 Glib::Threads::RWLock::ReaderLock lm (lock);
2484 return quarter_notes_between_frames_locked (_metrics, start, end);
2488 TempoMap::quarter_notes_between_frames_locked (const Metrics& metrics, const framecnt_t start, const framecnt_t end) const
2490 const TempoSection* prev_t = 0;
2492 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2495 if ((*i)->is_tempo()) {
2496 t = static_cast<TempoSection*> (*i);
2500 if (prev_t && t->frame() > start) {
2507 const double start_qn = prev_t->pulse_at_frame (start);
2509 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2512 if ((*i)->is_tempo()) {
2513 t = static_cast<TempoSection*> (*i);
2517 if (prev_t && t->frame() > end) {
2523 const double end_qn = prev_t->pulse_at_frame (end);
2525 return (end_qn - start_qn) * 4.0;
2529 TempoMap::check_solved (const Metrics& metrics) const
2531 TempoSection* prev_t = 0;
2532 MeterSection* prev_m = 0;
2534 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2537 if ((*i)->is_tempo()) {
2538 t = static_cast<TempoSection*> (*i);
2543 /* check ordering */
2544 if ((t->minute() <= prev_t->minute()) || (t->pulse() <= prev_t->pulse())) {
2548 /* precision check ensures tempo and frames align.*/
2549 if (t->frame() != frame_at_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()))) {
2550 if (!t->locked_to_meter()) {
2555 /* gradient limit - who knows what it should be?
2556 things are also ok (if a little chaotic) without this
2558 if (fabs (prev_t->c_func()) > 1000.0) {
2559 //std::cout << "c : " << prev_t->c_func() << std::endl;
2566 if (!(*i)->is_tempo()) {
2567 m = static_cast<MeterSection*> (*i);
2568 if (prev_m && m->position_lock_style() == AudioTime) {
2569 const TempoSection* t = &tempo_section_at_minute_locked (metrics, minute_at_frame (m->frame() - 1));
2570 const double nascent_m_minute = t->minute_at_pulse (m->pulse());
2571 /* Here we check that a preceding section of music doesn't overlap a subsequent one.
2573 if (t && (nascent_m_minute > m->minute() || nascent_m_minute < 0.0)) {
2587 TempoMap::set_active_tempi (const Metrics& metrics, const framepos_t& frame)
2589 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2591 if ((*i)->is_tempo()) {
2592 t = static_cast<TempoSection*> (*i);
2594 t->set_active (true);
2596 } else if (t->position_lock_style() == AudioTime) {
2597 if (t->active () && t->frame() < frame) {
2598 t->set_active (false);
2600 } else if (t->frame() > frame) {
2601 t->set_active (true);
2602 } else if (t->frame() == frame) {
2612 TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const double& minute)
2614 TempoSection* prev_t = 0;
2615 TempoSection* section_prev = 0;
2616 double first_m_minute = 0.0;
2617 const bool sml = section->locked_to_meter();
2619 /* can't move a tempo before the first meter */
2620 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2622 if (!(*i)->is_tempo()) {
2623 m = static_cast<MeterSection*> (*i);
2625 first_m_minute = m->minute();
2630 if (!section->initial() && minute <= first_m_minute) {
2634 section->set_active (true);
2635 section->set_minute (minute);
2637 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2639 if ((*i)->is_tempo()) {
2640 t = static_cast<TempoSection*> (*i);
2652 if (t->frame() == frame_at_minute (minute)) {
2656 const bool tlm = t->position_lock_style() == MusicTime;
2658 if (prev_t && !section_prev && ((sml && tlm && t->pulse() > section->pulse()) || (!tlm && t->minute() > minute))) {
2659 section_prev = prev_t;
2661 if (t->locked_to_meter()) {
2662 section_prev->set_c_func (section_prev->compute_c_func_minute (section->note_types_per_minute(), minute));
2663 if (!section->locked_to_meter()) {
2664 section->set_pulse (section_prev->pulse_at_ntpm (section->note_types_per_minute(), minute));
2670 if (t->position_lock_style() == MusicTime) {
2671 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->note_types_per_minute(), t->pulse()));
2672 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
2674 prev_t->set_c_func (prev_t->compute_c_func_minute (t->note_types_per_minute(), t->minute()));
2675 if (!t->locked_to_meter()) {
2676 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
2685 section_prev->set_c_func (section_prev->compute_c_func_minute (section->note_types_per_minute(), minute));
2686 if (!section->locked_to_meter()) {
2687 section->set_pulse (section_prev->pulse_at_ntpm (section->note_types_per_minute(), minute));
2692 recompute_tempi (imaginary);
2694 if (check_solved (imaginary)) {
2697 dunp (imaginary, std::cout);
2701 MetricSectionFrameSorter fcmp;
2702 imaginary.sort (fcmp);
2704 recompute_tempi (imaginary);
2706 if (check_solved (imaginary)) {
2714 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2716 TempoSection* prev_t = 0;
2717 TempoSection* section_prev = 0;
2719 section->set_pulse (pulse);
2721 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2723 if ((*i)->is_tempo()) {
2724 t = static_cast<TempoSection*> (*i);
2735 section_prev = prev_t;
2738 if (t->position_lock_style() == MusicTime) {
2739 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->note_types_per_minute(), t->pulse()));
2740 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
2742 prev_t->set_c_func (prev_t->compute_c_func_minute (t->note_types_per_minute(), t->minute()));
2743 if (!t->locked_to_meter()) {
2744 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
2753 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->note_types_per_minute(), pulse));
2754 section->set_minute (section_prev->minute_at_ntpm (section->note_types_per_minute(), pulse));
2758 recompute_tempi (imaginary);
2760 if (check_solved (imaginary)) {
2763 dunp (imaginary, std::cout);
2767 MetricSectionSorter cmp;
2768 imaginary.sort (cmp);
2770 recompute_tempi (imaginary);
2772 * XX need a restriction here, but only for this case,
2773 * as audio locked tempos don't interact in the same way.
2775 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2777 * |50 bpm |250 bpm |60 bpm
2778 * drag 250 to the pulse after 60->
2779 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2781 if (check_solved (imaginary)) {
2789 TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const double& minute)
2791 /* disallow moving first meter past any subsequent one, and any initial meter before the first one */
2792 const MeterSection* other = &meter_section_at_minute_locked (imaginary, minute);
2793 if ((section->initial() && !other->initial()) || (other->initial() && !section->initial() && other->minute() >= minute)) {
2797 if (section->initial()) {
2798 /* lock the first tempo to our first meter */
2799 if (!set_active_tempi (imaginary, section->frame_at_minute (minute))) {
2804 TempoSection* meter_locked_tempo = 0;
2806 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2808 if ((*ii)->is_tempo()) {
2809 t = static_cast<TempoSection*> (*ii);
2810 if ((t->locked_to_meter() || t->initial()) && t->frame() == section->frame()) {
2811 meter_locked_tempo = t;
2817 if (!meter_locked_tempo) {
2821 MeterSection* prev_m = 0;
2823 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2824 bool solved = false;
2826 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2828 if (!(*i)->is_tempo()) {
2829 m = static_cast<MeterSection*> (*i);
2831 if (prev_m && !section->initial()) {
2832 const double beats = (pulse_at_minute_locked (imaginary, minute) - prev_m->pulse()) * prev_m->note_divisor();
2833 if (beats + prev_m->beat() < section->beat()) {
2834 /* set the section pulse according to its musical position,
2835 * as an earlier time than this has been requested.
2837 const double new_pulse = ((section->beat() - prev_m->beat())
2838 / prev_m->note_divisor()) + prev_m->pulse();
2840 tempo_copy->set_position_lock_style (MusicTime);
2841 if ((solved = solve_map_pulse (future_map, tempo_copy, new_pulse))) {
2842 meter_locked_tempo->set_position_lock_style (MusicTime);
2843 section->set_position_lock_style (MusicTime);
2844 section->set_pulse (new_pulse);
2845 solve_map_pulse (imaginary, meter_locked_tempo, new_pulse);
2846 meter_locked_tempo->set_position_lock_style (AudioTime);
2847 section->set_position_lock_style (AudioTime);
2848 section->set_minute (meter_locked_tempo->minute());
2854 Metrics::const_iterator d = future_map.begin();
2855 while (d != future_map.end()) {
2864 /* all is ok. set section's locked tempo if allowed.
2865 possibly disallow if there is an adjacent audio-locked tempo.
2866 XX this check could possibly go. its never actually happened here.
2868 MeterSection* meter_copy = const_cast<MeterSection*>
2869 (&meter_section_at_minute_locked (future_map, section->minute()));
2871 meter_copy->set_minute (minute);
2873 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2874 section->set_minute (minute);
2875 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2876 / prev_m->note_divisor()) + prev_m->pulse());
2877 solve_map_minute (imaginary, meter_locked_tempo, minute);
2882 Metrics::const_iterator d = future_map.begin();
2883 while (d != future_map.end()) {
2893 /* initial (first meter atm) */
2895 tempo_copy->set_minute (minute);
2896 tempo_copy->set_pulse (0.0);
2898 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2899 section->set_minute (minute);
2900 meter_locked_tempo->set_minute (minute);
2901 meter_locked_tempo->set_pulse (0.0);
2902 solve_map_minute (imaginary, meter_locked_tempo, minute);
2907 Metrics::const_iterator d = future_map.begin();
2908 while (d != future_map.end()) {
2917 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2918 section->set_beat (b_bbt);
2919 section->set_pulse (0.0);
2929 MetricSectionFrameSorter fcmp;
2930 imaginary.sort (fcmp);
2932 recompute_meters (imaginary);
2938 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2940 /* disallow setting section to an existing meter's bbt */
2941 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2943 if (!(*i)->is_tempo()) {
2944 m = static_cast<MeterSection*> (*i);
2945 if (m != section && m->bbt().bars == when.bars) {
2951 MeterSection* prev_m = 0;
2952 MeterSection* section_prev = 0;
2954 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2956 if (!(*i)->is_tempo()) {
2957 m = static_cast<MeterSection*> (*i);
2963 pair<double, BBT_Time> b_bbt;
2964 double new_pulse = 0.0;
2966 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2967 section_prev = prev_m;
2969 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2970 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2971 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2973 section->set_beat (b_bbt);
2974 section->set_pulse (pulse);
2975 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
2979 if (m->position_lock_style() == AudioTime) {
2980 TempoSection* meter_locked_tempo = 0;
2982 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2984 if ((*ii)->is_tempo()) {
2985 t = static_cast<TempoSection*> (*ii);
2986 if ((t->locked_to_meter() || t->initial()) && t->frame() == m->frame()) {
2987 meter_locked_tempo = t;
2993 if (!meter_locked_tempo) {
2998 double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3000 if (beats + prev_m->beat() != m->beat()) {
3001 /* tempo/ meter change caused a change in beat (bar). */
3003 /* the user has requested that the previous section of music overlaps this one.
3004 we have no choice but to change the bar number here, as being locked to audio means
3005 we must stay where we are on the timeline.
3007 beats = m->beat() - prev_m->beat();
3008 b_bbt = make_pair (beats + prev_m->beat()
3009 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3010 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3012 } else if (!m->initial()) {
3013 b_bbt = make_pair (m->beat(), m->bbt());
3014 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3017 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3020 meter_locked_tempo->set_pulse (new_pulse);
3021 m->set_beat (b_bbt);
3022 m->set_pulse (new_pulse);
3026 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3027 if (beats + prev_m->beat() != m->beat()) {
3028 /* tempo/ meter change caused a change in beat (bar). */
3029 b_bbt = make_pair (beats + prev_m->beat()
3030 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3032 b_bbt = make_pair (beats + prev_m->beat()
3035 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3036 m->set_beat (b_bbt);
3037 m->set_pulse (new_pulse);
3038 m->set_minute (minute_at_pulse_locked (imaginary, new_pulse));
3045 if (!section_prev) {
3047 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
3048 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3049 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
3051 section->set_beat (b_bbt);
3052 section->set_pulse (pulse);
3053 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
3056 MetricSectionSorter cmp;
3057 imaginary.sort (cmp);
3059 recompute_meters (imaginary);
3064 /** places a copy of _metrics into copy and returns a pointer
3065 * to section's equivalent in copy.
3068 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
3070 TempoSection* ret = 0;
3072 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3075 if ((*i)->is_tempo()) {
3076 t = static_cast<TempoSection*> (*i);
3078 ret = new TempoSection (*t);
3079 copy.push_back (ret);
3083 TempoSection* cp = new TempoSection (*t);
3084 copy.push_back (cp);
3086 if (!(*i)->is_tempo()) {
3087 m = static_cast<MeterSection *> (*i);
3088 MeterSection* cp = new MeterSection (*m);
3089 copy.push_back (cp);
3097 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
3099 MeterSection* ret = 0;
3101 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3104 if ((*i)->is_tempo()) {
3105 t = static_cast<TempoSection*> (*i);
3106 TempoSection* cp = new TempoSection (*t);
3107 copy.push_back (cp);
3110 if (!(*i)->is_tempo()) {
3111 m = static_cast<MeterSection *> (*i);
3113 ret = new MeterSection (*m);
3114 copy.push_back (ret);
3117 MeterSection* cp = new MeterSection (*m);
3118 copy.push_back (cp);
3125 /** answers the question "is this a valid beat position for this tempo section?".
3126 * it returns true if the tempo section can be moved to the requested bbt position,
3127 * leaving the tempo map in a solved state.
3128 * @param ts the tempo section to be moved
3129 * @param bbt the requested new position for the tempo section
3130 * @return true if the tempo section can be moved to the position, otherwise false.
3133 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
3136 TempoSection* tempo_copy = 0;
3139 Glib::Threads::RWLock::ReaderLock lm (lock);
3140 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
3146 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
3148 Metrics::const_iterator d = copy.begin();
3149 while (d != copy.end()) {
3158 * 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,
3159 * taking any possible reordering as a consequence of this into account.
3160 * @param section - the section to be altered
3161 * @param bbt - the BBT time where the altered tempo will fall
3162 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
3164 pair<double, framepos_t>
3165 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
3168 pair<double, framepos_t> ret = make_pair (0.0, 0);
3170 Glib::Threads::RWLock::ReaderLock lm (lock);
3172 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
3174 const double beat = beat_at_bbt_locked (future_map, bbt);
3176 if (section->position_lock_style() == AudioTime) {
3177 tempo_copy->set_position_lock_style (MusicTime);
3180 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
3181 ret.first = tempo_copy->pulse();
3182 ret.second = tempo_copy->frame();
3184 ret.first = section->pulse();
3185 ret.second = section->frame();
3188 Metrics::const_iterator d = future_map.begin();
3189 while (d != future_map.end()) {
3196 /** moves a TempoSection to a specified position.
3197 * @param ts - the section to be moved
3198 * @param frame - the new position in frames for the tempo
3199 * @param sub_num - the snap division to use if using musical time.
3201 * if sub_num is non-zero, the frame position is used to calculate an exact
3204 * -1 | snap to bars (meter-based)
3205 * 0 | no snap - use audio frame for musical position
3206 * 1 | snap to meter-based (BBT) beat
3207 * >1 | snap to quarter-note subdivision (i.e. 4 will snap to sixteenth notes)
3209 * this follows the snap convention in the gui.
3210 * if sub_num is zero, the musical position will be taken from the supplied frame.
3213 TempoMap::gui_set_tempo_position (TempoSection* ts, const framepos_t& frame, const int& sub_num)
3217 if (ts->position_lock_style() == MusicTime) {
3219 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
3220 Glib::Threads::RWLock::WriterLock lm (lock);
3221 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3223 tempo_copy->set_position_lock_style (AudioTime);
3225 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3226 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
3227 const double pulse = pulse_at_beat_locked (future_map, beat);
3229 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3230 solve_map_pulse (_metrics, ts, pulse);
3231 recompute_meters (_metrics);
3239 Glib::Threads::RWLock::WriterLock lm (lock);
3240 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3242 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3244 /* We're moving the object that defines the grid while snapping to it...
3245 * Placing the ts at the beat corresponding to the requested frame may shift the
3246 * grid in such a way that the mouse is left hovering over a completerly different division,
3247 * causing jittering when the mouse next moves (esp. large tempo deltas).
3249 * This alters the snap behaviour slightly in that we snap to beat divisions
3250 * in the future map rather than the existing one.
3252 const double qn = exact_qn_at_frame_locked (future_map, frame, sub_num);
3253 const framepos_t snapped_frame = frame_at_minute (minute_at_quarter_note_locked (future_map, qn));
3255 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (snapped_frame))) {
3256 solve_map_minute (_metrics, ts, minute_at_frame (snapped_frame));
3257 ts->set_pulse (qn / 4.0);
3258 recompute_meters (_metrics);
3261 solve_map_minute (_metrics, ts, minute_at_frame (frame));
3262 recompute_meters (_metrics);
3268 Metrics::const_iterator d = future_map.begin();
3269 while (d != future_map.end()) {
3274 MetricPositionChanged (); // Emit Signal
3277 /** moves a MeterSection to a specified position.
3278 * @param ms - the section to be moved
3279 * @param frame - the new position in frames for the meter
3281 * as a meter cannot snap to anything but bars,
3282 * the supplied frame is rounded to the nearest bar, possibly
3283 * leaving the meter position unchanged.
3286 TempoMap::gui_set_meter_position (MeterSection* ms, const framepos_t& frame)
3290 if (ms->position_lock_style() == AudioTime) {
3293 Glib::Threads::RWLock::WriterLock lm (lock);
3294 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3296 if (solve_map_minute (future_map, copy, minute_at_frame (frame))) {
3297 solve_map_minute (_metrics, ms, minute_at_frame (frame));
3298 recompute_tempi (_metrics);
3303 Glib::Threads::RWLock::WriterLock lm (lock);
3304 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3306 const double beat = beat_at_minute_locked (_metrics, minute_at_frame (frame));
3307 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
3309 if (solve_map_bbt (future_map, copy, bbt)) {
3310 solve_map_bbt (_metrics, ms, bbt);
3311 recompute_tempi (_metrics);
3316 Metrics::const_iterator d = future_map.begin();
3317 while (d != future_map.end()) {
3322 MetricPositionChanged (); // Emit Signal
3326 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
3329 bool can_solve = false;
3331 Glib::Threads::RWLock::WriterLock lm (lock);
3332 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3333 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3334 recompute_tempi (future_map);
3336 if (check_solved (future_map)) {
3337 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3338 recompute_map (_metrics);
3343 Metrics::const_iterator d = future_map.begin();
3344 while (d != future_map.end()) {
3349 MetricPositionChanged (); // Emit Signal
3355 TempoMap::gui_stretch_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame)
3358 Ts (future prev_t) Tnext
3361 |----------|----------
3368 Glib::Threads::RWLock::WriterLock lm (lock);
3374 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3375 TempoSection* prev_to_prev_t = 0;
3376 const frameoffset_t fr_off = end_frame - frame;
3380 if (prev_t->pulse() > 0.0) {
3381 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_minute_locked (future_map, minute_at_frame (prev_t->frame() - 1)));
3384 TempoSection* next_t = 0;
3385 for (Metrics::iterator i = future_map.begin(); i != future_map.end(); ++i) {
3386 TempoSection* t = 0;
3387 if ((*i)->is_tempo()) {
3388 t = static_cast<TempoSection*> (*i);
3389 if (t->frame() > ts->frame()) {
3395 /* minimum allowed measurement distance in frames */
3396 const framepos_t min_dframe = 2;
3398 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
3399 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
3401 double contribution = 0.0;
3403 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3404 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3407 const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
3409 const double start_pulse = prev_t->pulse_at_minute (minute_at_frame (frame));
3410 const double end_pulse = prev_t->pulse_at_minute (minute_at_frame (end_frame));
3414 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
3416 if (prev_t->position_lock_style() == MusicTime) {
3417 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3418 if (frame > prev_to_prev_t->frame() + min_dframe && (frame + prev_t_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
3420 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame())
3421 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
3423 new_bpm = prev_t->note_types_per_minute();
3426 /* prev to prev is irrelevant */
3428 if (start_pulse > prev_t->pulse() && end_pulse > prev_t->pulse()) {
3429 new_bpm = prev_t->note_types_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
3431 new_bpm = prev_t->note_types_per_minute();
3436 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3437 if (frame > prev_to_prev_t->frame() + min_dframe && end_frame > prev_to_prev_t->frame() + min_dframe) {
3439 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame())
3440 / (double) ((end_frame) - prev_to_prev_t->frame()));
3442 new_bpm = prev_t->note_types_per_minute();
3445 /* prev_to_prev_t is irrelevant */
3447 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3448 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
3450 new_bpm = prev_t->note_types_per_minute();
3456 double frame_ratio = 1.0;
3457 double pulse_ratio = 1.0;
3458 const double pulse_pos = frame;
3460 if (prev_to_prev_t) {
3461 if (pulse_pos > prev_to_prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_to_prev_t->frame() + min_dframe) {
3462 frame_ratio = (((pulse_pos - fr_off) - prev_to_prev_t->frame()) / (double) ((pulse_pos) - prev_to_prev_t->frame()));
3464 if (end_pulse > prev_to_prev_t->pulse() && start_pulse > prev_to_prev_t->pulse()) {
3465 pulse_ratio = ((start_pulse - prev_to_prev_t->pulse()) / (end_pulse - prev_to_prev_t->pulse()));
3468 if (pulse_pos > prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_t->frame() + min_dframe) {
3469 frame_ratio = (((pulse_pos - fr_off) - prev_t->frame()) / (double) ((pulse_pos) - prev_t->frame()));
3471 pulse_ratio = (start_pulse / end_pulse);
3473 new_bpm = prev_t->note_types_per_minute() * (pulse_ratio * frame_ratio);
3476 /* don't clamp and proceed here.
3477 testing has revealed that this can go negative,
3478 which is an entirely different thing to just being too low.
3480 if (new_bpm < 0.5) {
3483 new_bpm = min (new_bpm, (double) 1000.0);
3484 prev_t->set_note_types_per_minute (new_bpm);
3485 recompute_tempi (future_map);
3486 recompute_meters (future_map);
3488 if (check_solved (future_map)) {
3489 ts->set_note_types_per_minute (new_bpm);
3490 recompute_tempi (_metrics);
3491 recompute_meters (_metrics);
3495 Metrics::const_iterator d = future_map.begin();
3496 while (d != future_map.end()) {
3501 MetricPositionChanged (); // Emit Signal
3504 /** Returns the exact bbt-based beat corresponding to the bar, beat or quarter note subdivision nearest to
3505 * the supplied frame, possibly returning a negative value.
3507 * @param frame The session frame position.
3508 * @param sub_num The subdivision to use when rounding the beat.
3509 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3510 * Positive integers indicate quarter note (non BBT) divisions.
3511 * 0 indicates that the returned beat should not be rounded (equivalent to quarter_note_at_frame()).
3512 * @return The beat position of the supplied frame.
3514 * when working to a musical grid, the use of sub_nom indicates that
3515 * the position should be interpreted musically.
3517 * it effectively snaps to meter bars, meter beats or quarter note divisions
3518 * (as per current gui convention) and returns a musical position independent of frame rate.
3520 * If the supplied frame lies before the first meter, the return will be negative,
3521 * in which case the returned beat uses the first meter (for BBT subdivisions) and
3522 * the continuation of the tempo curve (backwards).
3524 * This function is sensitive to tempo and meter.
3527 TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num) const
3529 Glib::Threads::RWLock::ReaderLock lm (lock);
3531 return exact_beat_at_frame_locked (_metrics, frame, sub_num);
3535 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t divisions) const
3537 return beat_at_pulse_locked (_metrics, exact_qn_at_frame_locked (metrics, frame, divisions) / 4.0);
3540 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
3541 * the supplied frame, possibly returning a negative value.
3543 * @param frame The session frame position.
3544 * @param sub_num The subdivision to use when rounding the quarter note.
3545 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3546 * Positive integers indicate quarter note (non BBT) divisions.
3547 * 0 indicates that the returned quarter note should not be rounded (equivalent to quarter_note_at_frame()).
3548 * @return The quarter note position of the supplied frame.
3550 * When working to a musical grid, the use of sub_nom indicates that
3551 * the frame position should be interpreted musically.
3553 * it effectively snaps to meter bars, meter beats or quarter note divisions
3554 * (as per current gui convention) and returns a musical position independent of frame rate.
3556 * If the supplied frame lies before the first meter, the return will be negative,
3557 * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
3558 * the continuation of the tempo curve (backwards).
3560 * This function is tempo-sensitive.
3563 TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num) const
3565 Glib::Threads::RWLock::ReaderLock lm (lock);
3567 return exact_qn_at_frame_locked (_metrics, frame, sub_num);
3571 TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num) const
3573 double qn = quarter_note_at_minute_locked (metrics, minute_at_frame (frame));
3576 qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
3577 } else if (sub_num == 1) {
3578 /* the gui requested exact musical (BBT) beat */
3579 qn = quarter_note_at_beat_locked (metrics, floor (beat_at_minute_locked (metrics, minute_at_frame (frame)) + 0.5));
3580 } else if (sub_num == -1) {
3582 Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
3586 const double prev_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3588 const double next_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3590 if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
3600 /** returns the frame duration of the supplied BBT time at a specified frame position in the tempo map.
3601 * @param pos the frame position in the tempo map.
3602 * @param bbt the distance in BBT time from pos to calculate.
3603 * @param dir the rounding direction..
3604 * @return the duration in frames between pos and bbt
3607 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
3609 Glib::Threads::RWLock::ReaderLock lm (lock);
3611 BBT_Time pos_bbt = bbt_at_minute_locked (_metrics, minute_at_frame (pos));
3613 const double divisions = meter_section_at_minute_locked (_metrics, minute_at_frame (pos)).divisions_per_bar();
3616 pos_bbt.bars += bbt.bars;
3618 pos_bbt.ticks += bbt.ticks;
3619 if ((double) pos_bbt.ticks > BBT_Time::ticks_per_beat) {
3621 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3624 pos_bbt.beats += bbt.beats;
3625 if ((double) pos_bbt.beats > divisions) {
3627 pos_bbt.beats -= divisions;
3629 const framecnt_t pos_bbt_frame = frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3631 return pos_bbt_frame - pos;
3635 if (pos_bbt.bars <= bbt.bars) {
3638 pos_bbt.bars -= bbt.bars;
3641 if (pos_bbt.ticks < bbt.ticks) {
3642 if (pos_bbt.bars > 1) {
3643 if (pos_bbt.beats == 1) {
3645 pos_bbt.beats = divisions;
3649 pos_bbt.ticks = BBT_Time::ticks_per_beat - (bbt.ticks - pos_bbt.ticks);
3655 pos_bbt.ticks -= bbt.ticks;
3658 if (pos_bbt.beats <= bbt.beats) {
3659 if (pos_bbt.bars > 1) {
3661 pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
3666 pos_bbt.beats -= bbt.beats;
3669 return pos - frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3676 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
3678 return round_to_type (fr, dir, Bar);
3682 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
3684 return round_to_type (fr, dir, Beat);
3688 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3690 Glib::Threads::RWLock::ReaderLock lm (lock);
3691 uint32_t ticks = (uint32_t) floor (max (0.0, beat_at_minute_locked (_metrics, minute_at_frame (fr))) * BBT_Time::ticks_per_beat);
3692 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3693 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3695 ticks -= beats * BBT_Time::ticks_per_beat;
3698 /* round to next (or same iff dir == RoundUpMaybe) */
3700 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3702 if (mod == 0 && dir == RoundUpMaybe) {
3703 /* right on the subdivision, which is fine, so do nothing */
3705 } else if (mod == 0) {
3706 /* right on the subdivision, so the difference is just the subdivision ticks */
3707 ticks += ticks_one_subdivisions_worth;
3710 /* not on subdivision, compute distance to next subdivision */
3712 ticks += ticks_one_subdivisions_worth - mod;
3715 if (ticks >= BBT_Time::ticks_per_beat) {
3716 ticks -= BBT_Time::ticks_per_beat;
3718 } else if (dir < 0) {
3720 /* round to previous (or same iff dir == RoundDownMaybe) */
3722 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3724 if (difference == 0 && dir == RoundDownAlways) {
3725 /* right on the subdivision, but force-rounding down,
3726 so the difference is just the subdivision ticks */
3727 difference = ticks_one_subdivisions_worth;
3730 if (ticks < difference) {
3731 ticks = BBT_Time::ticks_per_beat - ticks;
3733 ticks -= difference;
3737 /* round to nearest */
3740 /* compute the distance to the previous and next subdivision */
3742 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3744 /* closer to the next subdivision, so shift forward */
3746 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3748 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3750 if (ticks > BBT_Time::ticks_per_beat) {
3752 ticks -= BBT_Time::ticks_per_beat;
3753 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3756 } else if (rem > 0) {
3758 /* closer to previous subdivision, so shift backward */
3762 /* can't go backwards past zero, so ... */
3765 /* step back to previous beat */
3767 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3768 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3770 ticks = lrint (ticks - rem);
3771 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3774 /* on the subdivision, do nothing */
3778 const framepos_t ret_frame = frame_at_minute (minute_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat)));
3784 TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3786 Glib::Threads::RWLock::ReaderLock lm (lock);
3787 uint32_t ticks = (uint32_t) floor (max (0.0, quarter_note_at_minute_locked (_metrics, minute_at_frame (fr))) * BBT_Time::ticks_per_beat);
3788 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3789 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3791 ticks -= beats * BBT_Time::ticks_per_beat;
3794 /* round to next (or same iff dir == RoundUpMaybe) */
3796 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3798 if (mod == 0 && dir == RoundUpMaybe) {
3799 /* right on the subdivision, which is fine, so do nothing */
3801 } else if (mod == 0) {
3802 /* right on the subdivision, so the difference is just the subdivision ticks */
3803 ticks += ticks_one_subdivisions_worth;
3806 /* not on subdivision, compute distance to next subdivision */
3808 ticks += ticks_one_subdivisions_worth - mod;
3811 //NOTE: this code intentionally limits the rounding so we don't advance to the next beat.
3812 // For the purposes of "jump-to-next-subdivision", we DO want to advance to the next beat.
3813 // And since the "prev" direction DOES move beats, I assume this code is unintended.
3814 // But I'm keeping it around, until we determine there are no terrible consequences.
3815 // if (ticks >= BBT_Time::ticks_per_beat) {
3816 // ticks -= BBT_Time::ticks_per_beat;
3819 } else if (dir < 0) {
3821 /* round to previous (or same iff dir == RoundDownMaybe) */
3823 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3825 if (difference == 0 && dir == RoundDownAlways) {
3826 /* right on the subdivision, but force-rounding down,
3827 so the difference is just the subdivision ticks */
3828 difference = ticks_one_subdivisions_worth;
3831 if (ticks < difference) {
3832 ticks = BBT_Time::ticks_per_beat - ticks;
3834 ticks -= difference;
3838 /* round to nearest */
3841 /* compute the distance to the previous and next subdivision */
3843 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3845 /* closer to the next subdivision, so shift forward */
3847 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3849 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3851 if (ticks > BBT_Time::ticks_per_beat) {
3853 ticks -= BBT_Time::ticks_per_beat;
3854 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3857 } else if (rem > 0) {
3859 /* closer to previous subdivision, so shift backward */
3863 /* can't go backwards past zero, so ... */
3866 /* step back to previous beat */
3868 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3869 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3871 ticks = lrint (ticks - rem);
3872 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3875 /* on the subdivision, do nothing */
3879 const framepos_t ret_frame = frame_at_minute (minute_at_quarter_note_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat)));
3885 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
3887 Glib::Threads::RWLock::ReaderLock lm (lock);
3889 const double beat_at_framepos = max (0.0, beat_at_minute_locked (_metrics, minute_at_frame (frame)));
3890 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
3895 /* find bar previous to 'frame' */
3900 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3902 } else if (dir > 0) {
3903 /* find bar following 'frame' */
3907 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3909 /* true rounding: find nearest bar */
3910 framepos_t raw_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3913 framepos_t prev_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3915 framepos_t next_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3917 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
3928 return frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos)));
3929 } else if (dir > 0) {
3930 return frame_at_minute (minute_at_beat_locked (_metrics, ceil (beat_at_framepos)));
3932 return frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5)));
3941 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
3942 framepos_t lower, framepos_t upper, uint32_t bar_mod)
3944 Glib::Threads::RWLock::ReaderLock lm (lock);
3945 int32_t cnt = ceil (beat_at_minute_locked (_metrics, minute_at_frame (lower)));
3947 /* although the map handles negative beats, bbt doesn't. */
3952 if (minute_at_beat_locked (_metrics, cnt) >= minute_at_frame (upper)) {
3956 while (pos >= 0 && pos < upper) {
3957 pos = frame_at_minute (minute_at_beat_locked (_metrics, cnt));
3958 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
3959 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
3960 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
3961 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c_func()));
3965 BBT_Time bbt = bbt_at_minute_locked (_metrics, minute_at_frame (lower));
3970 bbt.bars -= bbt.bars % bar_mod;
3974 while (pos >= 0 && pos < upper) {
3975 pos = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3976 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
3977 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
3978 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c_func()));
3979 bbt.bars += bar_mod;
3985 TempoMap::tempo_section_at_frame (framepos_t frame) const
3987 Glib::Threads::RWLock::ReaderLock lm (lock);
3989 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
3993 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute) const
3995 TempoSection* prev = 0;
3999 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4001 if ((*i)->is_tempo()) {
4002 t = static_cast<TempoSection*> (*i);
4006 if (prev && t->minute() > minute) {
4016 abort(); /*NOTREACHED*/
4023 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4025 TempoSection* prev_t = 0;
4026 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
4030 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4031 if ((*i)->is_tempo()) {
4032 t = static_cast<TempoSection*> (*i);
4033 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
4043 /* don't use this to calculate length (the tempo is only correct for this frame).
4044 do that stuff based on the beat_at_frame and frame_at_beat api
4047 TempoMap::frames_per_quarter_note_at (const framepos_t& frame, const framecnt_t& sr) const
4049 Glib::Threads::RWLock::ReaderLock lm (lock);
4051 const TempoSection* ts_at = 0;
4052 const TempoSection* ts_after = 0;
4053 Metrics::const_iterator i;
4056 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4058 if ((*i)->is_tempo()) {
4059 t = static_cast<TempoSection*> (*i);
4063 if (ts_at && (*i)->frame() > frame) {
4073 return (60.0 * _frame_rate) / ts_at->tempo_at_minute (minute_at_frame (frame)).quarter_notes_per_minute();
4075 /* must be treated as constant tempo */
4076 return ts_at->frames_per_quarter_note (_frame_rate);
4080 TempoMap::meter_section_at_frame (framepos_t frame) const
4082 Glib::Threads::RWLock::ReaderLock lm (lock);
4083 return meter_section_at_minute_locked (_metrics, minute_at_frame (frame));
4087 TempoMap::meter_section_at_minute_locked (const Metrics& metrics, double minute) const
4089 Metrics::const_iterator i;
4090 MeterSection* prev = 0;
4094 for (i = metrics.begin(); i != metrics.end(); ++i) {
4096 if (!(*i)->is_tempo()) {
4097 m = static_cast<MeterSection*> (*i);
4099 if (prev && (*i)->minute() > minute) {
4109 abort(); /*NOTREACHED*/
4116 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4118 MeterSection* prev_m = 0;
4120 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4122 if (!(*i)->is_tempo()) {
4123 m = static_cast<MeterSection*> (*i);
4124 if (prev_m && m->beat() > beat) {
4135 TempoMap::meter_section_at_beat (double beat) const
4137 Glib::Threads::RWLock::ReaderLock lm (lock);
4138 return meter_section_at_beat_locked (_metrics, beat);
4142 TempoMap::meter_at_frame (framepos_t frame) const
4144 TempoMetric m (metric_at (frame));
4149 TempoMap::fix_legacy_session ()
4151 MeterSection* prev_m = 0;
4152 TempoSection* prev_t = 0;
4154 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4158 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
4160 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
4163 m->set_minute (0.0);
4164 m->set_position_lock_style (AudioTime);
4169 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
4170 + (m->bbt().beats - 1)
4171 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
4173 m->set_beat (start);
4174 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
4175 + (m->bbt().beats - 1)
4176 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
4177 m->set_pulse (start_beat / prev_m->note_divisor());
4180 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4188 t->set_minute (0.0);
4189 t->set_position_lock_style (AudioTime);
4195 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
4196 + (t->legacy_bbt().beats - 1)
4197 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
4199 t->set_pulse (beat / prev_m->note_divisor());
4201 /* really shouldn't happen but.. */
4202 t->set_pulse (beat / 4.0);
4211 TempoMap::get_state ()
4213 Metrics::const_iterator i;
4214 XMLNode *root = new XMLNode ("TempoMap");
4217 Glib::Threads::RWLock::ReaderLock lm (lock);
4218 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4219 root->add_child_nocopy ((*i)->get_state());
4227 TempoMap::set_state (const XMLNode& node, int /*version*/)
4230 Glib::Threads::RWLock::WriterLock lm (lock);
4233 XMLNodeConstIterator niter;
4234 Metrics old_metrics (_metrics);
4237 nlist = node.children();
4239 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
4240 XMLNode* child = *niter;
4242 if (child->name() == TempoSection::xml_state_node_name) {
4245 TempoSection* ts = new TempoSection (*child, _frame_rate);
4246 _metrics.push_back (ts);
4249 catch (failed_constructor& err){
4250 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4251 _metrics = old_metrics;
4252 old_metrics.clear();
4256 } else if (child->name() == MeterSection::xml_state_node_name) {
4259 MeterSection* ms = new MeterSection (*child, _frame_rate);
4260 _metrics.push_back (ms);
4263 catch (failed_constructor& err) {
4264 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4265 _metrics = old_metrics;
4266 old_metrics.clear();
4272 if (niter == nlist.end()) {
4273 MetricSectionSorter cmp;
4274 _metrics.sort (cmp);
4277 /* check for legacy sessions where bbt was the base musical unit for tempo */
4278 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4280 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
4281 if (t->legacy_bbt().bars != 0) {
4282 fix_legacy_session();
4289 /* check for multiple tempo/meters at the same location, which
4290 ardour2 somehow allowed.
4293 Metrics::iterator prev = _metrics.end();
4294 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4295 if (prev != _metrics.end()) {
4297 MeterSection* prev_m;
4299 TempoSection* prev_t;
4300 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
4301 if (prev_m->pulse() == ms->pulse()) {
4302 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4303 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4306 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
4307 if (prev_t->pulse() == ts->pulse()) {
4308 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4309 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4317 recompute_map (_metrics);
4319 Metrics::const_iterator d = old_metrics.begin();
4320 while (d != old_metrics.end()) {
4324 old_metrics.clear ();
4327 PropertyChanged (PropertyChange ());
4333 TempoMap::dump (std::ostream& o) const
4335 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
4336 const MeterSection* m;
4337 const TempoSection* t;
4338 const TempoSection* prev_t = 0;
4340 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4342 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
4343 o << "Tempo @ " << *i << t->note_types_per_minute() << " BPM (pulse = 1/" << t->note_type()
4344 << " type= " << enum_2_string (t->type()) << ") " << " at pulse= " << t->pulse()
4345 << " minute= " << t->minute() << " frame= " << t->frame() << " (initial? " << t->initial() << ')'
4346 << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
4348 o << " current : " << t->note_types_per_minute()
4349 << " | " << t->pulse() << " | " << t->frame() << " | " << t->minute() << std::endl;
4350 o << " previous : " << prev_t->note_types_per_minute()
4351 << " | " << prev_t->pulse() << " | " << prev_t->frame() << " | " << prev_t->minute() << std::endl;
4352 o << " calculated : " << prev_t->tempo_at_pulse (t->pulse())
4353 << " | " << prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute())
4354 << " | " << frame_at_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()))
4355 << " | " << prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()) << std::endl;
4358 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
4359 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt()
4360 << " frame= " << m->frame() << " pulse: " << m->pulse() << " beat : " << m->beat()
4361 << " pos lock: " << enum_2_string (m->position_lock_style()) << " (initial? " << m->initial() << ')' << endl;
4364 o << "------" << std::endl;
4368 TempoMap::n_tempos() const
4370 Glib::Threads::RWLock::ReaderLock lm (lock);
4373 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4374 if ((*i)->is_tempo()) {
4383 TempoMap::n_meters() const
4385 Glib::Threads::RWLock::ReaderLock lm (lock);
4388 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4389 if (!(*i)->is_tempo()) {
4398 TempoMap::insert_time (framepos_t where, framecnt_t amount)
4400 for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
4401 if ((*i)->frame() >= where && !(*i)->initial ()) {
4405 if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
4406 gui_set_meter_position (ms, (*i)->frame() + amount);
4409 if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
4410 gui_set_tempo_position (ts, (*i)->frame() + amount, 0);
4415 PropertyChanged (PropertyChange ());
4419 TempoMap::remove_time (framepos_t where, framecnt_t amount)
4423 std::list<MetricSection*> metric_kill_list;
4425 TempoSection* last_tempo = NULL;
4426 MeterSection* last_meter = NULL;
4427 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
4428 bool meter_after = false; // is there a meter marker likewise?
4430 Glib::Threads::RWLock::WriterLock lm (lock);
4431 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4432 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
4433 metric_kill_list.push_back(*i);
4434 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
4437 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
4441 else if ((*i)->frame() >= where) {
4442 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
4443 (*i)->set_minute ((*i)->minute() - minute_at_frame (amount));
4444 if ((*i)->frame() == where) {
4445 // marker was immediately after end of range
4446 tempo_after = dynamic_cast<TempoSection*> (*i);
4447 meter_after = dynamic_cast<MeterSection*> (*i);
4453 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
4454 if (last_tempo && !tempo_after) {
4455 metric_kill_list.remove(last_tempo);
4456 last_tempo->set_minute (minute_at_frame (where));
4459 if (last_meter && !meter_after) {
4460 metric_kill_list.remove(last_meter);
4461 last_meter->set_minute (minute_at_frame (where));
4465 //remove all the remaining metrics
4466 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
4467 _metrics.remove(*i);
4472 recompute_map (_metrics);
4475 PropertyChanged (PropertyChange ());
4479 /** Add some (fractional) Beats to a session frame position, and return the result in frames.
4480 * pos can be -ve, if required.
4483 TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats beats) const
4485 Glib::Threads::RWLock::ReaderLock lm (lock);
4486 const double frame_qn = quarter_note_at_minute_locked (_metrics, minute_at_frame (frame));
4488 return frame_at_minute (minute_at_quarter_note_locked (_metrics, frame_qn + beats.to_double()));
4492 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
4494 Glib::Threads::RWLock::ReaderLock lm (lock);
4496 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_minute_locked (_metrics, minute_at_frame (pos)));
4497 pos_bbt.ticks += op.ticks;
4498 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
4500 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
4502 pos_bbt.beats += op.beats;
4503 /* the meter in effect will start on the bar */
4504 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();
4505 while (pos_bbt.beats >= divisions_per_bar + 1) {
4507 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4508 pos_bbt.beats -= divisions_per_bar;
4510 pos_bbt.bars += op.bars;
4512 return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
4515 /** Count the number of beats that are equivalent to distance when going forward,
4519 TempoMap::framewalk_to_qn (framepos_t pos, framecnt_t distance) const
4521 Glib::Threads::RWLock::ReaderLock lm (lock);
4523 return Evoral::Beats (quarter_notes_between_frames_locked (_metrics, pos, pos + distance));
4527 bool operator() (const BBT_Time& a, const BBT_Time& b) {
4533 operator<< (std::ostream& o, const Meter& m) {
4534 return o << m.divisions_per_bar() << '/' << m.note_divisor();
4538 operator<< (std::ostream& o, const Tempo& t) {
4539 return o << t.note_types_per_minute() << " 1/" << t.note_type() << "'s per minute";
4543 operator<< (std::ostream& o, const MetricSection& section) {
4545 o << "MetricSection @ " << section.frame() << ' ';
4547 const TempoSection* ts;
4548 const MeterSection* ms;
4550 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
4551 o << *((const Tempo*) ts);
4552 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
4553 o << *((const Meter*) ms);