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 if (tempo.note_types_per_minute() <= 0.0) {
1065 TempoSection* ts = 0;
1067 Glib::Threads::RWLock::WriterLock lm (lock);
1068 ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), type, pls, true);
1072 PropertyChanged (PropertyChange ());
1078 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, TempoSection::Type type, PositionLockStyle pls)
1080 if (tempo.note_types_per_minute() <= 0.0) {
1084 const bool locked_to_meter = ts.locked_to_meter();
1087 Glib::Threads::RWLock::WriterLock lm (lock);
1088 TempoSection& first (first_tempo());
1089 if (ts.frame() != first.frame()) {
1090 remove_tempo_locked (ts);
1091 add_tempo_locked (tempo, pulse, minute_at_frame (frame), type, pls, true, locked_to_meter);
1093 first.set_type (type);
1094 first.set_pulse (0.0);
1095 first.set_minute (minute_at_frame (frame));
1096 first.set_position_lock_style (AudioTime);
1098 /* cannot move the first tempo section */
1099 *static_cast<Tempo*>(&first) = tempo;
1100 recompute_map (_metrics);
1105 PropertyChanged (PropertyChange ());
1109 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, double minute
1110 , TempoSection::Type type, PositionLockStyle pls, bool recompute, bool locked_to_meter)
1112 TempoSection* t = new TempoSection (pulse, minute, tempo.note_types_per_minute(), tempo.note_type(), type, pls, _frame_rate);
1113 t->set_locked_to_meter (locked_to_meter);
1114 bool solved = false;
1119 if (pls == AudioTime) {
1120 solved = solve_map_minute (_metrics, t, t->minute());
1122 solved = solve_map_pulse (_metrics, t, t->pulse());
1124 recompute_meters (_metrics);
1127 if (!solved && recompute) {
1128 recompute_map (_metrics);
1135 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1137 MeterSection* m = 0;
1139 Glib::Threads::RWLock::WriterLock lm (lock);
1140 m = add_meter_locked (meter, beat, where, frame, pls, true);
1145 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1150 PropertyChanged (PropertyChange ());
1155 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, framepos_t frame, PositionLockStyle pls)
1158 Glib::Threads::RWLock::WriterLock lm (lock);
1159 const double beat = beat_at_bbt_locked (_metrics, where);
1161 if (!ms.initial()) {
1162 remove_meter_locked (ms);
1163 add_meter_locked (meter, beat, where, frame, pls, true);
1165 MeterSection& first (first_meter());
1166 TempoSection& first_t (first_tempo());
1167 /* cannot move the first meter section */
1168 *static_cast<Meter*>(&first) = meter;
1169 first.set_position_lock_style (AudioTime);
1170 first.set_pulse (0.0);
1171 //first.set_minute (minute_at_frame (frame));
1172 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1173 first.set_beat (beat);
1174 first_t.set_minute (first.minute());
1175 first_t.set_pulse (0.0);
1176 first_t.set_position_lock_style (AudioTime);
1177 recompute_map (_metrics);
1181 PropertyChanged (PropertyChange ());
1185 TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, framepos_t frame, PositionLockStyle pls, bool recompute)
1187 const MeterSection& prev_m = meter_section_at_minute_locked (_metrics, minute_at_beat_locked (_metrics, beat) - minute_at_frame (1));
1188 const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1189 const double time_minutes = minute_at_pulse_locked (_metrics, pulse);
1190 TempoSection* mlt = 0;
1192 if (pls == AudioTime) {
1193 /* add meter-locked tempo */
1194 mlt = add_tempo_locked (tempo_at_minute_locked (_metrics, time_minutes), pulse, minute_at_frame (frame), TempoSection::Ramp, AudioTime, true, true);
1202 MeterSection* new_meter = new MeterSection (pulse, minute_at_frame (frame), beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls, _frame_rate);
1204 bool solved = false;
1206 do_insert (new_meter);
1210 if (pls == AudioTime) {
1211 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (frame));
1212 /* we failed, most likely due to some impossible frame requirement wrt audio-locked tempi.
1213 fudge frame so that the meter ends up at its BBT position instead.
1216 solved = solve_map_minute (_metrics, new_meter, minute_at_frame (prev_m.frame() + 1));
1219 solved = solve_map_bbt (_metrics, new_meter, where);
1220 /* required due to resetting the pulse of meter-locked tempi above.
1221 Arguably solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
1222 but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
1224 recompute_map (_metrics);
1228 if (!solved && recompute) {
1229 /* if this has failed to solve, there is little we can do other than to ensure that
1230 the new map is recalculated.
1232 warning << "Adding meter may have left the tempo map unsolved." << endmsg;
1233 recompute_map (_metrics);
1240 TempoMap::change_initial_tempo (double note_types_per_minute, double note_type)
1242 Tempo newtempo (note_types_per_minute, note_type);
1245 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1246 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1251 Glib::Threads::RWLock::WriterLock lm (lock);
1252 *((Tempo*) t) = newtempo;
1253 recompute_map (_metrics);
1255 PropertyChanged (PropertyChange ());
1262 TempoMap::change_existing_tempo_at (framepos_t where, double note_types_per_minute, double note_type)
1264 Tempo newtempo (note_types_per_minute, note_type);
1267 TempoSection* first;
1268 Metrics::iterator i;
1270 /* find the TempoSection immediately preceding "where"
1273 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1275 if ((*i)->frame() > where) {
1281 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1294 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1304 Glib::Threads::RWLock::WriterLock lm (lock);
1305 /* cannot move the first tempo section */
1306 *((Tempo*)prev) = newtempo;
1307 recompute_map (_metrics);
1310 PropertyChanged (PropertyChange ());
1314 TempoMap::first_meter () const
1316 const MeterSection *m = 0;
1318 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1319 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1324 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1325 abort(); /*NOTREACHED*/
1330 TempoMap::first_meter ()
1332 MeterSection *m = 0;
1334 /* CALLER MUST HOLD LOCK */
1336 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1337 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1342 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1343 abort(); /*NOTREACHED*/
1348 TempoMap::first_tempo () const
1350 const TempoSection *t = 0;
1352 /* CALLER MUST HOLD LOCK */
1354 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1355 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1365 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1366 abort(); /*NOTREACHED*/
1371 TempoMap::first_tempo ()
1373 TempoSection *t = 0;
1375 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1376 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1386 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1387 abort(); /*NOTREACHED*/
1391 TempoMap::recompute_tempi (Metrics& metrics)
1393 TempoSection* prev_t = 0;
1395 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1398 if ((*i)->is_tempo()) {
1399 t = static_cast<TempoSection*> (*i);
1411 if (t->position_lock_style() == AudioTime) {
1412 prev_t->set_c_func (prev_t->compute_c_func_minute (t->note_types_per_minute(), t->minute()));
1413 if (!t->locked_to_meter()) {
1414 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
1418 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->note_types_per_minute(), t->pulse()));
1419 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
1427 prev_t->set_c_func (0.0);
1430 /* tempos must be positioned correctly.
1431 the current approach is to use a meter's bbt time as its base position unit.
1432 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1433 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1436 TempoMap::recompute_meters (Metrics& metrics)
1438 MeterSection* meter = 0;
1439 MeterSection* prev_m = 0;
1441 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1442 if (!(*mi)->is_tempo()) {
1443 meter = static_cast<MeterSection*> (*mi);
1444 if (meter->position_lock_style() == AudioTime) {
1446 pair<double, BBT_Time> b_bbt;
1447 TempoSection* meter_locked_tempo = 0;
1448 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1450 if ((*ii)->is_tempo()) {
1451 t = static_cast<TempoSection*> (*ii);
1452 if ((t->locked_to_meter() || t->initial()) && t->frame() == meter->frame()) {
1453 meter_locked_tempo = t;
1460 double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1461 if (beats + prev_m->beat() != meter->beat()) {
1462 /* reordering caused a bbt change */
1464 beats = meter->beat() - prev_m->beat();
1465 b_bbt = make_pair (beats + prev_m->beat()
1466 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1467 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1469 } else if (!meter->initial()) {
1470 b_bbt = make_pair (meter->beat(), meter->bbt());
1471 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1474 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1476 if (meter_locked_tempo) {
1477 meter_locked_tempo->set_pulse (pulse);
1479 meter->set_beat (b_bbt);
1480 meter->set_pulse (pulse);
1485 pair<double, BBT_Time> b_bbt;
1487 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1488 if (beats + prev_m->beat() != meter->beat()) {
1489 /* reordering caused a bbt change */
1490 b_bbt = make_pair (beats + prev_m->beat()
1491 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1493 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1495 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1497 /* shouldn't happen - the first is audio-locked */
1498 pulse = pulse_at_beat_locked (metrics, meter->beat());
1499 b_bbt = make_pair (meter->beat(), meter->bbt());
1502 meter->set_beat (b_bbt);
1503 meter->set_pulse (pulse);
1504 meter->set_minute (minute_at_pulse_locked (metrics, pulse));
1513 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1515 /* CALLER MUST HOLD WRITE LOCK */
1517 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1520 /* silly call from Session::process() during startup
1525 recompute_tempi (metrics);
1526 recompute_meters (metrics);
1530 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1532 Glib::Threads::RWLock::ReaderLock lm (lock);
1533 TempoMetric m (first_meter(), first_tempo());
1535 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1536 at something, because we insert the default tempo and meter during
1537 TempoMap construction.
1539 now see if we can find better candidates.
1542 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1544 if ((*i)->frame() > frame) {
1558 /* XX meters only */
1560 TempoMap::metric_at (BBT_Time bbt) const
1562 Glib::Threads::RWLock::ReaderLock lm (lock);
1563 TempoMetric m (first_meter(), first_tempo());
1565 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1566 at something, because we insert the default tempo and meter during
1567 TempoMap construction.
1569 now see if we can find better candidates.
1572 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1574 if (!(*i)->is_tempo()) {
1575 mw = static_cast<MeterSection*> (*i);
1576 BBT_Time section_start (mw->bbt());
1578 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1589 /** Returns the BBT (meter-based) beat corresponding to the supplied frame, possibly returning a negative value.
1590 * @param frame The session frame position.
1591 * @return The beat duration according to the tempo map at the supplied frame.
1593 * If the supplied frame lies before the first meter, the returned beat duration will be negative.
1594 * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
1596 * This function uses both tempo and meter.
1599 TempoMap::beat_at_frame (const framecnt_t& frame) const
1601 Glib::Threads::RWLock::ReaderLock lm (lock);
1603 return beat_at_minute_locked (_metrics, minute_at_frame (frame));
1606 /* This function uses both tempo and meter.*/
1608 TempoMap::beat_at_minute_locked (const Metrics& metrics, const double& minute) const
1610 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
1611 MeterSection* prev_m = 0;
1612 MeterSection* next_m = 0;
1614 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1615 if (!(*i)->is_tempo()) {
1616 if (prev_m && (*i)->minute() > minute) {
1617 next_m = static_cast<MeterSection*> (*i);
1620 prev_m = static_cast<MeterSection*> (*i);
1624 const double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
1626 /* audio locked meters fake their beat */
1627 if (next_m && next_m->beat() < beat) {
1628 return next_m->beat();
1634 /** Returns the frame corresponding to the supplied BBT (meter-based) beat.
1635 * @param beat The BBT (meter-based) beat.
1636 * @return The frame duration according to the tempo map at the supplied BBT (meter-based) beat.
1638 * This function uses both tempo and meter.
1641 TempoMap::frame_at_beat (const double& beat) const
1643 Glib::Threads::RWLock::ReaderLock lm (lock);
1645 return frame_at_minute (minute_at_beat_locked (_metrics, beat));
1648 /* meter & tempo section based */
1650 TempoMap::minute_at_beat_locked (const Metrics& metrics, const double& beat) const
1652 MeterSection* prev_m = 0;
1653 TempoSection* prev_t = 0;
1657 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1658 if (!(*i)->is_tempo()) {
1659 m = static_cast<MeterSection*> (*i);
1660 if (prev_m && m->beat() > beat) {
1670 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1671 if ((*i)->is_tempo()) {
1672 t = static_cast<TempoSection*> (*i);
1673 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1682 return prev_t->minute_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse());
1685 /** Returns a Tempo corresponding to the supplied frame position.
1686 * @param frame The audio frame.
1687 * @return a Tempo according to the tempo map at the supplied frame.
1691 TempoMap::tempo_at_frame (const framepos_t& frame) const
1693 Glib::Threads::RWLock::ReaderLock lm (lock);
1695 return tempo_at_minute_locked (_metrics, minute_at_frame (frame));
1699 TempoMap::tempo_at_minute_locked (const Metrics& metrics, const double& minute) const
1701 TempoSection* prev_t = 0;
1705 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1706 if ((*i)->is_tempo()) {
1707 t = static_cast<TempoSection*> (*i);
1711 if ((prev_t) && t->minute() > minute) {
1712 /* t is the section past frame */
1713 return prev_t->tempo_at_minute (minute);
1719 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type());
1722 /** returns the frame at which the supplied tempo occurs, or
1723 * the frame of the last tempo section (search exhausted)
1724 * only the position of the first occurence will be returned
1728 TempoMap::frame_at_tempo (const Tempo& tempo) const
1730 Glib::Threads::RWLock::ReaderLock lm (lock);
1732 return frame_at_minute (minute_at_tempo_locked (_metrics, tempo));
1736 TempoMap::minute_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1738 TempoSection* prev_t = 0;
1739 const double tempo_bpm = tempo.note_types_per_minute();
1741 Metrics::const_iterator i;
1743 for (i = metrics.begin(); i != metrics.end(); ++i) {
1745 if ((*i)->is_tempo()) {
1746 t = static_cast<TempoSection*> (*i);
1752 const double t_bpm = t->note_types_per_minute();
1754 if (t_bpm == tempo_bpm) {
1759 const double prev_t_bpm = prev_t->note_types_per_minute();
1761 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1762 return prev_t->minute_at_ntpm (prev_t->note_types_per_minute(), prev_t->pulse());
1769 return prev_t->minute();
1773 TempoMap::tempo_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1775 TempoSection* prev_t = 0;
1779 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1780 if ((*i)->is_tempo()) {
1781 t = static_cast<TempoSection*> (*i);
1785 if ((prev_t) && t->pulse() > pulse) {
1786 /* t is the section past frame */
1787 return prev_t->tempo_at_pulse (pulse);
1793 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type());
1797 TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1799 TempoSection* prev_t = 0;
1800 const double tempo_bpm = tempo.note_types_per_minute();
1802 Metrics::const_iterator i;
1804 for (i = metrics.begin(); i != metrics.end(); ++i) {
1806 if ((*i)->is_tempo()) {
1807 t = static_cast<TempoSection*> (*i);
1813 const double t_bpm = t->note_types_per_minute();
1815 if (t_bpm == tempo_bpm) {
1820 const double prev_t_bpm = prev_t->note_types_per_minute();
1822 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1823 return prev_t->pulse_at_ntpm (prev_t->note_types_per_minute(), prev_t->minute());
1830 return prev_t->pulse();
1833 /** Returns a Tempo corresponding to the supplied position in quarter-note beats.
1834 * @param qn the position in quarter note beats.
1835 * @return the Tempo at the supplied quarter-note.
1838 TempoMap::tempo_at_quarter_note (const double& qn) const
1840 Glib::Threads::RWLock::ReaderLock lm (lock);
1842 return tempo_at_pulse_locked (_metrics, qn / 4.0);
1845 /** Returns the position in quarter-note beats corresponding to the supplied Tempo.
1846 * @param tempo the tempo.
1847 * @return the position in quarter-note beats where the map bpm
1848 * is equal to that of the Tempo. currently ignores note_type.
1851 TempoMap::quarter_note_at_tempo (const Tempo& tempo) const
1853 Glib::Threads::RWLock::ReaderLock lm (lock);
1855 return pulse_at_tempo_locked (_metrics, tempo) * 4.0;;
1858 /** Returns the whole-note pulse corresponding to the supplied BBT (meter-based) beat.
1859 * @param metrics the list of metric sections used to calculate the pulse.
1860 * @param beat The BBT (meter-based) beat.
1861 * @return the whole-note pulse at the supplied BBT (meter-based) beat.
1863 * a pulse or whole note is the base musical position of a MetricSection.
1864 * it is equivalent to four quarter notes.
1868 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1870 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1872 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1875 /** Returns the BBT (meter-based) beat corresponding to the supplied whole-note pulse .
1876 * @param metrics the list of metric sections used to calculate the beat.
1877 * @param pulse the whole-note pulse.
1878 * @return the meter-based beat at the supplied whole-note pulse.
1880 * a pulse or whole note is the base musical position of a MetricSection.
1881 * it is equivalent to four quarter notes.
1884 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1886 MeterSection* prev_m = 0;
1888 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1890 if (!(*i)->is_tempo()) {
1891 m = static_cast<MeterSection*> (*i);
1892 if (prev_m && m->pulse() > pulse) {
1900 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1904 /* tempo section based */
1906 TempoMap::pulse_at_minute_locked (const Metrics& metrics, const double& minute) const
1908 /* HOLD (at least) THE READER LOCK */
1909 TempoSection* prev_t = 0;
1911 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1913 if ((*i)->is_tempo()) {
1914 t = static_cast<TempoSection*> (*i);
1918 if (prev_t && t->minute() > minute) {
1919 /*the previous ts is the one containing the frame */
1920 const double ret = prev_t->pulse_at_minute (minute);
1921 /* audio locked section in new meter*/
1922 if (t->pulse() < ret) {
1931 /* treated as constant for this ts */
1932 const double pulses_in_section = ((minute - prev_t->minute()) * prev_t->note_types_per_minute()) / prev_t->note_type();
1934 return pulses_in_section + prev_t->pulse();
1937 /* tempo section based */
1939 TempoMap::minute_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1941 /* HOLD THE READER LOCK */
1943 const TempoSection* prev_t = 0;
1945 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1948 if ((*i)->is_tempo()) {
1949 t = static_cast<TempoSection*> (*i);
1953 if (prev_t && t->pulse() > pulse) {
1954 return prev_t->minute_at_pulse (pulse);
1960 /* must be treated as constant, irrespective of _type */
1961 double const dtime = ((pulse - prev_t->pulse()) * prev_t->note_type()) / prev_t->note_types_per_minute();
1963 return dtime + prev_t->minute();
1966 /** Returns the BBT (meter-based) beat corresponding to the supplied BBT time.
1967 * @param bbt The BBT time (meter-based).
1968 * @return bbt The BBT beat (meter-based) at the supplied BBT time.
1972 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
1974 Glib::Threads::RWLock::ReaderLock lm (lock);
1975 return beat_at_bbt_locked (_metrics, bbt);
1980 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1982 /* CALLER HOLDS READ LOCK */
1984 MeterSection* prev_m = 0;
1986 /* because audio-locked meters have 'fake' integral beats,
1987 there is no pulse offset here.
1991 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1992 if (!(*i)->is_tempo()) {
1993 m = static_cast<MeterSection*> (*i);
1995 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1996 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
2004 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2005 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
2006 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
2011 /** Returns the BBT time corresponding to the supplied BBT (meter-based) beat.
2012 * @param beat The BBT (meter-based) beat.
2013 * @return The BBT time (meter-based) at the supplied meter-based beat.
2017 TempoMap::bbt_at_beat (const double& beat)
2019 Glib::Threads::RWLock::ReaderLock lm (lock);
2020 return bbt_at_beat_locked (_metrics, beat);
2024 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
2026 /* CALLER HOLDS READ LOCK */
2027 MeterSection* prev_m = 0;
2028 const double beats = max (0.0, b);
2030 MeterSection* m = 0;
2032 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2033 if (!(*i)->is_tempo()) {
2034 m = static_cast<MeterSection*> (*i);
2036 if (m->beat() > beats) {
2037 /* this is the meter after the one our beat is on*/
2047 const double beats_in_ms = beats - prev_m->beat();
2048 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2049 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2050 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2051 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2055 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2056 ret.beats = (uint32_t) floor (remaining_beats);
2057 ret.bars = total_bars;
2059 /* 0 0 0 to 1 1 0 - based mapping*/
2063 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2065 ret.ticks -= BBT_Time::ticks_per_beat;
2068 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2076 /** Returns the quarter-note beat corresponding to the supplied BBT time (meter-based).
2077 * @param bbt The BBT time (meter-based).
2078 * @return the quarter note beat at the supplied BBT time
2080 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2082 * while the input uses meter, the output does not.
2085 TempoMap::quarter_note_at_bbt (const Timecode::BBT_Time& bbt)
2087 Glib::Threads::RWLock::ReaderLock lm (lock);
2089 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2093 TempoMap::quarter_note_at_bbt_rt (const Timecode::BBT_Time& bbt)
2095 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2098 throw std::logic_error ("TempoMap::quarter_note_at_bbt_rt() could not lock tempo map");
2101 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2105 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2107 /* CALLER HOLDS READ LOCK */
2109 MeterSection* prev_m = 0;
2111 /* because audio-locked meters have 'fake' integral beats,
2112 there is no pulse offset here.
2116 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2117 if (!(*i)->is_tempo()) {
2118 m = static_cast<MeterSection*> (*i);
2120 if (m->bbt().bars > bbt.bars) {
2128 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2129 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
2130 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
2135 /** Returns the BBT time corresponding to the supplied quarter-note beat.
2136 * @param qn the quarter-note beat.
2137 * @return The BBT time (meter-based) at the supplied meter-based beat.
2139 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2143 TempoMap::bbt_at_quarter_note (const double& qn)
2145 Glib::Threads::RWLock::ReaderLock lm (lock);
2147 return bbt_at_pulse_locked (_metrics, qn / 4.0);
2150 /** Returns the BBT time (meter-based) corresponding to the supplied whole-note pulse position.
2151 * @param metrics The list of metric sections used to determine the result.
2152 * @param pulse The whole-note pulse.
2153 * @return The BBT time at the supplied whole-note pulse.
2155 * a pulse or whole note is the basic musical position of a MetricSection.
2156 * it is equivalent to four quarter notes.
2157 * while the output uses meter, the input does not.
2160 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2162 MeterSection* prev_m = 0;
2164 MeterSection* m = 0;
2166 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2168 if (!(*i)->is_tempo()) {
2169 m = static_cast<MeterSection*> (*i);
2172 double const pulses_to_m = m->pulse() - prev_m->pulse();
2173 if (prev_m->pulse() + pulses_to_m > pulse) {
2174 /* this is the meter after the one our beat is on*/
2185 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
2186 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2187 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2188 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2189 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2193 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2194 ret.beats = (uint32_t) floor (remaining_beats);
2195 ret.bars = total_bars;
2197 /* 0 0 0 to 1 1 0 mapping*/
2201 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2203 ret.ticks -= BBT_Time::ticks_per_beat;
2206 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2214 /** Returns the BBT time corresponding to the supplied frame position.
2215 * @param frame the position in audio samples.
2216 * @return the BBT time at the frame position .
2220 TempoMap::bbt_at_frame (framepos_t frame)
2227 warning << string_compose (_("tempo map was asked for BBT time at frame %1\n"), frame) << endmsg;
2230 Glib::Threads::RWLock::ReaderLock lm (lock);
2232 return bbt_at_minute_locked (_metrics, minute_at_frame (frame));
2236 TempoMap::bbt_at_frame_rt (framepos_t frame)
2238 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2241 throw std::logic_error ("TempoMap::bbt_at_frame_rt() could not lock tempo map");
2244 return bbt_at_minute_locked (_metrics, minute_at_frame (frame));
2248 TempoMap::bbt_at_minute_locked (const Metrics& metrics, const double& minute) const
2258 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
2259 MeterSection* prev_m = 0;
2260 MeterSection* next_m = 0;
2264 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2265 if (!(*i)->is_tempo()) {
2266 m = static_cast<MeterSection*> (*i);
2267 if (prev_m && m->minute() > minute) {
2275 double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
2277 /* handle frame before first meter */
2278 if (minute < prev_m->minute()) {
2281 /* audio locked meters fake their beat */
2282 if (next_m && next_m->beat() < beat) {
2283 beat = next_m->beat();
2286 beat = max (0.0, beat);
2288 const double beats_in_ms = beat - prev_m->beat();
2289 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2290 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2291 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2292 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2296 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2297 ret.beats = (uint32_t) floor (remaining_beats);
2298 ret.bars = total_bars;
2300 /* 0 0 0 to 1 1 0 - based mapping*/
2304 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2306 ret.ticks -= BBT_Time::ticks_per_beat;
2309 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2317 /** Returns the frame position corresponding to the supplied BBT time.
2318 * @param bbt the position in BBT time.
2319 * @return the frame position at bbt.
2323 TempoMap::frame_at_bbt (const BBT_Time& bbt)
2326 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
2330 if (bbt.beats < 1) {
2331 throw std::logic_error ("beats are counted from one");
2333 Glib::Threads::RWLock::ReaderLock lm (lock);
2335 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
2338 /* meter & tempo section based */
2340 TempoMap::minute_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2342 /* HOLD THE READER LOCK */
2344 const double ret = minute_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2349 * Returns the quarter-note beat position corresponding to the supplied frame.
2351 * @param frame the position in frames.
2352 * @return The quarter-note position of the supplied frame. Ignores meter.
2356 TempoMap::quarter_note_at_frame (const framepos_t frame) const
2358 Glib::Threads::RWLock::ReaderLock lm (lock);
2360 const double ret = quarter_note_at_minute_locked (_metrics, minute_at_frame (frame));
2366 TempoMap::quarter_note_at_minute_locked (const Metrics& metrics, const double minute) const
2368 const double ret = pulse_at_minute_locked (metrics, minute) * 4.0;
2374 TempoMap::quarter_note_at_frame_rt (const framepos_t frame) const
2376 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2379 throw std::logic_error ("TempoMap::quarter_note_at_frame_rt() could not lock tempo map");
2382 const double ret = pulse_at_minute_locked (_metrics, minute_at_frame (frame)) * 4.0;
2388 * Returns the frame position corresponding to the supplied quarter-note beat.
2390 * @param quarter_note the quarter-note position.
2391 * @return the frame position of the supplied quarter-note. Ignores meter.
2396 TempoMap::frame_at_quarter_note (const double quarter_note) const
2398 Glib::Threads::RWLock::ReaderLock lm (lock);
2400 const framepos_t ret = frame_at_minute (minute_at_quarter_note_locked (_metrics, quarter_note));
2406 TempoMap::minute_at_quarter_note_locked (const Metrics& metrics, const double quarter_note) const
2408 const double ret = minute_at_pulse_locked (metrics, quarter_note / 4.0);
2413 /** Returns the quarter-note beats corresponding to the supplied BBT (meter-based) beat.
2414 * @param beat The BBT (meter-based) beat.
2415 * @return The quarter-note position of the supplied BBT (meter-based) beat.
2417 * a quarter-note may be compared with and assigned to Evoral::Beats.
2421 TempoMap::quarter_note_at_beat (const double beat) const
2423 Glib::Threads::RWLock::ReaderLock lm (lock);
2425 const double ret = quarter_note_at_beat_locked (_metrics, beat);
2431 TempoMap::quarter_note_at_beat_locked (const Metrics& metrics, const double beat) const
2433 const double ret = pulse_at_beat_locked (metrics, beat) * 4.0;
2438 /** Returns the BBT (meter-based) beat position corresponding to the supplied quarter-note beats.
2439 * @param quarter_note The position in quarter-note beats.
2440 * @return the BBT (meter-based) beat position of the supplied quarter-note beats.
2442 * a quarter-note is the musical unit of Evoral::Beats.
2446 TempoMap::beat_at_quarter_note (const double quarter_note) const
2448 Glib::Threads::RWLock::ReaderLock lm (lock);
2450 const double ret = beat_at_quarter_note_locked (_metrics, quarter_note);
2456 TempoMap::beat_at_quarter_note_locked (const Metrics& metrics, const double quarter_note) const
2459 return beat_at_pulse_locked (metrics, quarter_note / 4.0);
2462 /** Returns the duration in frames between two supplied quarter-note beat positions.
2463 * @param start the first position in quarter-note beats.
2464 * @param end the end position in quarter-note beats.
2465 * @return the frame distance ober the quarter-note beats duration.
2467 * use this rather than e.g.
2468 * frame_at-quarter_note (end_beats) - frame_at_quarter_note (start_beats).
2469 * frames_between_quarter_notes() doesn't round to audio frames as an intermediate step,
2473 TempoMap::frames_between_quarter_notes (const double start, const double end) const
2475 Glib::Threads::RWLock::ReaderLock lm (lock);
2477 return frame_at_minute (minutes_between_quarter_notes_locked (_metrics, start, end));
2481 TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end) const
2484 return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
2488 TempoMap::quarter_notes_between_frames (const framecnt_t start, const framecnt_t end) const
2490 Glib::Threads::RWLock::ReaderLock lm (lock);
2492 return quarter_notes_between_frames_locked (_metrics, start, end);
2496 TempoMap::quarter_notes_between_frames_locked (const Metrics& metrics, const framecnt_t start, const framecnt_t end) const
2498 const TempoSection* prev_t = 0;
2500 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2503 if ((*i)->is_tempo()) {
2504 t = static_cast<TempoSection*> (*i);
2508 if (prev_t && t->frame() > start) {
2515 const double start_qn = prev_t->pulse_at_frame (start);
2517 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2520 if ((*i)->is_tempo()) {
2521 t = static_cast<TempoSection*> (*i);
2525 if (prev_t && t->frame() > end) {
2531 const double end_qn = prev_t->pulse_at_frame (end);
2533 return (end_qn - start_qn) * 4.0;
2537 TempoMap::check_solved (const Metrics& metrics) const
2539 TempoSection* prev_t = 0;
2540 MeterSection* prev_m = 0;
2542 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2545 if ((*i)->is_tempo()) {
2546 t = static_cast<TempoSection*> (*i);
2551 /* check ordering */
2552 if ((t->minute() <= prev_t->minute()) || (t->pulse() <= prev_t->pulse())) {
2556 /* precision check ensures tempo and frames align.*/
2557 if (t->frame() != frame_at_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()))) {
2558 if (!t->locked_to_meter()) {
2563 /* gradient limit - who knows what it should be?
2564 things are also ok (if a little chaotic) without this
2566 if (fabs (prev_t->c_func()) > 1000.0) {
2567 //std::cout << "c : " << prev_t->c_func() << std::endl;
2574 if (!(*i)->is_tempo()) {
2575 m = static_cast<MeterSection*> (*i);
2576 if (prev_m && m->position_lock_style() == AudioTime) {
2577 const TempoSection* t = &tempo_section_at_minute_locked (metrics, minute_at_frame (m->frame() - 1));
2578 const framepos_t nascent_m_frame = frame_at_minute (t->minute_at_pulse (m->pulse()));
2579 /* Here we check that a preceding section of music doesn't overlap a subsequent one.
2581 if (t && (nascent_m_frame > m->frame() || nascent_m_frame < 0)) {
2595 TempoMap::set_active_tempi (const Metrics& metrics, const framepos_t& frame)
2597 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2599 if ((*i)->is_tempo()) {
2600 t = static_cast<TempoSection*> (*i);
2602 t->set_active (true);
2604 } else if (t->position_lock_style() == AudioTime) {
2605 if (t->active () && t->frame() < frame) {
2606 t->set_active (false);
2608 } else if (t->frame() > frame) {
2609 t->set_active (true);
2610 } else if (t->frame() == frame) {
2620 TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const double& minute)
2622 TempoSection* prev_t = 0;
2623 TempoSection* section_prev = 0;
2624 double first_m_minute = 0.0;
2625 const bool sml = section->locked_to_meter();
2627 /* can't move a tempo before the first meter */
2628 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2630 if (!(*i)->is_tempo()) {
2631 m = static_cast<MeterSection*> (*i);
2633 first_m_minute = m->minute();
2638 if (!section->initial() && minute <= first_m_minute) {
2642 section->set_active (true);
2643 section->set_minute (minute);
2645 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2647 if ((*i)->is_tempo()) {
2648 t = static_cast<TempoSection*> (*i);
2660 if (t->frame() == frame_at_minute (minute)) {
2664 const bool tlm = t->position_lock_style() == MusicTime;
2666 if (prev_t && !section_prev && ((sml && tlm && t->pulse() > section->pulse()) || (!tlm && t->minute() > minute))) {
2667 section_prev = prev_t;
2669 section_prev->set_c_func (section_prev->compute_c_func_minute (section->note_types_per_minute(), minute));
2670 if (!section->locked_to_meter()) {
2671 section->set_pulse (section_prev->pulse_at_ntpm (section->note_types_per_minute(), minute));
2676 if (t->position_lock_style() == MusicTime) {
2677 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->note_types_per_minute(), t->pulse()));
2678 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
2680 prev_t->set_c_func (prev_t->compute_c_func_minute (t->note_types_per_minute(), t->minute()));
2681 if (!t->locked_to_meter()) {
2682 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
2691 recompute_tempi (imaginary);
2693 if (check_solved (imaginary)) {
2696 dunp (imaginary, std::cout);
2700 MetricSectionFrameSorter fcmp;
2701 imaginary.sort (fcmp);
2703 recompute_tempi (imaginary);
2705 if (check_solved (imaginary)) {
2713 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2715 TempoSection* prev_t = 0;
2716 TempoSection* section_prev = 0;
2718 section->set_pulse (pulse);
2720 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2722 if ((*i)->is_tempo()) {
2723 t = static_cast<TempoSection*> (*i);
2734 section_prev = prev_t;
2737 if (t->position_lock_style() == MusicTime) {
2738 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->note_types_per_minute(), t->pulse()));
2739 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
2741 prev_t->set_c_func (prev_t->compute_c_func_minute (t->note_types_per_minute(), t->minute()));
2742 if (!t->locked_to_meter()) {
2743 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
2752 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->note_types_per_minute(), pulse));
2753 section->set_minute (section_prev->minute_at_ntpm (section->note_types_per_minute(), pulse));
2757 recompute_tempi (imaginary);
2759 if (check_solved (imaginary)) {
2762 dunp (imaginary, std::cout);
2766 MetricSectionSorter cmp;
2767 imaginary.sort (cmp);
2769 recompute_tempi (imaginary);
2771 * XX need a restriction here, but only for this case,
2772 * as audio locked tempos don't interact in the same way.
2774 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2776 * |50 bpm |250 bpm |60 bpm
2777 * drag 250 to the pulse after 60->
2778 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2780 if (check_solved (imaginary)) {
2788 TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const double& minute)
2790 /* disallow moving first meter past any subsequent one, and any initial meter before the first one */
2791 const MeterSection* other = &meter_section_at_minute_locked (imaginary, minute);
2792 if ((section->initial() && !other->initial()) || (other->initial() && !section->initial() && other->minute() >= minute)) {
2796 if (section->initial()) {
2797 /* lock the first tempo to our first meter */
2798 if (!set_active_tempi (imaginary, section->frame_at_minute (minute))) {
2803 TempoSection* meter_locked_tempo = 0;
2805 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2807 if ((*ii)->is_tempo()) {
2808 t = static_cast<TempoSection*> (*ii);
2809 if ((t->locked_to_meter() || t->initial()) && t->frame() == section->frame()) {
2810 meter_locked_tempo = t;
2816 if (!meter_locked_tempo) {
2820 MeterSection* prev_m = 0;
2822 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2823 bool solved = false;
2825 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2827 if (!(*i)->is_tempo()) {
2828 m = static_cast<MeterSection*> (*i);
2830 if (prev_m && !section->initial()) {
2831 const double beats = (pulse_at_minute_locked (imaginary, minute) - prev_m->pulse()) * prev_m->note_divisor();
2832 if (beats + prev_m->beat() < section->beat()) {
2833 /* set the section pulse according to its musical position,
2834 * as an earlier time than this has been requested.
2836 const double new_pulse = ((section->beat() - prev_m->beat())
2837 / prev_m->note_divisor()) + prev_m->pulse();
2839 tempo_copy->set_position_lock_style (MusicTime);
2840 if ((solved = solve_map_pulse (future_map, tempo_copy, new_pulse))) {
2841 meter_locked_tempo->set_position_lock_style (MusicTime);
2842 section->set_position_lock_style (MusicTime);
2843 section->set_pulse (new_pulse);
2844 solve_map_pulse (imaginary, meter_locked_tempo, new_pulse);
2845 meter_locked_tempo->set_position_lock_style (AudioTime);
2846 section->set_position_lock_style (AudioTime);
2847 section->set_minute (meter_locked_tempo->minute());
2853 Metrics::const_iterator d = future_map.begin();
2854 while (d != future_map.end()) {
2863 /* all is ok. set section's locked tempo if allowed.
2864 possibly disallow if there is an adjacent audio-locked tempo.
2865 XX this check could possibly go. its never actually happened here.
2867 MeterSection* meter_copy = const_cast<MeterSection*>
2868 (&meter_section_at_minute_locked (future_map, section->minute()));
2870 meter_copy->set_minute (minute);
2872 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2873 section->set_minute (minute);
2874 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2875 / prev_m->note_divisor()) + prev_m->pulse());
2876 solve_map_minute (imaginary, meter_locked_tempo, minute);
2881 Metrics::const_iterator d = future_map.begin();
2882 while (d != future_map.end()) {
2892 /* initial (first meter atm) */
2894 tempo_copy->set_minute (minute);
2895 tempo_copy->set_pulse (0.0);
2897 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2898 section->set_minute (minute);
2899 meter_locked_tempo->set_minute (minute);
2900 meter_locked_tempo->set_pulse (0.0);
2901 solve_map_minute (imaginary, meter_locked_tempo, minute);
2906 Metrics::const_iterator d = future_map.begin();
2907 while (d != future_map.end()) {
2916 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2917 section->set_beat (b_bbt);
2918 section->set_pulse (0.0);
2928 MetricSectionFrameSorter fcmp;
2929 imaginary.sort (fcmp);
2931 recompute_meters (imaginary);
2937 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2939 /* disallow setting section to an existing meter's bbt */
2940 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2942 if (!(*i)->is_tempo()) {
2943 m = static_cast<MeterSection*> (*i);
2944 if (m != section && m->bbt().bars == when.bars) {
2950 MeterSection* prev_m = 0;
2951 MeterSection* section_prev = 0;
2953 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2955 if (!(*i)->is_tempo()) {
2956 m = static_cast<MeterSection*> (*i);
2962 pair<double, BBT_Time> b_bbt;
2963 double new_pulse = 0.0;
2965 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2966 section_prev = prev_m;
2968 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2969 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2970 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2972 section->set_beat (b_bbt);
2973 section->set_pulse (pulse);
2974 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
2978 if (m->position_lock_style() == AudioTime) {
2979 TempoSection* meter_locked_tempo = 0;
2981 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2983 if ((*ii)->is_tempo()) {
2984 t = static_cast<TempoSection*> (*ii);
2985 if ((t->locked_to_meter() || t->initial()) && t->frame() == m->frame()) {
2986 meter_locked_tempo = t;
2992 if (!meter_locked_tempo) {
2997 double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2999 if (beats + prev_m->beat() != m->beat()) {
3000 /* tempo/ meter change caused a change in beat (bar). */
3002 /* the user has requested that the previous section of music overlaps this one.
3003 we have no choice but to change the bar number here, as being locked to audio means
3004 we must stay where we are on the timeline.
3006 beats = m->beat() - prev_m->beat();
3007 b_bbt = make_pair (beats + prev_m->beat()
3008 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3009 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3011 } else if (!m->initial()) {
3012 b_bbt = make_pair (m->beat(), m->bbt());
3013 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
3016 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3019 meter_locked_tempo->set_pulse (new_pulse);
3020 m->set_beat (b_bbt);
3021 m->set_pulse (new_pulse);
3025 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
3026 if (beats + prev_m->beat() != m->beat()) {
3027 /* tempo/ meter change caused a change in beat (bar). */
3028 b_bbt = make_pair (beats + prev_m->beat()
3029 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3031 b_bbt = make_pair (beats + prev_m->beat()
3034 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3035 m->set_beat (b_bbt);
3036 m->set_pulse (new_pulse);
3037 m->set_minute (minute_at_pulse_locked (imaginary, new_pulse));
3044 if (!section_prev) {
3046 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
3047 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3048 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
3050 section->set_beat (b_bbt);
3051 section->set_pulse (pulse);
3052 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
3055 MetricSectionSorter cmp;
3056 imaginary.sort (cmp);
3058 recompute_meters (imaginary);
3063 /** places a copy of _metrics into copy and returns a pointer
3064 * to section's equivalent in copy.
3067 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
3069 TempoSection* ret = 0;
3071 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3074 if ((*i)->is_tempo()) {
3075 t = static_cast<TempoSection*> (*i);
3077 ret = new TempoSection (*t);
3078 copy.push_back (ret);
3082 TempoSection* cp = new TempoSection (*t);
3083 copy.push_back (cp);
3085 if (!(*i)->is_tempo()) {
3086 m = static_cast<MeterSection *> (*i);
3087 MeterSection* cp = new MeterSection (*m);
3088 copy.push_back (cp);
3096 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
3098 MeterSection* ret = 0;
3100 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3103 if ((*i)->is_tempo()) {
3104 t = static_cast<TempoSection*> (*i);
3105 TempoSection* cp = new TempoSection (*t);
3106 copy.push_back (cp);
3109 if (!(*i)->is_tempo()) {
3110 m = static_cast<MeterSection *> (*i);
3112 ret = new MeterSection (*m);
3113 copy.push_back (ret);
3116 MeterSection* cp = new MeterSection (*m);
3117 copy.push_back (cp);
3124 /** answers the question "is this a valid beat position for this tempo section?".
3125 * it returns true if the tempo section can be moved to the requested bbt position,
3126 * leaving the tempo map in a solved state.
3127 * @param ts the tempo section to be moved
3128 * @param bbt the requested new position for the tempo section
3129 * @return true if the tempo section can be moved to the position, otherwise false.
3132 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
3135 TempoSection* tempo_copy = 0;
3138 Glib::Threads::RWLock::ReaderLock lm (lock);
3139 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
3145 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
3147 Metrics::const_iterator d = copy.begin();
3148 while (d != copy.end()) {
3157 * 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,
3158 * taking any possible reordering as a consequence of this into account.
3159 * @param section - the section to be altered
3160 * @param bbt - the BBT time where the altered tempo will fall
3161 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
3163 pair<double, framepos_t>
3164 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
3167 pair<double, framepos_t> ret = make_pair (0.0, 0);
3169 Glib::Threads::RWLock::ReaderLock lm (lock);
3171 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
3173 const double beat = beat_at_bbt_locked (future_map, bbt);
3175 if (section->position_lock_style() == AudioTime) {
3176 tempo_copy->set_position_lock_style (MusicTime);
3179 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
3180 ret.first = tempo_copy->pulse();
3181 ret.second = tempo_copy->frame();
3183 ret.first = section->pulse();
3184 ret.second = section->frame();
3187 Metrics::const_iterator d = future_map.begin();
3188 while (d != future_map.end()) {
3195 /** moves a TempoSection to a specified position.
3196 * @param ts - the section to be moved
3197 * @param frame - the new position in frames for the tempo
3198 * @param sub_num - the snap division to use if using musical time.
3200 * if sub_num is non-zero, the frame position is used to calculate an exact
3203 * -1 | snap to bars (meter-based)
3204 * 0 | no snap - use audio frame for musical position
3205 * 1 | snap to meter-based (BBT) beat
3206 * >1 | snap to quarter-note subdivision (i.e. 4 will snap to sixteenth notes)
3208 * this follows the snap convention in the gui.
3209 * if sub_num is zero, the musical position will be taken from the supplied frame.
3212 TempoMap::gui_set_tempo_position (TempoSection* ts, const framepos_t& frame, const int& sub_num)
3216 if (ts->position_lock_style() == MusicTime) {
3218 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
3219 Glib::Threads::RWLock::WriterLock lm (lock);
3220 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3222 tempo_copy->set_position_lock_style (AudioTime);
3224 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3225 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
3226 const double pulse = pulse_at_beat_locked (future_map, beat);
3228 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3229 solve_map_pulse (_metrics, ts, pulse);
3230 recompute_meters (_metrics);
3238 Glib::Threads::RWLock::WriterLock lm (lock);
3239 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3241 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3243 /* We're moving the object that defines the grid while snapping to it...
3244 * Placing the ts at the beat corresponding to the requested frame may shift the
3245 * grid in such a way that the mouse is left hovering over a completerly different division,
3246 * causing jittering when the mouse next moves (esp. large tempo deltas).
3248 * This alters the snap behaviour slightly in that we snap to beat divisions
3249 * in the future map rather than the existing one.
3251 const double qn = exact_qn_at_frame_locked (future_map, frame, sub_num);
3252 const framepos_t snapped_frame = frame_at_minute (minute_at_quarter_note_locked (future_map, qn));
3254 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (snapped_frame))) {
3255 solve_map_minute (_metrics, ts, minute_at_frame (snapped_frame));
3256 ts->set_pulse (qn / 4.0);
3257 recompute_meters (_metrics);
3260 solve_map_minute (_metrics, ts, minute_at_frame (frame));
3261 recompute_meters (_metrics);
3267 Metrics::const_iterator d = future_map.begin();
3268 while (d != future_map.end()) {
3273 MetricPositionChanged (); // Emit Signal
3276 /** moves a MeterSection to a specified position.
3277 * @param ms - the section to be moved
3278 * @param frame - the new position in frames for the meter
3280 * as a meter cannot snap to anything but bars,
3281 * the supplied frame is rounded to the nearest bar, possibly
3282 * leaving the meter position unchanged.
3285 TempoMap::gui_set_meter_position (MeterSection* ms, const framepos_t& frame)
3289 if (ms->position_lock_style() == AudioTime) {
3292 Glib::Threads::RWLock::WriterLock lm (lock);
3293 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3295 if (solve_map_minute (future_map, copy, minute_at_frame (frame))) {
3296 solve_map_minute (_metrics, ms, minute_at_frame (frame));
3297 recompute_tempi (_metrics);
3302 Glib::Threads::RWLock::WriterLock lm (lock);
3303 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3305 const double beat = beat_at_minute_locked (_metrics, minute_at_frame (frame));
3306 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
3308 if (solve_map_bbt (future_map, copy, bbt)) {
3309 solve_map_bbt (_metrics, ms, bbt);
3310 recompute_tempi (_metrics);
3315 Metrics::const_iterator d = future_map.begin();
3316 while (d != future_map.end()) {
3321 MetricPositionChanged (); // Emit Signal
3325 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
3328 bool can_solve = false;
3330 Glib::Threads::RWLock::WriterLock lm (lock);
3331 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3332 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3333 recompute_tempi (future_map);
3335 if (check_solved (future_map)) {
3336 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3337 recompute_map (_metrics);
3342 Metrics::const_iterator d = future_map.begin();
3343 while (d != future_map.end()) {
3348 MetricPositionChanged (); // Emit Signal
3354 TempoMap::gui_stretch_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame)
3357 Ts (future prev_t) Tnext
3360 |----------|----------
3367 Glib::Threads::RWLock::WriterLock lm (lock);
3373 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3374 TempoSection* prev_to_prev_t = 0;
3375 const frameoffset_t fr_off = end_frame - frame;
3379 if (prev_t->pulse() > 0.0) {
3380 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_minute_locked (future_map, minute_at_frame (prev_t->frame() - 1)));
3383 TempoSection* next_t = 0;
3384 for (Metrics::iterator i = future_map.begin(); i != future_map.end(); ++i) {
3385 TempoSection* t = 0;
3386 if ((*i)->is_tempo()) {
3387 t = static_cast<TempoSection*> (*i);
3388 if (t->frame() > ts->frame()) {
3394 /* minimum allowed measurement distance in frames */
3395 const framepos_t min_dframe = 2;
3397 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
3398 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
3400 double contribution = 0.0;
3402 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3403 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3406 const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
3408 const double start_pulse = prev_t->pulse_at_minute (minute_at_frame (frame));
3409 const double end_pulse = prev_t->pulse_at_minute (minute_at_frame (end_frame));
3413 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
3415 if (prev_t->position_lock_style() == MusicTime) {
3416 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3417 if (frame > prev_to_prev_t->frame() + min_dframe && (frame + prev_t_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
3419 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame())
3420 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
3422 new_bpm = prev_t->note_types_per_minute();
3425 /* prev to prev is irrelevant */
3427 if (start_pulse > prev_t->pulse() && end_pulse > prev_t->pulse()) {
3428 new_bpm = prev_t->note_types_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
3430 new_bpm = prev_t->note_types_per_minute();
3435 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3436 if (frame > prev_to_prev_t->frame() + min_dframe && end_frame > prev_to_prev_t->frame() + min_dframe) {
3438 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame())
3439 / (double) ((end_frame) - prev_to_prev_t->frame()));
3441 new_bpm = prev_t->note_types_per_minute();
3444 /* prev_to_prev_t is irrelevant */
3446 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3447 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
3449 new_bpm = prev_t->note_types_per_minute();
3455 double frame_ratio = 1.0;
3456 double pulse_ratio = 1.0;
3457 const double pulse_pos = frame;
3459 if (prev_to_prev_t) {
3460 if (pulse_pos > prev_to_prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_to_prev_t->frame() + min_dframe) {
3461 frame_ratio = (((pulse_pos - fr_off) - prev_to_prev_t->frame()) / (double) ((pulse_pos) - prev_to_prev_t->frame()));
3463 if (end_pulse > prev_to_prev_t->pulse() && start_pulse > prev_to_prev_t->pulse()) {
3464 pulse_ratio = ((start_pulse - prev_to_prev_t->pulse()) / (end_pulse - prev_to_prev_t->pulse()));
3467 if (pulse_pos > prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_t->frame() + min_dframe) {
3468 frame_ratio = (((pulse_pos - fr_off) - prev_t->frame()) / (double) ((pulse_pos) - prev_t->frame()));
3470 pulse_ratio = (start_pulse / end_pulse);
3472 new_bpm = prev_t->note_types_per_minute() * (pulse_ratio * frame_ratio);
3475 /* don't clamp and proceed here.
3476 testing has revealed that this can go negative,
3477 which is an entirely different thing to just being too low.
3479 if (new_bpm < 0.5) {
3482 new_bpm = min (new_bpm, (double) 1000.0);
3483 prev_t->set_note_types_per_minute (new_bpm);
3484 recompute_tempi (future_map);
3485 recompute_meters (future_map);
3487 if (check_solved (future_map)) {
3488 ts->set_note_types_per_minute (new_bpm);
3489 recompute_tempi (_metrics);
3490 recompute_meters (_metrics);
3494 Metrics::const_iterator d = future_map.begin();
3495 while (d != future_map.end()) {
3500 MetricPositionChanged (); // Emit Signal
3503 /** Returns the exact bbt-based beat corresponding to the bar, beat or quarter note subdivision nearest to
3504 * the supplied frame, possibly returning a negative value.
3506 * @param frame The session frame position.
3507 * @param sub_num The subdivision to use when rounding the beat.
3508 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3509 * Positive integers indicate quarter note (non BBT) divisions.
3510 * 0 indicates that the returned beat should not be rounded (equivalent to quarter_note_at_frame()).
3511 * @return The beat position of the supplied frame.
3513 * when working to a musical grid, the use of sub_nom indicates that
3514 * the position should be interpreted musically.
3516 * it effectively snaps to meter bars, meter beats or quarter note divisions
3517 * (as per current gui convention) and returns a musical position independent of frame rate.
3519 * If the supplied frame lies before the first meter, the return will be negative,
3520 * in which case the returned beat uses the first meter (for BBT subdivisions) and
3521 * the continuation of the tempo curve (backwards).
3523 * This function is sensitive to tempo and meter.
3526 TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num) const
3528 Glib::Threads::RWLock::ReaderLock lm (lock);
3530 return exact_beat_at_frame_locked (_metrics, frame, sub_num);
3534 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t divisions) const
3536 return beat_at_pulse_locked (_metrics, exact_qn_at_frame_locked (metrics, frame, divisions) / 4.0);
3539 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
3540 * the supplied frame, possibly returning a negative value.
3542 * @param frame The session frame position.
3543 * @param sub_num The subdivision to use when rounding the quarter note.
3544 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3545 * Positive integers indicate quarter note (non BBT) divisions.
3546 * 0 indicates that the returned quarter note should not be rounded (equivalent to quarter_note_at_frame()).
3547 * @return The quarter note position of the supplied frame.
3549 * When working to a musical grid, the use of sub_nom indicates that
3550 * the frame position should be interpreted musically.
3552 * it effectively snaps to meter bars, meter beats or quarter note divisions
3553 * (as per current gui convention) and returns a musical position independent of frame rate.
3555 * If the supplied frame lies before the first meter, the return will be negative,
3556 * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
3557 * the continuation of the tempo curve (backwards).
3559 * This function is tempo-sensitive.
3562 TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num) const
3564 Glib::Threads::RWLock::ReaderLock lm (lock);
3566 return exact_qn_at_frame_locked (_metrics, frame, sub_num);
3570 TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num) const
3572 double qn = quarter_note_at_minute_locked (metrics, minute_at_frame (frame));
3575 qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
3576 } else if (sub_num == 1) {
3577 /* the gui requested exact musical (BBT) beat */
3578 qn = quarter_note_at_beat_locked (metrics, floor (beat_at_minute_locked (metrics, minute_at_frame (frame)) + 0.5));
3579 } else if (sub_num == -1) {
3581 Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
3585 const double prev_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3587 const double next_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3589 if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
3599 /** returns the frame duration of the supplied BBT time at a specified frame position in the tempo map.
3600 * @param pos the frame position in the tempo map.
3601 * @param bbt the distance in BBT time from pos to calculate.
3602 * @param dir the rounding direction..
3603 * @return the duration in frames between pos and bbt
3606 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
3608 Glib::Threads::RWLock::ReaderLock lm (lock);
3610 BBT_Time pos_bbt = bbt_at_minute_locked (_metrics, minute_at_frame (pos));
3612 const double divisions = meter_section_at_minute_locked (_metrics, minute_at_frame (pos)).divisions_per_bar();
3615 pos_bbt.bars += bbt.bars;
3617 pos_bbt.ticks += bbt.ticks;
3618 if ((double) pos_bbt.ticks > BBT_Time::ticks_per_beat) {
3620 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3623 pos_bbt.beats += bbt.beats;
3624 if ((double) pos_bbt.beats > divisions) {
3626 pos_bbt.beats -= divisions;
3628 const framecnt_t pos_bbt_frame = frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3630 return pos_bbt_frame - pos;
3634 if (pos_bbt.bars <= bbt.bars) {
3637 pos_bbt.bars -= bbt.bars;
3640 if (pos_bbt.ticks < bbt.ticks) {
3641 if (pos_bbt.bars > 1) {
3642 if (pos_bbt.beats == 1) {
3644 pos_bbt.beats = divisions;
3648 pos_bbt.ticks = BBT_Time::ticks_per_beat - (bbt.ticks - pos_bbt.ticks);
3654 pos_bbt.ticks -= bbt.ticks;
3657 if (pos_bbt.beats <= bbt.beats) {
3658 if (pos_bbt.bars > 1) {
3660 pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
3665 pos_bbt.beats -= bbt.beats;
3668 return pos - frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3675 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
3677 return round_to_type (fr, dir, Bar);
3681 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
3683 return round_to_type (fr, dir, Beat);
3687 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3689 Glib::Threads::RWLock::ReaderLock lm (lock);
3690 uint32_t ticks = (uint32_t) floor (max (0.0, beat_at_minute_locked (_metrics, minute_at_frame (fr))) * BBT_Time::ticks_per_beat);
3691 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3692 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3694 ticks -= beats * BBT_Time::ticks_per_beat;
3697 /* round to next (or same iff dir == RoundUpMaybe) */
3699 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3701 if (mod == 0 && dir == RoundUpMaybe) {
3702 /* right on the subdivision, which is fine, so do nothing */
3704 } else if (mod == 0) {
3705 /* right on the subdivision, so the difference is just the subdivision ticks */
3706 ticks += ticks_one_subdivisions_worth;
3709 /* not on subdivision, compute distance to next subdivision */
3711 ticks += ticks_one_subdivisions_worth - mod;
3714 if (ticks >= BBT_Time::ticks_per_beat) {
3715 ticks -= BBT_Time::ticks_per_beat;
3717 } else if (dir < 0) {
3719 /* round to previous (or same iff dir == RoundDownMaybe) */
3721 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3723 if (difference == 0 && dir == RoundDownAlways) {
3724 /* right on the subdivision, but force-rounding down,
3725 so the difference is just the subdivision ticks */
3726 difference = ticks_one_subdivisions_worth;
3729 if (ticks < difference) {
3730 ticks = BBT_Time::ticks_per_beat - ticks;
3732 ticks -= difference;
3736 /* round to nearest */
3739 /* compute the distance to the previous and next subdivision */
3741 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3743 /* closer to the next subdivision, so shift forward */
3745 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3747 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3749 if (ticks > BBT_Time::ticks_per_beat) {
3751 ticks -= BBT_Time::ticks_per_beat;
3752 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3755 } else if (rem > 0) {
3757 /* closer to previous subdivision, so shift backward */
3761 /* can't go backwards past zero, so ... */
3764 /* step back to previous beat */
3766 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3767 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3769 ticks = lrint (ticks - rem);
3770 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3773 /* on the subdivision, do nothing */
3777 const framepos_t ret_frame = frame_at_minute (minute_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat)));
3783 TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3785 Glib::Threads::RWLock::ReaderLock lm (lock);
3786 uint32_t ticks = (uint32_t) floor (max (0.0, quarter_note_at_minute_locked (_metrics, minute_at_frame (fr))) * BBT_Time::ticks_per_beat);
3787 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3788 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3790 ticks -= beats * BBT_Time::ticks_per_beat;
3793 /* round to next (or same iff dir == RoundUpMaybe) */
3795 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3797 if (mod == 0 && dir == RoundUpMaybe) {
3798 /* right on the subdivision, which is fine, so do nothing */
3800 } else if (mod == 0) {
3801 /* right on the subdivision, so the difference is just the subdivision ticks */
3802 ticks += ticks_one_subdivisions_worth;
3805 /* not on subdivision, compute distance to next subdivision */
3807 ticks += ticks_one_subdivisions_worth - mod;
3810 //NOTE: this code intentionally limits the rounding so we don't advance to the next beat.
3811 // For the purposes of "jump-to-next-subdivision", we DO want to advance to the next beat.
3812 // And since the "prev" direction DOES move beats, I assume this code is unintended.
3813 // But I'm keeping it around, until we determine there are no terrible consequences.
3814 // if (ticks >= BBT_Time::ticks_per_beat) {
3815 // ticks -= BBT_Time::ticks_per_beat;
3818 } else if (dir < 0) {
3820 /* round to previous (or same iff dir == RoundDownMaybe) */
3822 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3824 if (difference == 0 && dir == RoundDownAlways) {
3825 /* right on the subdivision, but force-rounding down,
3826 so the difference is just the subdivision ticks */
3827 difference = ticks_one_subdivisions_worth;
3830 if (ticks < difference) {
3831 ticks = BBT_Time::ticks_per_beat - ticks;
3833 ticks -= difference;
3837 /* round to nearest */
3840 /* compute the distance to the previous and next subdivision */
3842 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3844 /* closer to the next subdivision, so shift forward */
3846 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3848 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3850 if (ticks > BBT_Time::ticks_per_beat) {
3852 ticks -= BBT_Time::ticks_per_beat;
3853 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3856 } else if (rem > 0) {
3858 /* closer to previous subdivision, so shift backward */
3862 /* can't go backwards past zero, so ... */
3865 /* step back to previous beat */
3867 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3868 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3870 ticks = lrint (ticks - rem);
3871 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3874 /* on the subdivision, do nothing */
3878 const framepos_t ret_frame = frame_at_minute (minute_at_quarter_note_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat)));
3884 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
3886 Glib::Threads::RWLock::ReaderLock lm (lock);
3888 const double beat_at_framepos = max (0.0, beat_at_minute_locked (_metrics, minute_at_frame (frame)));
3889 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
3894 /* find bar previous to 'frame' */
3899 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3901 } else if (dir > 0) {
3902 /* find bar following 'frame' */
3906 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3908 /* true rounding: find nearest bar */
3909 framepos_t raw_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3912 framepos_t prev_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3914 framepos_t next_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3916 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
3927 return frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos)));
3928 } else if (dir > 0) {
3929 return frame_at_minute (minute_at_beat_locked (_metrics, ceil (beat_at_framepos)));
3931 return frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5)));
3940 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
3941 framepos_t lower, framepos_t upper, uint32_t bar_mod)
3943 Glib::Threads::RWLock::ReaderLock lm (lock);
3944 int32_t cnt = ceil (beat_at_minute_locked (_metrics, minute_at_frame (lower)));
3946 /* although the map handles negative beats, bbt doesn't. */
3951 if (minute_at_beat_locked (_metrics, cnt) >= minute_at_frame (upper)) {
3955 while (pos >= 0 && pos < upper) {
3956 pos = frame_at_minute (minute_at_beat_locked (_metrics, cnt));
3957 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
3958 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
3959 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
3960 const double c = (tempo.type() == TempoSection::Constant) ? 0.0 : tempo.c_func();
3962 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, c));
3966 BBT_Time bbt = bbt_at_minute_locked (_metrics, minute_at_frame (lower));
3971 bbt.bars -= bbt.bars % bar_mod;
3975 while (pos >= 0 && pos < upper) {
3976 pos = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3977 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
3978 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
3979 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c_func()));
3980 bbt.bars += bar_mod;
3986 TempoMap::tempo_section_at_frame (framepos_t frame) const
3988 Glib::Threads::RWLock::ReaderLock lm (lock);
3990 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
3994 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute) const
3996 TempoSection* prev = 0;
4000 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4002 if ((*i)->is_tempo()) {
4003 t = static_cast<TempoSection*> (*i);
4007 if (prev && t->minute() > minute) {
4017 abort(); /*NOTREACHED*/
4024 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4026 TempoSection* prev_t = 0;
4027 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
4031 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4032 if ((*i)->is_tempo()) {
4033 t = static_cast<TempoSection*> (*i);
4034 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
4044 /* don't use this to calculate length (the tempo is only correct for this frame).
4045 do that stuff based on the beat_at_frame and frame_at_beat api
4048 TempoMap::frames_per_quarter_note_at (const framepos_t& frame, const framecnt_t& sr) const
4050 Glib::Threads::RWLock::ReaderLock lm (lock);
4052 const TempoSection* ts_at = 0;
4053 const TempoSection* ts_after = 0;
4054 Metrics::const_iterator i;
4057 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4059 if ((*i)->is_tempo()) {
4060 t = static_cast<TempoSection*> (*i);
4064 if (ts_at && (*i)->frame() > frame) {
4074 return (60.0 * _frame_rate) / ts_at->tempo_at_minute (minute_at_frame (frame)).quarter_notes_per_minute();
4076 /* must be treated as constant tempo */
4077 return ts_at->frames_per_quarter_note (_frame_rate);
4081 TempoMap::meter_section_at_frame (framepos_t frame) const
4083 Glib::Threads::RWLock::ReaderLock lm (lock);
4084 return meter_section_at_minute_locked (_metrics, minute_at_frame (frame));
4088 TempoMap::meter_section_at_minute_locked (const Metrics& metrics, double minute) const
4090 Metrics::const_iterator i;
4091 MeterSection* prev = 0;
4095 for (i = metrics.begin(); i != metrics.end(); ++i) {
4097 if (!(*i)->is_tempo()) {
4098 m = static_cast<MeterSection*> (*i);
4100 if (prev && (*i)->minute() > minute) {
4110 abort(); /*NOTREACHED*/
4117 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4119 MeterSection* prev_m = 0;
4121 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4123 if (!(*i)->is_tempo()) {
4124 m = static_cast<MeterSection*> (*i);
4125 if (prev_m && m->beat() > beat) {
4136 TempoMap::meter_section_at_beat (double beat) const
4138 Glib::Threads::RWLock::ReaderLock lm (lock);
4139 return meter_section_at_beat_locked (_metrics, beat);
4143 TempoMap::meter_at_frame (framepos_t frame) const
4145 TempoMetric m (metric_at (frame));
4150 TempoMap::fix_legacy_session ()
4152 MeterSection* prev_m = 0;
4153 TempoSection* prev_t = 0;
4155 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4159 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
4161 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
4164 m->set_minute (0.0);
4165 m->set_position_lock_style (AudioTime);
4170 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
4171 + (m->bbt().beats - 1)
4172 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
4174 m->set_beat (start);
4175 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
4176 + (m->bbt().beats - 1)
4177 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
4178 m->set_pulse (start_beat / prev_m->note_divisor());
4181 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4189 t->set_minute (0.0);
4190 t->set_position_lock_style (AudioTime);
4196 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
4197 + (t->legacy_bbt().beats - 1)
4198 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
4200 t->set_pulse (beat / prev_m->note_divisor());
4202 /* really shouldn't happen but.. */
4203 t->set_pulse (beat / 4.0);
4212 TempoMap::get_state ()
4214 Metrics::const_iterator i;
4215 XMLNode *root = new XMLNode ("TempoMap");
4218 Glib::Threads::RWLock::ReaderLock lm (lock);
4219 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4220 root->add_child_nocopy ((*i)->get_state());
4228 TempoMap::set_state (const XMLNode& node, int /*version*/)
4231 Glib::Threads::RWLock::WriterLock lm (lock);
4234 XMLNodeConstIterator niter;
4235 Metrics old_metrics (_metrics);
4238 nlist = node.children();
4240 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
4241 XMLNode* child = *niter;
4243 if (child->name() == TempoSection::xml_state_node_name) {
4246 TempoSection* ts = new TempoSection (*child, _frame_rate);
4247 _metrics.push_back (ts);
4250 catch (failed_constructor& err){
4251 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4252 _metrics = old_metrics;
4253 old_metrics.clear();
4257 } else if (child->name() == MeterSection::xml_state_node_name) {
4260 MeterSection* ms = new MeterSection (*child, _frame_rate);
4261 _metrics.push_back (ms);
4264 catch (failed_constructor& err) {
4265 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4266 _metrics = old_metrics;
4267 old_metrics.clear();
4273 if (niter == nlist.end()) {
4274 MetricSectionSorter cmp;
4275 _metrics.sort (cmp);
4278 /* check for legacy sessions where bbt was the base musical unit for tempo */
4279 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4281 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
4282 if (t->legacy_bbt().bars != 0) {
4283 fix_legacy_session();
4290 /* check for multiple tempo/meters at the same location, which
4291 ardour2 somehow allowed.
4294 Metrics::iterator prev = _metrics.end();
4295 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4296 if (prev != _metrics.end()) {
4298 MeterSection* prev_m;
4300 TempoSection* prev_t;
4301 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
4302 if (prev_m->pulse() == ms->pulse()) {
4303 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4304 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4307 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
4308 if (prev_t->pulse() == ts->pulse()) {
4309 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4310 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4318 recompute_map (_metrics);
4320 Metrics::const_iterator d = old_metrics.begin();
4321 while (d != old_metrics.end()) {
4325 old_metrics.clear ();
4328 PropertyChanged (PropertyChange ());
4334 TempoMap::dump (std::ostream& o) const
4336 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
4337 const MeterSection* m;
4338 const TempoSection* t;
4339 const TempoSection* prev_t = 0;
4341 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4343 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
4344 o << "Tempo @ " << *i << t->note_types_per_minute() << " BPM (pulse = 1/" << t->note_type()
4345 << " type= " << enum_2_string (t->type()) << ") " << " at pulse= " << t->pulse()
4346 << " minute= " << t->minute() << " frame= " << t->frame() << " (initial? " << t->initial() << ')'
4347 << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
4349 o << " current : " << t->note_types_per_minute()
4350 << " | " << t->pulse() << " | " << t->frame() << " | " << t->minute() << std::endl;
4351 o << " previous : " << prev_t->note_types_per_minute()
4352 << " | " << prev_t->pulse() << " | " << prev_t->frame() << " | " << prev_t->minute() << std::endl;
4353 o << " calculated : " << prev_t->tempo_at_pulse (t->pulse())
4354 << " | " << prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute())
4355 << " | " << frame_at_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()))
4356 << " | " << prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()) << std::endl;
4359 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
4360 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt()
4361 << " frame= " << m->frame() << " pulse: " << m->pulse() << " beat : " << m->beat()
4362 << " pos lock: " << enum_2_string (m->position_lock_style()) << " (initial? " << m->initial() << ')' << endl;
4365 o << "------" << std::endl;
4369 TempoMap::n_tempos() const
4371 Glib::Threads::RWLock::ReaderLock lm (lock);
4374 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4375 if ((*i)->is_tempo()) {
4384 TempoMap::n_meters() const
4386 Glib::Threads::RWLock::ReaderLock lm (lock);
4389 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4390 if (!(*i)->is_tempo()) {
4399 TempoMap::insert_time (framepos_t where, framecnt_t amount)
4401 for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
4402 if ((*i)->frame() >= where && !(*i)->initial ()) {
4406 if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
4407 gui_set_meter_position (ms, (*i)->frame() + amount);
4410 if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
4411 gui_set_tempo_position (ts, (*i)->frame() + amount, 0);
4416 PropertyChanged (PropertyChange ());
4420 TempoMap::remove_time (framepos_t where, framecnt_t amount)
4424 std::list<MetricSection*> metric_kill_list;
4426 TempoSection* last_tempo = NULL;
4427 MeterSection* last_meter = NULL;
4428 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
4429 bool meter_after = false; // is there a meter marker likewise?
4431 Glib::Threads::RWLock::WriterLock lm (lock);
4432 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4433 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
4434 metric_kill_list.push_back(*i);
4435 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
4438 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
4442 else if ((*i)->frame() >= where) {
4443 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
4444 (*i)->set_minute ((*i)->minute() - minute_at_frame (amount));
4445 if ((*i)->frame() == where) {
4446 // marker was immediately after end of range
4447 tempo_after = dynamic_cast<TempoSection*> (*i);
4448 meter_after = dynamic_cast<MeterSection*> (*i);
4454 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
4455 if (last_tempo && !tempo_after) {
4456 metric_kill_list.remove(last_tempo);
4457 last_tempo->set_minute (minute_at_frame (where));
4460 if (last_meter && !meter_after) {
4461 metric_kill_list.remove(last_meter);
4462 last_meter->set_minute (minute_at_frame (where));
4466 //remove all the remaining metrics
4467 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
4468 _metrics.remove(*i);
4473 recompute_map (_metrics);
4476 PropertyChanged (PropertyChange ());
4480 /** Add some (fractional) Beats to a session frame position, and return the result in frames.
4481 * pos can be -ve, if required.
4484 TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats beats) const
4486 Glib::Threads::RWLock::ReaderLock lm (lock);
4487 const double frame_qn = quarter_note_at_minute_locked (_metrics, minute_at_frame (frame));
4489 return frame_at_minute (minute_at_quarter_note_locked (_metrics, frame_qn + beats.to_double()));
4493 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
4495 Glib::Threads::RWLock::ReaderLock lm (lock);
4497 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_minute_locked (_metrics, minute_at_frame (pos)));
4498 pos_bbt.ticks += op.ticks;
4499 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
4501 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
4503 pos_bbt.beats += op.beats;
4504 /* the meter in effect will start on the bar */
4505 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();
4506 while (pos_bbt.beats >= divisions_per_bar + 1) {
4508 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4509 pos_bbt.beats -= divisions_per_bar;
4511 pos_bbt.bars += op.bars;
4513 return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
4516 /** Count the number of beats that are equivalent to distance when going forward,
4520 TempoMap::framewalk_to_qn (framepos_t pos, framecnt_t distance) const
4522 Glib::Threads::RWLock::ReaderLock lm (lock);
4524 return Evoral::Beats (quarter_notes_between_frames_locked (_metrics, pos, pos + distance));
4528 bool operator() (const BBT_Time& a, const BBT_Time& b) {
4534 operator<< (std::ostream& o, const Meter& m) {
4535 return o << m.divisions_per_bar() << '/' << m.note_divisor();
4539 operator<< (std::ostream& o, const Tempo& t) {
4540 return o << t.note_types_per_minute() << " 1/" << t.note_type() << "'s per minute";
4544 operator<< (std::ostream& o, const MetricSection& section) {
4546 o << "MetricSection @ " << section.frame() << ' ';
4548 const TempoSection* ts;
4549 const MeterSection* ms;
4551 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
4552 o << *((const Tempo*) ts);
4553 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
4554 o << *((const Meter*) ms);