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;
1019 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1020 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
1023 bool const ipm = insert_meter->position_lock_style() == MusicTime;
1024 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
1029 } else if (insert_tempo) {
1030 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
1031 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
1034 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
1035 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
1042 _metrics.insert (i, section);
1046 /* user supplies the exact pulse if pls == MusicTime */
1048 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, ARDOUR::TempoSection::Type type, PositionLockStyle pls)
1050 TempoSection* ts = 0;
1052 Glib::Threads::RWLock::WriterLock lm (lock);
1053 ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), type, pls, true);
1057 PropertyChanged (PropertyChange ());
1063 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, TempoSection::Type type, PositionLockStyle pls)
1065 const bool locked_to_meter = ts.locked_to_meter();
1068 Glib::Threads::RWLock::WriterLock lm (lock);
1069 TempoSection& first (first_tempo());
1070 if (ts.frame() != first.frame()) {
1071 remove_tempo_locked (ts);
1072 add_tempo_locked (tempo, pulse, minute_at_frame (frame), type, pls, true, locked_to_meter);
1074 first.set_type (type);
1075 first.set_pulse (0.0);
1076 first.set_minute (minute_at_frame (frame));
1077 first.set_position_lock_style (AudioTime);
1079 /* cannot move the first tempo section */
1080 *static_cast<Tempo*>(&first) = tempo;
1081 recompute_map (_metrics);
1086 PropertyChanged (PropertyChange ());
1090 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, double minute
1091 , TempoSection::Type type, PositionLockStyle pls, bool recompute, bool locked_to_meter)
1093 TempoSection* t = new TempoSection (pulse, minute, tempo.note_types_per_minute(), tempo.note_type(), type, pls, _frame_rate);
1094 t->set_locked_to_meter (locked_to_meter);
1095 bool solved = false;
1100 if (pls == AudioTime) {
1101 solved = solve_map_minute (_metrics, t, t->minute());
1103 solved = solve_map_pulse (_metrics, t, t->pulse());
1105 recompute_meters (_metrics);
1108 if (!solved && recompute) {
1109 recompute_map (_metrics);
1116 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, PositionLockStyle pls)
1118 MeterSection* m = 0;
1120 Glib::Threads::RWLock::WriterLock lm (lock);
1121 m = add_meter_locked (meter, beat, where, pls, true);
1126 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1131 PropertyChanged (PropertyChange ());
1136 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, PositionLockStyle pls)
1139 Glib::Threads::RWLock::WriterLock lm (lock);
1140 const double beat = beat_at_bbt_locked (_metrics, where);
1142 if (!ms.initial()) {
1143 remove_meter_locked (ms);
1144 add_meter_locked (meter, beat, where, pls, true);
1146 MeterSection& first (first_meter());
1147 TempoSection& first_t (first_tempo());
1148 /* cannot move the first meter section */
1149 *static_cast<Meter*>(&first) = meter;
1150 first.set_position_lock_style (AudioTime);
1151 first.set_pulse (0.0);
1152 //first.set_minute (minute_at_frame (frame));
1153 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1154 first.set_beat (beat);
1155 first_t.set_minute (first.minute());
1156 first_t.set_pulse (0.0);
1157 first_t.set_position_lock_style (AudioTime);
1158 recompute_map (_metrics);
1162 PropertyChanged (PropertyChange ());
1166 TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, PositionLockStyle pls, bool recompute)
1168 const MeterSection& prev_m = meter_section_at_minute_locked (_metrics, minute_at_beat_locked (_metrics, beat) - minute_at_frame (1));
1169 const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1170 const double time_minutes = minute_at_pulse_locked (_metrics, pulse);
1171 TempoSection* mlt = 0;
1173 if (pls == AudioTime) {
1174 /* add meter-locked tempo */
1175 mlt = add_tempo_locked (tempo_at_minute_locked (_metrics, time_minutes), pulse, time_minutes, TempoSection::Ramp, AudioTime, true, true);
1183 MeterSection* new_meter = new MeterSection (pulse, time_minutes, beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls, _frame_rate);
1184 bool solved = false;
1186 do_insert (new_meter);
1190 if (pls == AudioTime) {
1191 solved = solve_map_minute (_metrics, new_meter, time_minutes);
1193 solved = solve_map_bbt (_metrics, new_meter, where);
1194 /* required due to resetting the pulse of meter-locked tempi above.
1195 Arguably solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
1196 but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
1198 recompute_map (_metrics);
1202 if (!solved && recompute) {
1203 /* if this has failed to solve, there is little we can do other than to ensure that
1204 the new map is recalculated.
1206 warning << "Adding meter may have left the tempo map unsolved." << endmsg;
1207 recompute_map (_metrics);
1214 TempoMap::change_initial_tempo (double note_types_per_minute, double note_type)
1216 Tempo newtempo (note_types_per_minute, note_type);
1219 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1220 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1225 Glib::Threads::RWLock::WriterLock lm (lock);
1226 *((Tempo*) t) = newtempo;
1227 recompute_map (_metrics);
1229 PropertyChanged (PropertyChange ());
1236 TempoMap::change_existing_tempo_at (framepos_t where, double note_types_per_minute, double note_type)
1238 Tempo newtempo (note_types_per_minute, note_type);
1241 TempoSection* first;
1242 Metrics::iterator i;
1244 /* find the TempoSection immediately preceding "where"
1247 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1249 if ((*i)->frame() > where) {
1255 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1268 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1278 Glib::Threads::RWLock::WriterLock lm (lock);
1279 /* cannot move the first tempo section */
1280 *((Tempo*)prev) = newtempo;
1281 recompute_map (_metrics);
1284 PropertyChanged (PropertyChange ());
1288 TempoMap::first_meter () const
1290 const MeterSection *m = 0;
1292 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1293 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1298 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1299 abort(); /*NOTREACHED*/
1304 TempoMap::first_meter ()
1306 MeterSection *m = 0;
1308 /* CALLER MUST HOLD LOCK */
1310 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1311 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1316 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1317 abort(); /*NOTREACHED*/
1322 TempoMap::first_tempo () const
1324 const TempoSection *t = 0;
1326 /* CALLER MUST HOLD LOCK */
1328 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1329 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1339 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1340 abort(); /*NOTREACHED*/
1345 TempoMap::first_tempo ()
1347 TempoSection *t = 0;
1349 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1350 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1360 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1361 abort(); /*NOTREACHED*/
1365 TempoMap::recompute_tempi (Metrics& metrics)
1367 TempoSection* prev_t = 0;
1369 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1372 if ((*i)->is_tempo()) {
1373 t = static_cast<TempoSection*> (*i);
1385 if (t->position_lock_style() == AudioTime) {
1386 prev_t->set_c_func (prev_t->compute_c_func_minute (t->note_types_per_minute(), t->minute()));
1387 if (!t->locked_to_meter()) {
1388 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
1392 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->note_types_per_minute(), t->pulse()));
1393 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
1401 prev_t->set_c_func (0.0);
1404 /* tempos must be positioned correctly.
1405 the current approach is to use a meter's bbt time as its base position unit.
1406 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1407 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1410 TempoMap::recompute_meters (Metrics& metrics)
1412 MeterSection* meter = 0;
1413 MeterSection* prev_m = 0;
1415 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1416 if (!(*mi)->is_tempo()) {
1417 meter = static_cast<MeterSection*> (*mi);
1418 if (meter->position_lock_style() == AudioTime) {
1420 pair<double, BBT_Time> b_bbt;
1421 TempoSection* meter_locked_tempo = 0;
1422 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1424 if ((*ii)->is_tempo()) {
1425 t = static_cast<TempoSection*> (*ii);
1426 if ((t->locked_to_meter() || t->initial()) && t->frame() == meter->frame()) {
1427 meter_locked_tempo = t;
1434 double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1435 if (beats + prev_m->beat() != meter->beat()) {
1436 /* reordering caused a bbt change */
1438 beats = meter->beat() - prev_m->beat();
1439 b_bbt = make_pair (beats + prev_m->beat()
1440 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1441 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1443 } else if (!meter->initial()) {
1444 b_bbt = make_pair (meter->beat(), meter->bbt());
1445 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1448 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1450 if (meter_locked_tempo) {
1451 meter_locked_tempo->set_pulse (pulse);
1453 meter->set_beat (b_bbt);
1454 meter->set_pulse (pulse);
1459 pair<double, BBT_Time> b_bbt;
1461 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1462 if (beats + prev_m->beat() != meter->beat()) {
1463 /* reordering caused a bbt change */
1464 b_bbt = make_pair (beats + prev_m->beat()
1465 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1467 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1469 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1471 /* shouldn't happen - the first is audio-locked */
1472 pulse = pulse_at_beat_locked (metrics, meter->beat());
1473 b_bbt = make_pair (meter->beat(), meter->bbt());
1476 meter->set_beat (b_bbt);
1477 meter->set_pulse (pulse);
1478 meter->set_minute (minute_at_pulse_locked (metrics, pulse));
1487 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1489 /* CALLER MUST HOLD WRITE LOCK */
1491 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1494 /* silly call from Session::process() during startup
1499 recompute_tempi (metrics);
1500 recompute_meters (metrics);
1504 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1506 Glib::Threads::RWLock::ReaderLock lm (lock);
1507 TempoMetric m (first_meter(), first_tempo());
1509 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1510 at something, because we insert the default tempo and meter during
1511 TempoMap construction.
1513 now see if we can find better candidates.
1516 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1518 if ((*i)->frame() > frame) {
1532 /* XX meters only */
1534 TempoMap::metric_at (BBT_Time bbt) const
1536 Glib::Threads::RWLock::ReaderLock lm (lock);
1537 TempoMetric m (first_meter(), first_tempo());
1539 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1540 at something, because we insert the default tempo and meter during
1541 TempoMap construction.
1543 now see if we can find better candidates.
1546 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1548 if (!(*i)->is_tempo()) {
1549 mw = static_cast<MeterSection*> (*i);
1550 BBT_Time section_start (mw->bbt());
1552 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1563 /** Returns the BBT (meter-based) beat corresponding to the supplied frame, possibly returning a negative value.
1564 * @param frame The session frame position.
1565 * @return The beat duration according to the tempo map at the supplied frame.
1567 * If the supplied frame lies before the first meter, the returned beat duration will be negative.
1568 * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
1570 * This function uses both tempo and meter.
1573 TempoMap::beat_at_frame (const framecnt_t& frame) const
1575 Glib::Threads::RWLock::ReaderLock lm (lock);
1577 return beat_at_minute_locked (_metrics, minute_at_frame (frame));
1580 /* This function uses both tempo and meter.*/
1582 TempoMap::beat_at_minute_locked (const Metrics& metrics, const double& minute) const
1584 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
1585 MeterSection* prev_m = 0;
1586 MeterSection* next_m = 0;
1588 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1589 if (!(*i)->is_tempo()) {
1590 if (prev_m && (*i)->minute() > minute) {
1591 next_m = static_cast<MeterSection*> (*i);
1594 prev_m = static_cast<MeterSection*> (*i);
1598 const double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
1600 /* audio locked meters fake their beat */
1601 if (next_m && next_m->beat() < beat) {
1602 return next_m->beat();
1608 /** Returns the frame corresponding to the supplied BBT (meter-based) beat.
1609 * @param beat The BBT (meter-based) beat.
1610 * @return The frame duration according to the tempo map at the supplied BBT (meter-based) beat.
1612 * This function uses both tempo and meter.
1615 TempoMap::frame_at_beat (const double& beat) const
1617 Glib::Threads::RWLock::ReaderLock lm (lock);
1619 return frame_at_minute (minute_at_beat_locked (_metrics, beat));
1622 /* meter & tempo section based */
1624 TempoMap::minute_at_beat_locked (const Metrics& metrics, const double& beat) const
1626 MeterSection* prev_m = 0;
1627 TempoSection* prev_t = 0;
1631 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1632 if (!(*i)->is_tempo()) {
1633 m = static_cast<MeterSection*> (*i);
1634 if (prev_m && m->beat() > beat) {
1644 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1645 if ((*i)->is_tempo()) {
1646 t = static_cast<TempoSection*> (*i);
1647 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1656 return prev_t->minute_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse());
1659 /** Returns a Tempo corresponding to the supplied frame position.
1660 * @param frame The audio frame.
1661 * @return a Tempo according to the tempo map at the supplied frame.
1665 TempoMap::tempo_at_frame (const framepos_t& frame) const
1667 Glib::Threads::RWLock::ReaderLock lm (lock);
1669 return tempo_at_minute_locked (_metrics, minute_at_frame (frame));
1673 TempoMap::tempo_at_minute_locked (const Metrics& metrics, const double& minute) const
1675 TempoSection* prev_t = 0;
1679 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1680 if ((*i)->is_tempo()) {
1681 t = static_cast<TempoSection*> (*i);
1685 if ((prev_t) && t->minute() > minute) {
1686 /* t is the section past frame */
1687 return prev_t->tempo_at_minute (minute);
1693 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type());
1696 /** returns the frame at which the supplied tempo occurs, or
1697 * the frame of the last tempo section (search exhausted)
1698 * only the position of the first occurence will be returned
1702 TempoMap::frame_at_tempo (const Tempo& tempo) const
1704 Glib::Threads::RWLock::ReaderLock lm (lock);
1706 return frame_at_minute (minute_at_tempo_locked (_metrics, tempo));
1710 TempoMap::minute_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1712 TempoSection* prev_t = 0;
1713 const double tempo_bpm = tempo.note_types_per_minute();
1715 Metrics::const_iterator i;
1717 for (i = metrics.begin(); i != metrics.end(); ++i) {
1719 if ((*i)->is_tempo()) {
1720 t = static_cast<TempoSection*> (*i);
1726 const double t_bpm = t->note_types_per_minute();
1728 if (t_bpm == tempo_bpm) {
1733 const double prev_t_bpm = prev_t->note_types_per_minute();
1735 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1736 return prev_t->minute_at_ntpm (prev_t->note_types_per_minute(), prev_t->pulse());
1743 return prev_t->minute();
1747 TempoMap::tempo_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1749 TempoSection* prev_t = 0;
1753 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1754 if ((*i)->is_tempo()) {
1755 t = static_cast<TempoSection*> (*i);
1759 if ((prev_t) && t->pulse() > pulse) {
1760 /* t is the section past frame */
1761 return prev_t->tempo_at_pulse (pulse);
1767 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type());
1771 TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1773 TempoSection* prev_t = 0;
1774 const double tempo_bpm = tempo.note_types_per_minute();
1776 Metrics::const_iterator i;
1778 for (i = metrics.begin(); i != metrics.end(); ++i) {
1780 if ((*i)->is_tempo()) {
1781 t = static_cast<TempoSection*> (*i);
1787 const double t_bpm = t->note_types_per_minute();
1789 if (t_bpm == tempo_bpm) {
1794 const double prev_t_bpm = prev_t->note_types_per_minute();
1796 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1797 return prev_t->pulse_at_ntpm (prev_t->note_types_per_minute(), prev_t->minute());
1804 return prev_t->pulse();
1807 /** Returns a Tempo corresponding to the supplied position in quarter-note beats.
1808 * @param qn the position in quarter note beats.
1809 * @return the Tempo at the supplied quarter-note.
1812 TempoMap::tempo_at_quarter_note (const double& qn) const
1814 Glib::Threads::RWLock::ReaderLock lm (lock);
1816 return tempo_at_pulse_locked (_metrics, qn / 4.0);
1819 /** Returns the position in quarter-note beats corresponding to the supplied Tempo.
1820 * @param tempo the tempo.
1821 * @return the position in quarter-note beats where the map bpm
1822 * is equal to that of the Tempo. currently ignores note_type.
1825 TempoMap::quarter_note_at_tempo (const Tempo& tempo) const
1827 Glib::Threads::RWLock::ReaderLock lm (lock);
1829 return pulse_at_tempo_locked (_metrics, tempo) * 4.0;;
1832 /** Returns the whole-note pulse corresponding to the supplied BBT (meter-based) beat.
1833 * @param metrics the list of metric sections used to calculate the pulse.
1834 * @param beat The BBT (meter-based) beat.
1835 * @return the whole-note pulse at the supplied BBT (meter-based) beat.
1837 * a pulse or whole note is the base musical position of a MetricSection.
1838 * it is equivalent to four quarter notes.
1842 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1844 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1846 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1849 /** Returns the BBT (meter-based) beat corresponding to the supplied whole-note pulse .
1850 * @param metrics the list of metric sections used to calculate the beat.
1851 * @param pulse the whole-note pulse.
1852 * @return the meter-based beat at the supplied whole-note pulse.
1854 * a pulse or whole note is the base musical position of a MetricSection.
1855 * it is equivalent to four quarter notes.
1858 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1860 MeterSection* prev_m = 0;
1862 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1864 if (!(*i)->is_tempo()) {
1865 m = static_cast<MeterSection*> (*i);
1866 if (prev_m && m->pulse() > pulse) {
1874 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1878 /* tempo section based */
1880 TempoMap::pulse_at_minute_locked (const Metrics& metrics, const double& minute) const
1882 /* HOLD (at least) THE READER LOCK */
1883 TempoSection* prev_t = 0;
1885 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1887 if ((*i)->is_tempo()) {
1888 t = static_cast<TempoSection*> (*i);
1892 if (prev_t && t->minute() > minute) {
1893 /*the previous ts is the one containing the frame */
1894 const double ret = prev_t->pulse_at_minute (minute);
1895 /* audio locked section in new meter*/
1896 if (t->pulse() < ret) {
1905 /* treated as constant for this ts */
1906 const double pulses_in_section = ((minute - prev_t->minute()) * prev_t->note_types_per_minute()) / prev_t->note_type();
1908 return pulses_in_section + prev_t->pulse();
1911 /* tempo section based */
1913 TempoMap::minute_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1915 /* HOLD THE READER LOCK */
1917 const TempoSection* prev_t = 0;
1919 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1922 if ((*i)->is_tempo()) {
1923 t = static_cast<TempoSection*> (*i);
1927 if (prev_t && t->pulse() > pulse) {
1928 return prev_t->minute_at_pulse (pulse);
1934 /* must be treated as constant, irrespective of _type */
1935 double const dtime = ((pulse - prev_t->pulse()) * prev_t->note_type()) / prev_t->note_types_per_minute();
1937 return dtime + prev_t->minute();
1940 /** Returns the BBT (meter-based) beat corresponding to the supplied BBT time.
1941 * @param bbt The BBT time (meter-based).
1942 * @return bbt The BBT beat (meter-based) at the supplied BBT time.
1946 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
1948 Glib::Threads::RWLock::ReaderLock lm (lock);
1949 return beat_at_bbt_locked (_metrics, bbt);
1954 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1956 /* CALLER HOLDS READ LOCK */
1958 MeterSection* prev_m = 0;
1960 /* because audio-locked meters have 'fake' integral beats,
1961 there is no pulse offset here.
1965 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1966 if (!(*i)->is_tempo()) {
1967 m = static_cast<MeterSection*> (*i);
1969 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1970 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1978 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1979 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1980 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1985 /** Returns the BBT time corresponding to the supplied BBT (meter-based) beat.
1986 * @param beat The BBT (meter-based) beat.
1987 * @return The BBT time (meter-based) at the supplied meter-based beat.
1991 TempoMap::bbt_at_beat (const double& beat)
1993 Glib::Threads::RWLock::ReaderLock lm (lock);
1994 return bbt_at_beat_locked (_metrics, beat);
1998 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
2000 /* CALLER HOLDS READ LOCK */
2001 MeterSection* prev_m = 0;
2002 const double beats = max (0.0, b);
2004 MeterSection* m = 0;
2006 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2007 if (!(*i)->is_tempo()) {
2008 m = static_cast<MeterSection*> (*i);
2010 if (m->beat() > beats) {
2011 /* this is the meter after the one our beat is on*/
2021 const double beats_in_ms = beats - prev_m->beat();
2022 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2023 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2024 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2025 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2029 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2030 ret.beats = (uint32_t) floor (remaining_beats);
2031 ret.bars = total_bars;
2033 /* 0 0 0 to 1 1 0 - based mapping*/
2037 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2039 ret.ticks -= BBT_Time::ticks_per_beat;
2042 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2050 /** Returns the quarter-note beat corresponding to the supplied BBT time (meter-based).
2051 * @param bbt The BBT time (meter-based).
2052 * @return the quarter note beat at the supplied BBT time
2054 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2056 * while the input uses meter, the output does not.
2059 TempoMap::quarter_note_at_bbt (const Timecode::BBT_Time& bbt)
2061 Glib::Threads::RWLock::ReaderLock lm (lock);
2063 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2067 TempoMap::quarter_note_at_bbt_rt (const Timecode::BBT_Time& bbt)
2069 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2072 throw std::logic_error ("TempoMap::quarter_note_at_bbt_rt() could not lock tempo map");
2075 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2079 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2081 /* CALLER HOLDS READ LOCK */
2083 MeterSection* prev_m = 0;
2085 /* because audio-locked meters have 'fake' integral beats,
2086 there is no pulse offset here.
2090 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2091 if (!(*i)->is_tempo()) {
2092 m = static_cast<MeterSection*> (*i);
2094 if (m->bbt().bars > bbt.bars) {
2102 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2103 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
2104 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
2109 /** Returns the BBT time corresponding to the supplied quarter-note beat.
2110 * @param qn the quarter-note beat.
2111 * @return The BBT time (meter-based) at the supplied meter-based beat.
2113 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2117 TempoMap::bbt_at_quarter_note (const double& qn)
2119 Glib::Threads::RWLock::ReaderLock lm (lock);
2121 return bbt_at_pulse_locked (_metrics, qn / 4.0);
2124 /** Returns the BBT time (meter-based) corresponding to the supplied whole-note pulse position.
2125 * @param metrics The list of metric sections used to determine the result.
2126 * @param pulse The whole-note pulse.
2127 * @return The BBT time at the supplied whole-note pulse.
2129 * a pulse or whole note is the basic musical position of a MetricSection.
2130 * it is equivalent to four quarter notes.
2131 * while the output uses meter, the input does not.
2134 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2136 MeterSection* prev_m = 0;
2138 MeterSection* m = 0;
2140 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2142 if (!(*i)->is_tempo()) {
2143 m = static_cast<MeterSection*> (*i);
2146 double const pulses_to_m = m->pulse() - prev_m->pulse();
2147 if (prev_m->pulse() + pulses_to_m > pulse) {
2148 /* this is the meter after the one our beat is on*/
2159 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
2160 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2161 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2162 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2163 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2167 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2168 ret.beats = (uint32_t) floor (remaining_beats);
2169 ret.bars = total_bars;
2171 /* 0 0 0 to 1 1 0 mapping*/
2175 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2177 ret.ticks -= BBT_Time::ticks_per_beat;
2180 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2188 /** Returns the BBT time corresponding to the supplied frame position.
2189 * @param frame the position in audio samples.
2190 * @return the BBT time at the frame position .
2194 TempoMap::bbt_at_frame (framepos_t frame)
2201 warning << string_compose (_("tempo map was asked for BBT time at frame %1\n"), frame) << endmsg;
2204 Glib::Threads::RWLock::ReaderLock lm (lock);
2206 return bbt_at_minute_locked (_metrics, minute_at_frame (frame));
2210 TempoMap::bbt_at_frame_rt (framepos_t frame)
2212 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2215 throw std::logic_error ("TempoMap::bbt_at_frame_rt() could not lock tempo map");
2218 return bbt_at_minute_locked (_metrics, minute_at_frame (frame));
2222 TempoMap::bbt_at_minute_locked (const Metrics& metrics, const double& minute) const
2232 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
2233 MeterSection* prev_m = 0;
2234 MeterSection* next_m = 0;
2238 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2239 if (!(*i)->is_tempo()) {
2240 m = static_cast<MeterSection*> (*i);
2241 if (prev_m && m->minute() > minute) {
2249 double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
2251 /* handle frame before first meter */
2252 if (minute < prev_m->minute()) {
2255 /* audio locked meters fake their beat */
2256 if (next_m && next_m->beat() < beat) {
2257 beat = next_m->beat();
2260 beat = max (0.0, beat);
2262 const double beats_in_ms = beat - prev_m->beat();
2263 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2264 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2265 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2266 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2270 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2271 ret.beats = (uint32_t) floor (remaining_beats);
2272 ret.bars = total_bars;
2274 /* 0 0 0 to 1 1 0 - based mapping*/
2278 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2280 ret.ticks -= BBT_Time::ticks_per_beat;
2283 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2291 /** Returns the frame position corresponding to the supplied BBT time.
2292 * @param bbt the position in BBT time.
2293 * @return the frame position at bbt.
2297 TempoMap::frame_at_bbt (const BBT_Time& bbt)
2300 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
2304 if (bbt.beats < 1) {
2305 throw std::logic_error ("beats are counted from one");
2307 Glib::Threads::RWLock::ReaderLock lm (lock);
2309 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
2312 /* meter & tempo section based */
2314 TempoMap::minute_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2316 /* HOLD THE READER LOCK */
2318 const double ret = minute_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2323 * Returns the quarter-note beat position corresponding to the supplied frame.
2325 * @param frame the position in frames.
2326 * @return The quarter-note position of the supplied frame. Ignores meter.
2330 TempoMap::quarter_note_at_frame (const framepos_t frame) const
2332 Glib::Threads::RWLock::ReaderLock lm (lock);
2334 const double ret = quarter_note_at_minute_locked (_metrics, minute_at_frame (frame));
2340 TempoMap::quarter_note_at_minute_locked (const Metrics& metrics, const double minute) const
2342 const double ret = pulse_at_minute_locked (metrics, minute) * 4.0;
2348 TempoMap::quarter_note_at_frame_rt (const framepos_t frame) const
2350 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2353 throw std::logic_error ("TempoMap::quarter_note_at_frame_rt() could not lock tempo map");
2356 const double ret = pulse_at_minute_locked (_metrics, minute_at_frame (frame)) * 4.0;
2362 * Returns the frame position corresponding to the supplied quarter-note beat.
2364 * @param quarter_note the quarter-note position.
2365 * @return the frame position of the supplied quarter-note. Ignores meter.
2370 TempoMap::frame_at_quarter_note (const double quarter_note) const
2372 Glib::Threads::RWLock::ReaderLock lm (lock);
2374 const framepos_t ret = frame_at_minute (minute_at_quarter_note_locked (_metrics, quarter_note));
2380 TempoMap::minute_at_quarter_note_locked (const Metrics& metrics, const double quarter_note) const
2382 const double ret = minute_at_pulse_locked (metrics, quarter_note / 4.0);
2387 /** Returns the quarter-note beats corresponding to the supplied BBT (meter-based) beat.
2388 * @param beat The BBT (meter-based) beat.
2389 * @return The quarter-note position of the supplied BBT (meter-based) beat.
2391 * a quarter-note may be compared with and assigned to Evoral::Beats.
2395 TempoMap::quarter_note_at_beat (const double beat) const
2397 Glib::Threads::RWLock::ReaderLock lm (lock);
2399 const double ret = quarter_note_at_beat_locked (_metrics, beat);
2405 TempoMap::quarter_note_at_beat_locked (const Metrics& metrics, const double beat) const
2407 const double ret = pulse_at_beat_locked (metrics, beat) * 4.0;
2412 /** Returns the BBT (meter-based) beat position corresponding to the supplied quarter-note beats.
2413 * @param quarter_note The position in quarter-note beats.
2414 * @return the BBT (meter-based) beat position of the supplied quarter-note beats.
2416 * a quarter-note is the musical unit of Evoral::Beats.
2420 TempoMap::beat_at_quarter_note (const double quarter_note) const
2422 Glib::Threads::RWLock::ReaderLock lm (lock);
2424 const double ret = beat_at_quarter_note_locked (_metrics, quarter_note);
2430 TempoMap::beat_at_quarter_note_locked (const Metrics& metrics, const double quarter_note) const
2433 return beat_at_pulse_locked (metrics, quarter_note / 4.0);
2436 /** Returns the duration in frames between two supplied quarter-note beat positions.
2437 * @param start the first position in quarter-note beats.
2438 * @param end the end position in quarter-note beats.
2439 * @return the frame distance ober the quarter-note beats duration.
2441 * use this rather than e.g.
2442 * frame_at-quarter_note (end_beats) - frame_at_quarter_note (start_beats).
2443 * frames_between_quarter_notes() doesn't round to audio frames as an intermediate step,
2447 TempoMap::frames_between_quarter_notes (const double start, const double end) const
2449 Glib::Threads::RWLock::ReaderLock lm (lock);
2451 return frame_at_minute (minutes_between_quarter_notes_locked (_metrics, start, end));
2455 TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end) const
2458 return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
2462 TempoMap::quarter_notes_between_frames (const framecnt_t start, const framecnt_t end) const
2464 Glib::Threads::RWLock::ReaderLock lm (lock);
2466 return quarter_notes_between_frames_locked (_metrics, start, end);
2470 TempoMap::quarter_notes_between_frames_locked (const Metrics& metrics, const framecnt_t start, const framecnt_t end) const
2472 const TempoSection* prev_t = 0;
2474 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2477 if ((*i)->is_tempo()) {
2478 t = static_cast<TempoSection*> (*i);
2482 if (prev_t && t->frame() > start) {
2489 const double start_qn = prev_t->pulse_at_frame (start);
2491 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2494 if ((*i)->is_tempo()) {
2495 t = static_cast<TempoSection*> (*i);
2499 if (prev_t && t->frame() > end) {
2505 const double end_qn = prev_t->pulse_at_frame (end);
2507 return (end_qn - start_qn) * 4.0;
2511 TempoMap::check_solved (const Metrics& metrics) const
2513 TempoSection* prev_t = 0;
2514 MeterSection* prev_m = 0;
2516 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2519 if ((*i)->is_tempo()) {
2520 t = static_cast<TempoSection*> (*i);
2525 /* check ordering */
2526 if ((t->minute() <= prev_t->minute()) || (t->pulse() <= prev_t->pulse())) {
2530 /* precision check ensures tempo and frames align.*/
2531 if (t->frame() != frame_at_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()))) {
2532 if (!t->locked_to_meter()) {
2537 /* gradient limit - who knows what it should be?
2538 things are also ok (if a little chaotic) without this
2540 if (fabs (prev_t->c_func()) > 1000.0) {
2541 //std::cout << "c : " << prev_t->c_func() << std::endl;
2548 if (!(*i)->is_tempo()) {
2549 m = static_cast<MeterSection*> (*i);
2550 if (prev_m && m->position_lock_style() == AudioTime) {
2551 const TempoSection* t = &tempo_section_at_minute_locked (metrics, minute_at_frame (m->frame() - 1));
2552 const double nascent_m_minute = t->minute_at_pulse (m->pulse());
2553 /* Here we check that a preceding section of music doesn't overlap a subsequent one.
2555 if (t && (nascent_m_minute > m->minute() || nascent_m_minute < 0.0)) {
2569 TempoMap::set_active_tempi (const Metrics& metrics, const framepos_t& frame)
2571 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2573 if ((*i)->is_tempo()) {
2574 t = static_cast<TempoSection*> (*i);
2576 t->set_active (true);
2578 } else if (t->position_lock_style() == AudioTime) {
2579 if (t->active () && t->frame() < frame) {
2580 t->set_active (false);
2582 } else if (t->frame() > frame) {
2583 t->set_active (true);
2584 } else if (t->frame() == frame) {
2594 TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const double& minute)
2596 TempoSection* prev_t = 0;
2597 TempoSection* section_prev = 0;
2598 double first_m_minute = 0.0;
2600 /* can't move a tempo before the first meter */
2601 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2603 if (!(*i)->is_tempo()) {
2604 m = static_cast<MeterSection*> (*i);
2606 first_m_minute = m->minute();
2611 if (!section->initial() && minute <= first_m_minute) {
2615 section->set_active (true);
2616 section->set_minute (minute);
2618 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2620 if ((*i)->is_tempo()) {
2621 t = static_cast<TempoSection*> (*i);
2628 section_prev = prev_t;
2629 if (t->locked_to_meter()) {
2635 if (t->frame() == frame_at_minute (minute)) {
2639 if (t->position_lock_style() == MusicTime) {
2640 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->note_types_per_minute(), t->pulse()));
2641 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
2643 prev_t->set_c_func (prev_t->compute_c_func_minute (t->note_types_per_minute(), t->minute()));
2644 if (!t->locked_to_meter()) {
2645 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
2654 section_prev->set_c_func (section_prev->compute_c_func_minute (section->note_types_per_minute(), minute));
2655 if (!section->locked_to_meter()) {
2656 section->set_pulse (section_prev->pulse_at_ntpm (section->note_types_per_minute(), minute));
2661 recompute_tempi (imaginary);
2663 if (check_solved (imaginary)) {
2666 dunp (imaginary, std::cout);
2670 MetricSectionFrameSorter fcmp;
2671 imaginary.sort (fcmp);
2673 recompute_tempi (imaginary);
2675 if (check_solved (imaginary)) {
2683 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2685 TempoSection* prev_t = 0;
2686 TempoSection* section_prev = 0;
2688 section->set_pulse (pulse);
2690 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2692 if ((*i)->is_tempo()) {
2693 t = static_cast<TempoSection*> (*i);
2704 section_prev = prev_t;
2707 if (t->position_lock_style() == MusicTime) {
2708 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->note_types_per_minute(), t->pulse()));
2709 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
2711 prev_t->set_c_func (prev_t->compute_c_func_minute (t->note_types_per_minute(), t->minute()));
2712 if (!t->locked_to_meter()) {
2713 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
2722 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->note_types_per_minute(), pulse));
2723 section->set_minute (section_prev->minute_at_ntpm (section->note_types_per_minute(), pulse));
2727 recompute_tempi (imaginary);
2729 if (check_solved (imaginary)) {
2732 dunp (imaginary, std::cout);
2736 MetricSectionSorter cmp;
2737 imaginary.sort (cmp);
2739 recompute_tempi (imaginary);
2741 * XX need a restriction here, but only for this case,
2742 * as audio locked tempos don't interact in the same way.
2744 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2746 * |50 bpm |250 bpm |60 bpm
2747 * drag 250 to the pulse after 60->
2748 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2750 if (check_solved (imaginary)) {
2758 TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const double& minute)
2760 /* disallow moving first meter past any subsequent one, and any initial meter before the first one */
2761 const MeterSection* other = &meter_section_at_minute_locked (imaginary, minute);
2762 if ((section->initial() && !other->initial()) || (other->initial() && !section->initial() && other->minute() >= minute)) {
2766 if (section->initial()) {
2767 /* lock the first tempo to our first meter */
2768 if (!set_active_tempi (imaginary, section->frame_at_minute (minute))) {
2773 TempoSection* meter_locked_tempo = 0;
2775 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2777 if ((*ii)->is_tempo()) {
2778 t = static_cast<TempoSection*> (*ii);
2779 if ((t->locked_to_meter() || t->initial()) && t->minute() == section->minute()) {
2780 meter_locked_tempo = t;
2786 if (!meter_locked_tempo) {
2790 MeterSection* prev_m = 0;
2792 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2793 bool solved = false;
2795 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2797 if (!(*i)->is_tempo()) {
2798 m = static_cast<MeterSection*> (*i);
2800 if (prev_m && !section->initial()) {
2801 const double beats = (pulse_at_minute_locked (imaginary, minute) - prev_m->pulse()) * prev_m->note_divisor();
2802 if (beats + prev_m->beat() < section->beat()) {
2803 /* set the section pulse according to its musical position,
2804 * as an earlier time than this has been requested.
2806 const double new_pulse = ((section->beat() - prev_m->beat())
2807 / prev_m->note_divisor()) + prev_m->pulse();
2809 tempo_copy->set_position_lock_style (MusicTime);
2810 if ((solved = solve_map_pulse (future_map, tempo_copy, new_pulse))) {
2811 meter_locked_tempo->set_position_lock_style (MusicTime);
2812 section->set_position_lock_style (MusicTime);
2813 section->set_pulse (new_pulse);
2814 solve_map_pulse (imaginary, meter_locked_tempo, new_pulse);
2815 meter_locked_tempo->set_position_lock_style (AudioTime);
2816 section->set_position_lock_style (AudioTime);
2817 section->set_minute (meter_locked_tempo->minute());
2823 Metrics::const_iterator d = future_map.begin();
2824 while (d != future_map.end()) {
2833 /* all is ok. set section's locked tempo if allowed.
2834 possibly disallowed if there is an adjacent audio-locked tempo.
2835 XX this check could possibly go. its never actually happened here.
2837 MeterSection* meter_copy = const_cast<MeterSection*>
2838 (&meter_section_at_minute_locked (future_map, section->minute()));
2840 meter_copy->set_minute (minute);
2842 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2843 section->set_minute (minute);
2844 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2845 / prev_m->note_divisor()) + prev_m->pulse());
2846 solve_map_minute (imaginary, meter_locked_tempo, minute);
2851 Metrics::const_iterator d = future_map.begin();
2852 while (d != future_map.end()) {
2862 /* initial (first meter atm) */
2864 tempo_copy->set_minute (minute);
2865 tempo_copy->set_pulse (0.0);
2867 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2868 section->set_minute (minute);
2869 meter_locked_tempo->set_minute (minute);
2870 meter_locked_tempo->set_pulse (0.0);
2871 solve_map_minute (imaginary, meter_locked_tempo, minute);
2876 Metrics::const_iterator d = future_map.begin();
2877 while (d != future_map.end()) {
2886 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2887 section->set_beat (b_bbt);
2888 section->set_pulse (0.0);
2898 MetricSectionFrameSorter fcmp;
2899 imaginary.sort (fcmp);
2901 recompute_meters (imaginary);
2907 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2909 /* disallow setting section to an existing meter's bbt */
2910 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2912 if (!(*i)->is_tempo()) {
2913 m = static_cast<MeterSection*> (*i);
2914 if (m != section && m->bbt().bars == when.bars) {
2920 MeterSection* prev_m = 0;
2921 MeterSection* section_prev = 0;
2923 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2925 if (!(*i)->is_tempo()) {
2926 m = static_cast<MeterSection*> (*i);
2932 pair<double, BBT_Time> b_bbt;
2933 double new_pulse = 0.0;
2935 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2936 section_prev = prev_m;
2938 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2939 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2940 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2942 section->set_beat (b_bbt);
2943 section->set_pulse (pulse);
2944 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
2948 if (m->position_lock_style() == AudioTime) {
2949 TempoSection* meter_locked_tempo = 0;
2951 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2953 if ((*ii)->is_tempo()) {
2954 t = static_cast<TempoSection*> (*ii);
2955 if ((t->locked_to_meter() || t->initial()) && t->frame() == m->frame()) {
2956 meter_locked_tempo = t;
2962 if (!meter_locked_tempo) {
2967 double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2969 if (beats + prev_m->beat() != m->beat()) {
2970 /* tempo/ meter change caused a change in beat (bar). */
2972 /* the user has requested that the previous section of music overlaps this one.
2973 we have no choice but to change the bar number here, as being locked to audio means
2974 we must stay where we are on the timeline.
2976 beats = m->beat() - prev_m->beat();
2977 b_bbt = make_pair (beats + prev_m->beat()
2978 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2979 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2981 } else if (!m->initial()) {
2982 b_bbt = make_pair (m->beat(), m->bbt());
2983 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2986 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2989 meter_locked_tempo->set_pulse (new_pulse);
2990 m->set_beat (b_bbt);
2991 m->set_pulse (new_pulse);
2995 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2996 if (beats + prev_m->beat() != m->beat()) {
2997 /* tempo/ meter change caused a change in beat (bar). */
2998 b_bbt = make_pair (beats + prev_m->beat()
2999 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
3001 b_bbt = make_pair (beats + prev_m->beat()
3004 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3005 m->set_beat (b_bbt);
3006 m->set_pulse (new_pulse);
3007 m->set_minute (minute_at_pulse_locked (imaginary, new_pulse));
3014 if (!section_prev) {
3016 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
3017 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
3018 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
3020 section->set_beat (b_bbt);
3021 section->set_pulse (pulse);
3022 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
3025 MetricSectionSorter cmp;
3026 imaginary.sort (cmp);
3028 recompute_meters (imaginary);
3033 /** places a copy of _metrics into copy and returns a pointer
3034 * to section's equivalent in copy.
3037 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
3039 TempoSection* ret = 0;
3041 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3044 if ((*i)->is_tempo()) {
3045 t = static_cast<TempoSection*> (*i);
3047 ret = new TempoSection (*t);
3048 copy.push_back (ret);
3052 TempoSection* cp = new TempoSection (*t);
3053 copy.push_back (cp);
3055 if (!(*i)->is_tempo()) {
3056 m = static_cast<MeterSection *> (*i);
3057 MeterSection* cp = new MeterSection (*m);
3058 copy.push_back (cp);
3066 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
3068 MeterSection* ret = 0;
3070 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3073 if ((*i)->is_tempo()) {
3074 t = static_cast<TempoSection*> (*i);
3075 TempoSection* cp = new TempoSection (*t);
3076 copy.push_back (cp);
3079 if (!(*i)->is_tempo()) {
3080 m = static_cast<MeterSection *> (*i);
3082 ret = new MeterSection (*m);
3083 copy.push_back (ret);
3086 MeterSection* cp = new MeterSection (*m);
3087 copy.push_back (cp);
3094 /** answers the question "is this a valid beat position for this tempo section?".
3095 * it returns true if the tempo section can be moved to the requested bbt position,
3096 * leaving the tempo map in a solved state.
3097 * @param ts the tempo section to be moved
3098 * @param bbt the requested new position for the tempo section
3099 * @return true if the tempo section can be moved to the position, otherwise false.
3102 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
3105 TempoSection* tempo_copy = 0;
3108 Glib::Threads::RWLock::ReaderLock lm (lock);
3109 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
3115 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
3117 Metrics::const_iterator d = copy.begin();
3118 while (d != copy.end()) {
3127 * 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,
3128 * taking any possible reordering as a consequence of this into account.
3129 * @param section - the section to be altered
3130 * @param bbt - the BBT time where the altered tempo will fall
3131 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
3133 pair<double, framepos_t>
3134 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
3137 pair<double, framepos_t> ret = make_pair (0.0, 0);
3139 Glib::Threads::RWLock::ReaderLock lm (lock);
3141 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
3143 const double beat = beat_at_bbt_locked (future_map, bbt);
3145 if (section->position_lock_style() == AudioTime) {
3146 tempo_copy->set_position_lock_style (MusicTime);
3149 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
3150 ret.first = tempo_copy->pulse();
3151 ret.second = tempo_copy->frame();
3153 ret.first = section->pulse();
3154 ret.second = section->frame();
3157 Metrics::const_iterator d = future_map.begin();
3158 while (d != future_map.end()) {
3165 /** moves a TempoSection to a specified position.
3166 * @param ts - the section to be moved
3167 * @param frame - the new position in frames for the tempo
3168 * @param sub_num - the snap division to use if using musical time.
3170 * if sub_num is non-zero, the frame position is used to calculate an exact
3173 * -1 | snap to bars (meter-based)
3174 * 0 | no snap - use audio frame for musical position
3175 * 1 | snap to meter-based (BBT) beat
3176 * >1 | snap to quarter-note subdivision (i.e. 4 will snap to sixteenth notes)
3178 * this follows the snap convention in the gui.
3179 * if sub_num is zero, the musical position will be taken from the supplied frame.
3182 TempoMap::gui_move_tempo (TempoSection* ts, const framepos_t& frame, const int& sub_num)
3186 if (ts->position_lock_style() == MusicTime) {
3188 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
3189 Glib::Threads::RWLock::WriterLock lm (lock);
3190 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3192 tempo_copy->set_position_lock_style (AudioTime);
3194 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3195 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
3196 const double pulse = pulse_at_beat_locked (future_map, beat);
3198 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3199 solve_map_pulse (_metrics, ts, pulse);
3200 recompute_meters (_metrics);
3208 Glib::Threads::RWLock::WriterLock lm (lock);
3209 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3211 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3213 /* We're moving the object that defines the grid while snapping to it...
3214 * Placing the ts at the beat corresponding to the requested frame may shift the
3215 * grid in such a way that the mouse is left hovering over a completerly different division,
3216 * causing jittering when the mouse next moves (esp. large tempo deltas).
3218 * This alters the snap behaviour slightly in that we snap to beat divisions
3219 * in the future map rather than the existing one.
3221 const double qn = exact_qn_at_frame_locked (future_map, frame, sub_num);
3222 const framepos_t snapped_frame = frame_at_minute (minute_at_quarter_note_locked (future_map, qn));
3224 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (snapped_frame))) {
3225 solve_map_minute (_metrics, ts, minute_at_frame (snapped_frame));
3226 ts->set_pulse (qn / 4.0);
3227 recompute_meters (_metrics);
3230 solve_map_minute (_metrics, ts, minute_at_frame (frame));
3231 recompute_meters (_metrics);
3237 Metrics::const_iterator d = future_map.begin();
3238 while (d != future_map.end()) {
3243 MetricPositionChanged (); // Emit Signal
3246 /** moves a MeterSection to a specified position.
3247 * @param ms - the section to be moved
3248 * @param frame - the new position in frames for the meter
3250 * as a meter cannot snap to anything but bars,
3251 * the supplied frame is rounded to the nearest bar, possibly
3252 * leaving the meter position unchanged.
3255 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
3259 if (ms->position_lock_style() == AudioTime) {
3262 Glib::Threads::RWLock::WriterLock lm (lock);
3263 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3265 if (solve_map_minute (future_map, copy, minute_at_frame (frame))) {
3266 solve_map_minute (_metrics, ms, minute_at_frame (frame));
3267 recompute_tempi (_metrics);
3272 Glib::Threads::RWLock::WriterLock lm (lock);
3273 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3275 const double beat = beat_at_minute_locked (_metrics, minute_at_frame (frame));
3276 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
3278 if (solve_map_bbt (future_map, copy, bbt)) {
3279 solve_map_bbt (_metrics, ms, bbt);
3280 recompute_tempi (_metrics);
3285 Metrics::const_iterator d = future_map.begin();
3286 while (d != future_map.end()) {
3291 MetricPositionChanged (); // Emit Signal
3295 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
3298 bool can_solve = false;
3300 Glib::Threads::RWLock::WriterLock lm (lock);
3301 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3302 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3303 recompute_tempi (future_map);
3305 if (check_solved (future_map)) {
3306 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3307 recompute_map (_metrics);
3312 Metrics::const_iterator d = future_map.begin();
3313 while (d != future_map.end()) {
3318 MetricPositionChanged (); // Emit Signal
3324 TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame)
3327 Ts (future prev_t) Tnext
3330 |----------|----------
3337 Glib::Threads::RWLock::WriterLock lm (lock);
3343 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3344 TempoSection* prev_to_prev_t = 0;
3345 const frameoffset_t fr_off = end_frame - frame;
3349 if (prev_t->pulse() > 0.0) {
3350 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_minute_locked (future_map, minute_at_frame (prev_t->frame() - 1)));
3353 TempoSection* next_t = 0;
3354 for (Metrics::iterator i = future_map.begin(); i != future_map.end(); ++i) {
3355 TempoSection* t = 0;
3356 if ((*i)->is_tempo()) {
3357 t = static_cast<TempoSection*> (*i);
3358 if (t->frame() > ts->frame()) {
3364 /* minimum allowed measurement distance in frames */
3365 const framepos_t min_dframe = 2;
3367 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
3368 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
3370 double contribution = 0.0;
3372 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3373 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3376 const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
3378 const double start_pulse = prev_t->pulse_at_minute (minute_at_frame (frame));
3379 const double end_pulse = prev_t->pulse_at_minute (minute_at_frame (end_frame));
3383 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
3385 if (prev_t->position_lock_style() == MusicTime) {
3386 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3387 if (frame > prev_to_prev_t->frame() + min_dframe && (frame + prev_t_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
3389 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame())
3390 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
3392 new_bpm = prev_t->note_types_per_minute();
3395 /* prev to prev is irrelevant */
3397 if (start_pulse > prev_t->pulse() && end_pulse > prev_t->pulse()) {
3398 new_bpm = prev_t->note_types_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
3400 new_bpm = prev_t->note_types_per_minute();
3405 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3406 if (frame > prev_to_prev_t->frame() + min_dframe && end_frame > prev_to_prev_t->frame() + min_dframe) {
3408 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame())
3409 / (double) ((end_frame) - prev_to_prev_t->frame()));
3411 new_bpm = prev_t->note_types_per_minute();
3414 /* prev_to_prev_t is irrelevant */
3416 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3417 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
3419 new_bpm = prev_t->note_types_per_minute();
3425 double frame_ratio = 1.0;
3426 double pulse_ratio = 1.0;
3427 const double pulse_pos = frame;
3429 if (prev_to_prev_t) {
3430 if (pulse_pos > prev_to_prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_to_prev_t->frame() + min_dframe) {
3431 frame_ratio = (((pulse_pos - fr_off) - prev_to_prev_t->frame()) / (double) ((pulse_pos) - prev_to_prev_t->frame()));
3433 if (end_pulse > prev_to_prev_t->pulse() && start_pulse > prev_to_prev_t->pulse()) {
3434 pulse_ratio = ((start_pulse - prev_to_prev_t->pulse()) / (end_pulse - prev_to_prev_t->pulse()));
3437 if (pulse_pos > prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_t->frame() + min_dframe) {
3438 frame_ratio = (((pulse_pos - fr_off) - prev_t->frame()) / (double) ((pulse_pos) - prev_t->frame()));
3440 pulse_ratio = (start_pulse / end_pulse);
3442 new_bpm = prev_t->note_types_per_minute() * (pulse_ratio * frame_ratio);
3445 /* don't clamp and proceed here.
3446 testing has revealed that this can go negative,
3447 which is an entirely different thing to just being too low.
3449 if (new_bpm < 0.5) {
3452 new_bpm = min (new_bpm, (double) 1000.0);
3453 prev_t->set_note_types_per_minute (new_bpm);
3454 recompute_tempi (future_map);
3455 recompute_meters (future_map);
3457 if (check_solved (future_map)) {
3458 ts->set_note_types_per_minute (new_bpm);
3459 recompute_tempi (_metrics);
3460 recompute_meters (_metrics);
3464 Metrics::const_iterator d = future_map.begin();
3465 while (d != future_map.end()) {
3470 MetricPositionChanged (); // Emit Signal
3473 /** Returns the exact bbt-based beat corresponding to the bar, beat or quarter note subdivision nearest to
3474 * the supplied frame, possibly returning a negative value.
3476 * @param frame The session frame position.
3477 * @param sub_num The subdivision to use when rounding the beat.
3478 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3479 * Positive integers indicate quarter note (non BBT) divisions.
3480 * 0 indicates that the returned beat should not be rounded (equivalent to quarter_note_at_frame()).
3481 * @return The beat position of the supplied frame.
3483 * when working to a musical grid, the use of sub_nom indicates that
3484 * the position should be interpreted musically.
3486 * it effectively snaps to meter bars, meter beats or quarter note divisions
3487 * (as per current gui convention) and returns a musical position independent of frame rate.
3489 * If the supplied frame lies before the first meter, the return will be negative,
3490 * in which case the returned beat uses the first meter (for BBT subdivisions) and
3491 * the continuation of the tempo curve (backwards).
3493 * This function is sensitive to tempo and meter.
3496 TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num) const
3498 Glib::Threads::RWLock::ReaderLock lm (lock);
3500 return exact_beat_at_frame_locked (_metrics, frame, sub_num);
3504 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t divisions) const
3506 return beat_at_pulse_locked (_metrics, exact_qn_at_frame_locked (metrics, frame, divisions) / 4.0);
3509 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
3510 * the supplied frame, possibly returning a negative value.
3512 * @param frame The session frame position.
3513 * @param sub_num The subdivision to use when rounding the quarter note.
3514 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3515 * Positive integers indicate quarter note (non BBT) divisions.
3516 * 0 indicates that the returned quarter note should not be rounded (equivalent to quarter_note_at_frame()).
3517 * @return The quarter note position of the supplied frame.
3519 * When working to a musical grid, the use of sub_nom indicates that
3520 * the frame position should be interpreted musically.
3522 * it effectively snaps to meter bars, meter beats or quarter note divisions
3523 * (as per current gui convention) and returns a musical position independent of frame rate.
3525 * If the supplied frame lies before the first meter, the return will be negative,
3526 * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
3527 * the continuation of the tempo curve (backwards).
3529 * This function is tempo-sensitive.
3532 TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num) const
3534 Glib::Threads::RWLock::ReaderLock lm (lock);
3536 return exact_qn_at_frame_locked (_metrics, frame, sub_num);
3540 TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num) const
3542 double qn = quarter_note_at_minute_locked (metrics, minute_at_frame (frame));
3545 qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
3546 } else if (sub_num == 1) {
3547 /* the gui requested exact musical (BBT) beat */
3548 qn = quarter_note_at_beat_locked (metrics, floor (beat_at_minute_locked (metrics, minute_at_frame (frame)) + 0.5));
3549 } else if (sub_num == -1) {
3551 Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
3555 const double prev_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3557 const double next_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3559 if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
3569 /** returns the frame duration of the supplied BBT time at a specified frame position in the tempo map.
3570 * @param pos the frame position in the tempo map.
3571 * @param bbt the distance in BBT time from pos to calculate.
3572 * @param dir the rounding direction..
3573 * @return the duration in frames between pos and bbt
3576 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
3578 Glib::Threads::RWLock::ReaderLock lm (lock);
3580 BBT_Time pos_bbt = bbt_at_minute_locked (_metrics, minute_at_frame (pos));
3582 const double divisions = meter_section_at_minute_locked (_metrics, minute_at_frame (pos)).divisions_per_bar();
3585 pos_bbt.bars += bbt.bars;
3587 pos_bbt.ticks += bbt.ticks;
3588 if ((double) pos_bbt.ticks > BBT_Time::ticks_per_beat) {
3590 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3593 pos_bbt.beats += bbt.beats;
3594 if ((double) pos_bbt.beats > divisions) {
3596 pos_bbt.beats -= divisions;
3598 const framecnt_t pos_bbt_frame = frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3600 return pos_bbt_frame - pos;
3604 if (pos_bbt.bars <= bbt.bars) {
3607 pos_bbt.bars -= bbt.bars;
3610 if (pos_bbt.ticks < bbt.ticks) {
3611 if (pos_bbt.bars > 1) {
3612 if (pos_bbt.beats == 1) {
3614 pos_bbt.beats = divisions;
3618 pos_bbt.ticks = BBT_Time::ticks_per_beat - (bbt.ticks - pos_bbt.ticks);
3624 pos_bbt.ticks -= bbt.ticks;
3627 if (pos_bbt.beats <= bbt.beats) {
3628 if (pos_bbt.bars > 1) {
3630 pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
3635 pos_bbt.beats -= bbt.beats;
3638 return pos - frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3645 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
3647 return round_to_type (fr, dir, Bar);
3651 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
3653 return round_to_type (fr, dir, Beat);
3657 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3659 Glib::Threads::RWLock::ReaderLock lm (lock);
3660 uint32_t ticks = (uint32_t) floor (max (0.0, beat_at_minute_locked (_metrics, minute_at_frame (fr))) * BBT_Time::ticks_per_beat);
3661 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3662 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3664 ticks -= beats * BBT_Time::ticks_per_beat;
3667 /* round to next (or same iff dir == RoundUpMaybe) */
3669 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3671 if (mod == 0 && dir == RoundUpMaybe) {
3672 /* right on the subdivision, which is fine, so do nothing */
3674 } else if (mod == 0) {
3675 /* right on the subdivision, so the difference is just the subdivision ticks */
3676 ticks += ticks_one_subdivisions_worth;
3679 /* not on subdivision, compute distance to next subdivision */
3681 ticks += ticks_one_subdivisions_worth - mod;
3684 if (ticks >= BBT_Time::ticks_per_beat) {
3685 ticks -= BBT_Time::ticks_per_beat;
3687 } else if (dir < 0) {
3689 /* round to previous (or same iff dir == RoundDownMaybe) */
3691 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3693 if (difference == 0 && dir == RoundDownAlways) {
3694 /* right on the subdivision, but force-rounding down,
3695 so the difference is just the subdivision ticks */
3696 difference = ticks_one_subdivisions_worth;
3699 if (ticks < difference) {
3700 ticks = BBT_Time::ticks_per_beat - ticks;
3702 ticks -= difference;
3706 /* round to nearest */
3709 /* compute the distance to the previous and next subdivision */
3711 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3713 /* closer to the next subdivision, so shift forward */
3715 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3717 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3719 if (ticks > BBT_Time::ticks_per_beat) {
3721 ticks -= BBT_Time::ticks_per_beat;
3722 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3725 } else if (rem > 0) {
3727 /* closer to previous subdivision, so shift backward */
3731 /* can't go backwards past zero, so ... */
3734 /* step back to previous beat */
3736 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3737 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3739 ticks = lrint (ticks - rem);
3740 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3743 /* on the subdivision, do nothing */
3747 const framepos_t ret_frame = frame_at_minute (minute_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat)));
3753 TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3755 Glib::Threads::RWLock::ReaderLock lm (lock);
3756 uint32_t ticks = (uint32_t) floor (max (0.0, quarter_note_at_minute_locked (_metrics, minute_at_frame (fr))) * BBT_Time::ticks_per_beat);
3757 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3758 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3760 ticks -= beats * BBT_Time::ticks_per_beat;
3763 /* round to next (or same iff dir == RoundUpMaybe) */
3765 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3767 if (mod == 0 && dir == RoundUpMaybe) {
3768 /* right on the subdivision, which is fine, so do nothing */
3770 } else if (mod == 0) {
3771 /* right on the subdivision, so the difference is just the subdivision ticks */
3772 ticks += ticks_one_subdivisions_worth;
3775 /* not on subdivision, compute distance to next subdivision */
3777 ticks += ticks_one_subdivisions_worth - mod;
3780 if (ticks >= BBT_Time::ticks_per_beat) {
3781 ticks -= BBT_Time::ticks_per_beat;
3783 } else if (dir < 0) {
3785 /* round to previous (or same iff dir == RoundDownMaybe) */
3787 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3789 if (difference == 0 && dir == RoundDownAlways) {
3790 /* right on the subdivision, but force-rounding down,
3791 so the difference is just the subdivision ticks */
3792 difference = ticks_one_subdivisions_worth;
3795 if (ticks < difference) {
3796 ticks = BBT_Time::ticks_per_beat - ticks;
3798 ticks -= difference;
3802 /* round to nearest */
3805 /* compute the distance to the previous and next subdivision */
3807 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3809 /* closer to the next subdivision, so shift forward */
3811 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3813 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3815 if (ticks > BBT_Time::ticks_per_beat) {
3817 ticks -= BBT_Time::ticks_per_beat;
3818 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3821 } else if (rem > 0) {
3823 /* closer to previous subdivision, so shift backward */
3827 /* can't go backwards past zero, so ... */
3830 /* step back to previous beat */
3832 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3833 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3835 ticks = lrint (ticks - rem);
3836 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3839 /* on the subdivision, do nothing */
3843 const framepos_t ret_frame = frame_at_minute (minute_at_quarter_note_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat)));
3849 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
3851 Glib::Threads::RWLock::ReaderLock lm (lock);
3853 const double beat_at_framepos = max (0.0, beat_at_minute_locked (_metrics, minute_at_frame (frame)));
3854 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
3859 /* find bar previous to 'frame' */
3862 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3864 } else if (dir > 0) {
3865 /* find bar following 'frame' */
3869 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3871 /* true rounding: find nearest bar */
3872 framepos_t raw_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3875 framepos_t prev_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3877 framepos_t next_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3879 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
3890 return frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos)));
3891 } else if (dir > 0) {
3892 return frame_at_minute (minute_at_beat_locked (_metrics, ceil (beat_at_framepos)));
3894 return frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5)));
3903 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
3904 framepos_t lower, framepos_t upper, uint32_t bar_mod)
3906 Glib::Threads::RWLock::ReaderLock lm (lock);
3907 int32_t cnt = ceil (beat_at_minute_locked (_metrics, minute_at_frame (lower)));
3909 /* although the map handles negative beats, bbt doesn't. */
3914 if (minute_at_beat_locked (_metrics, cnt) >= minute_at_frame (upper)) {
3918 while (pos >= 0 && pos < upper) {
3919 pos = frame_at_minute (minute_at_beat_locked (_metrics, cnt));
3920 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
3921 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
3922 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
3923 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c_func()));
3927 BBT_Time bbt = bbt_at_minute_locked (_metrics, minute_at_frame (lower));
3932 bbt.bars -= bbt.bars % bar_mod;
3936 while (pos >= 0 && pos < upper) {
3937 pos = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3938 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
3939 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
3940 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c_func()));
3941 bbt.bars += bar_mod;
3947 TempoMap::tempo_section_at_frame (framepos_t frame) const
3949 Glib::Threads::RWLock::ReaderLock lm (lock);
3951 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
3955 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute) const
3957 TempoSection* prev = 0;
3961 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3963 if ((*i)->is_tempo()) {
3964 t = static_cast<TempoSection*> (*i);
3968 if (prev && t->minute() > minute) {
3978 abort(); /*NOTREACHED*/
3985 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3987 TempoSection* prev_t = 0;
3988 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
3992 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3993 if ((*i)->is_tempo()) {
3994 t = static_cast<TempoSection*> (*i);
3995 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
4005 /* don't use this to calculate length (the tempo is only correct for this frame).
4006 do that stuff based on the beat_at_frame and frame_at_beat api
4009 TempoMap::frames_per_quarter_note_at (const framepos_t& frame, const framecnt_t& sr) const
4011 Glib::Threads::RWLock::ReaderLock lm (lock);
4013 const TempoSection* ts_at = 0;
4014 const TempoSection* ts_after = 0;
4015 Metrics::const_iterator i;
4018 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4020 if ((*i)->is_tempo()) {
4021 t = static_cast<TempoSection*> (*i);
4025 if (ts_at && (*i)->frame() > frame) {
4035 return (60.0 * _frame_rate) / ts_at->tempo_at_minute (minute_at_frame (frame)).quarter_notes_per_minute();
4037 /* must be treated as constant tempo */
4038 return ts_at->frames_per_quarter_note (_frame_rate);
4042 TempoMap::meter_section_at_frame (framepos_t frame) const
4044 Glib::Threads::RWLock::ReaderLock lm (lock);
4045 return meter_section_at_minute_locked (_metrics, minute_at_frame (frame));
4049 TempoMap::meter_section_at_minute_locked (const Metrics& metrics, double minute) const
4051 Metrics::const_iterator i;
4052 MeterSection* prev = 0;
4056 for (i = metrics.begin(); i != metrics.end(); ++i) {
4058 if (!(*i)->is_tempo()) {
4059 m = static_cast<MeterSection*> (*i);
4061 if (prev && (*i)->minute() > minute) {
4071 abort(); /*NOTREACHED*/
4078 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
4080 MeterSection* prev_m = 0;
4082 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4084 if (!(*i)->is_tempo()) {
4085 m = static_cast<MeterSection*> (*i);
4086 if (prev_m && m->beat() > beat) {
4097 TempoMap::meter_section_at_beat (double beat) const
4099 Glib::Threads::RWLock::ReaderLock lm (lock);
4100 return meter_section_at_beat_locked (_metrics, beat);
4104 TempoMap::meter_at_frame (framepos_t frame) const
4106 TempoMetric m (metric_at (frame));
4111 TempoMap::fix_legacy_session ()
4113 MeterSection* prev_m = 0;
4114 TempoSection* prev_t = 0;
4116 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4120 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
4122 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
4125 m->set_minute (0.0);
4126 m->set_position_lock_style (AudioTime);
4131 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
4132 + (m->bbt().beats - 1)
4133 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
4135 m->set_beat (start);
4136 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
4137 + (m->bbt().beats - 1)
4138 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
4139 m->set_pulse (start_beat / prev_m->note_divisor());
4142 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4150 t->set_minute (0.0);
4151 t->set_position_lock_style (AudioTime);
4157 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
4158 + (t->legacy_bbt().beats - 1)
4159 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
4161 t->set_pulse (beat / prev_m->note_divisor());
4163 /* really shouldn't happen but.. */
4164 t->set_pulse (beat / 4.0);
4173 TempoMap::get_state ()
4175 Metrics::const_iterator i;
4176 XMLNode *root = new XMLNode ("TempoMap");
4179 Glib::Threads::RWLock::ReaderLock lm (lock);
4180 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4181 root->add_child_nocopy ((*i)->get_state());
4189 TempoMap::set_state (const XMLNode& node, int /*version*/)
4192 Glib::Threads::RWLock::WriterLock lm (lock);
4195 XMLNodeConstIterator niter;
4196 Metrics old_metrics (_metrics);
4199 nlist = node.children();
4201 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
4202 XMLNode* child = *niter;
4204 if (child->name() == TempoSection::xml_state_node_name) {
4207 TempoSection* ts = new TempoSection (*child, _frame_rate);
4208 _metrics.push_back (ts);
4211 catch (failed_constructor& err){
4212 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4213 _metrics = old_metrics;
4214 old_metrics.clear();
4218 } else if (child->name() == MeterSection::xml_state_node_name) {
4221 MeterSection* ms = new MeterSection (*child, _frame_rate);
4222 _metrics.push_back (ms);
4225 catch (failed_constructor& err) {
4226 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4227 _metrics = old_metrics;
4228 old_metrics.clear();
4234 if (niter == nlist.end()) {
4235 MetricSectionSorter cmp;
4236 _metrics.sort (cmp);
4239 /* check for legacy sessions where bbt was the base musical unit for tempo */
4240 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4242 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
4243 if (t->legacy_bbt().bars != 0) {
4244 fix_legacy_session();
4251 /* check for multiple tempo/meters at the same location, which
4252 ardour2 somehow allowed.
4255 Metrics::iterator prev = _metrics.end();
4256 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4257 if (prev != _metrics.end()) {
4259 MeterSection* prev_m;
4261 TempoSection* prev_t;
4262 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
4263 if (prev_m->pulse() == ms->pulse()) {
4264 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4265 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4268 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
4269 if (prev_t->pulse() == ts->pulse()) {
4270 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4271 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4279 recompute_map (_metrics);
4281 Metrics::const_iterator d = old_metrics.begin();
4282 while (d != old_metrics.end()) {
4286 old_metrics.clear ();
4289 PropertyChanged (PropertyChange ());
4295 TempoMap::dump (std::ostream& o) const
4297 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
4298 const MeterSection* m;
4299 const TempoSection* t;
4300 const TempoSection* prev_t = 0;
4302 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4304 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
4305 o << "Tempo @ " << *i << t->note_types_per_minute() << " BPM (pulse = 1/" << t->note_type()
4306 << " type= " << enum_2_string (t->type()) << ") " << " at pulse= " << t->pulse()
4307 << " minute= " << t->minute() << " frame= " << t->frame() << " (initial? " << t->initial() << ')'
4308 << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
4310 o << " current : " << t->note_types_per_minute()
4311 << " | " << t->pulse() << " | " << t->frame() << " | " << t->minute() << std::endl;
4312 o << " previous : " << prev_t->note_types_per_minute()
4313 << " | " << prev_t->pulse() << " | " << prev_t->frame() << " | " << prev_t->minute() << std::endl;
4314 o << " calculated : " << prev_t->tempo_at_pulse (t->pulse())
4315 << " | " << prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute())
4316 << " | " << frame_at_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()))
4317 << " | " << prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()) << std::endl;
4320 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
4321 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt()
4322 << " frame= " << m->frame() << " pulse: " << m->pulse() << " beat : " << m->beat()
4323 << " pos lock: " << enum_2_string (m->position_lock_style()) << " (initial? " << m->initial() << ')' << endl;
4326 o << "------" << std::endl;
4330 TempoMap::n_tempos() const
4332 Glib::Threads::RWLock::ReaderLock lm (lock);
4335 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4336 if ((*i)->is_tempo()) {
4345 TempoMap::n_meters() const
4347 Glib::Threads::RWLock::ReaderLock lm (lock);
4350 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4351 if (!(*i)->is_tempo()) {
4360 TempoMap::insert_time (framepos_t where, framecnt_t amount)
4362 for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
4363 if ((*i)->frame() >= where && !(*i)->initial ()) {
4367 if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
4368 gui_move_meter (ms, (*i)->frame() + amount);
4371 if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
4372 gui_move_tempo (ts, (*i)->frame() + amount, 0);
4377 PropertyChanged (PropertyChange ());
4381 TempoMap::remove_time (framepos_t where, framecnt_t amount)
4385 std::list<MetricSection*> metric_kill_list;
4387 TempoSection* last_tempo = NULL;
4388 MeterSection* last_meter = NULL;
4389 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
4390 bool meter_after = false; // is there a meter marker likewise?
4392 Glib::Threads::RWLock::WriterLock lm (lock);
4393 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4394 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
4395 metric_kill_list.push_back(*i);
4396 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
4399 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
4403 else if ((*i)->frame() >= where) {
4404 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
4405 (*i)->set_minute ((*i)->minute() - minute_at_frame (amount));
4406 if ((*i)->frame() == where) {
4407 // marker was immediately after end of range
4408 tempo_after = dynamic_cast<TempoSection*> (*i);
4409 meter_after = dynamic_cast<MeterSection*> (*i);
4415 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
4416 if (last_tempo && !tempo_after) {
4417 metric_kill_list.remove(last_tempo);
4418 last_tempo->set_minute (minute_at_frame (where));
4421 if (last_meter && !meter_after) {
4422 metric_kill_list.remove(last_meter);
4423 last_meter->set_minute (minute_at_frame (where));
4427 //remove all the remaining metrics
4428 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
4429 _metrics.remove(*i);
4434 recompute_map (_metrics);
4437 PropertyChanged (PropertyChange ());
4441 /** Add some (fractional) Beats to a session frame position, and return the result in frames.
4442 * pos can be -ve, if required.
4445 TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats beats) const
4447 Glib::Threads::RWLock::ReaderLock lm (lock);
4448 const double frame_qn = quarter_note_at_minute_locked (_metrics, minute_at_frame (frame));
4450 return frame_at_minute (minute_at_quarter_note_locked (_metrics, frame_qn + beats.to_double()));
4454 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
4456 Glib::Threads::RWLock::ReaderLock lm (lock);
4458 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_minute_locked (_metrics, minute_at_frame (pos)));
4459 pos_bbt.ticks += op.ticks;
4460 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
4462 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
4464 pos_bbt.beats += op.beats;
4465 /* the meter in effect will start on the bar */
4466 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();
4467 while (pos_bbt.beats >= divisions_per_bar + 1) {
4469 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4470 pos_bbt.beats -= divisions_per_bar;
4472 pos_bbt.bars += op.bars;
4474 return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
4477 /** Count the number of beats that are equivalent to distance when going forward,
4481 TempoMap::framewalk_to_qn (framepos_t pos, framecnt_t distance) const
4483 Glib::Threads::RWLock::ReaderLock lm (lock);
4485 return Evoral::Beats (quarter_notes_between_frames_locked (_metrics, pos, pos + distance));
4489 bool operator() (const BBT_Time& a, const BBT_Time& b) {
4495 operator<< (std::ostream& o, const Meter& m) {
4496 return o << m.divisions_per_bar() << '/' << m.note_divisor();
4500 operator<< (std::ostream& o, const Tempo& t) {
4501 return o << t.note_types_per_minute() << " 1/" << t.note_type() << "'s per minute";
4505 operator<< (std::ostream& o, const MetricSection& section) {
4507 o << "MetricSection @ " << section.frame() << ' ';
4509 const TempoSection* ts;
4510 const MeterSection* ms;
4512 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
4513 o << *((const Tempo*) ts);
4514 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
4515 o << *((const Meter*) ms);