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);
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 bool had_beats_per_minute = false;
102 _legacy_bbt = BBT_Time (0, 0, 0);
104 if ((prop = node.property ("start")) != 0) {
105 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
109 /* legacy session - start used to be in bbt*/
112 info << _("Legacy session detected. TempoSection XML node will be altered.") << endmsg;
116 if ((prop = node.property ("pulse")) != 0) {
117 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
118 error << _("TempoSection XML node has an illegal \"pulse\" value") << endmsg;
124 if ((prop = node.property ("frame")) != 0) {
125 if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) {
126 error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg;
128 set_minute (minute_at_frame (frame));
132 if ((prop = node.property ("minute")) != 0) {
133 if (sscanf (prop->value().c_str(), "%lf", &minute) != 1) {
134 error << _("TempoSection XML node has an illegal \"minute\" value") << endmsg;
140 /* replace old beats-per-minute with note-types-per-minute */
141 if ((prop = node.property ("beats-per-minute")) != 0) {
142 info << _("Renaming legacy \"beats-per-minute\" XML node to note-types-per-minute") << endmsg;
143 if (sscanf (prop->value().c_str(), "%lf", &_note_types_per_minute) != 1 || _note_types_per_minute < 0.0) {
144 error << _("TempoSection XML node has an illegal \"beats-per-minutee\" value") << endmsg;
145 throw failed_constructor();
147 had_beats_per_minute = true;
150 if ((prop = node.property ("note-types-per-minute")) != 0) {
151 if (sscanf (prop->value().c_str(), "%lf", &_note_types_per_minute) != 1 || _note_types_per_minute < 0.0) {
152 error << _("TempoSection XML node has an illegal \"note-types-per-minute\" value") << endmsg;
153 throw failed_constructor();
155 } else if (!had_beats_per_minute) {
156 error << _("TempoSection XML node has no \"note-types-per-minute\" or \"beats-per-minute\" property") << endmsg;
157 throw failed_constructor();
160 if ((prop = node.property ("note-type")) == 0) {
161 /* older session, make note type be quarter by default */
164 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
165 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
166 throw failed_constructor();
170 if ((prop = node.property ("movable")) == 0) {
171 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
172 throw failed_constructor();
175 set_movable (string_is_affirmative (prop->value()));
177 if ((prop = node.property ("active")) == 0) {
178 warning << _("TempoSection XML node has no \"active\" property") << endmsg;
181 set_active (string_is_affirmative (prop->value()));
184 if ((prop = node.property ("tempo-type")) == 0) {
187 _type = Type (string_2_enum (prop->value(), _type));
190 if ((prop = node.property ("lock-style")) == 0) {
192 set_position_lock_style (MusicTime);
194 set_position_lock_style (AudioTime);
197 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
200 if ((prop = node.property ("locked-to-meter")) == 0) {
201 set_locked_to_meter (false);
203 set_locked_to_meter (string_is_affirmative (prop->value()));
208 TempoSection::get_state() const
210 XMLNode *root = new XMLNode (xml_state_node_name);
214 snprintf (buf, sizeof (buf), "%lf", pulse());
215 root->add_property ("pulse", buf);
216 snprintf (buf, sizeof (buf), "%li", frame());
217 root->add_property ("frame", buf);
218 snprintf (buf, sizeof (buf), "%lf", minute());
219 root->add_property ("minute", buf);
220 snprintf (buf, sizeof (buf), "%lf", _note_types_per_minute);
221 root->add_property ("note-types-per-minute", buf);
222 snprintf (buf, sizeof (buf), "%lf", _note_type);
223 root->add_property ("note-type", buf);
224 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
225 root->add_property ("movable", buf);
226 snprintf (buf, sizeof (buf), "%s", active()?"yes":"no");
227 root->add_property ("active", buf);
228 root->add_property ("tempo-type", enum_2_string (_type));
229 root->add_property ("lock-style", enum_2_string (position_lock_style()));
230 root->add_property ("locked-to-meter", locked_to_meter()?"yes":"no");
236 TempoSection::set_type (Type type)
241 /** returns the Tempo at the session-relative minute.
244 TempoSection::tempo_at_minute (const double& m) const
247 if (_type == Constant || _c_func == 0.0) {
248 return Tempo (note_types_per_minute(), note_type());
251 return Tempo (_tempo_at_time (m - minute()), _note_type);
254 /** returns the session relative minute where the supplied tempo in note types per minute occurs.
255 * @param ntpm the tempo in mote types per minute used to calculate the returned minute
256 * @param p the pulse used to calculate the returned minute for constant tempi
257 * @return the minute at the supplied tempo
259 * note that the note_type is currently ignored in this function. see below.
263 /** user feedback dictates that if tempoA (120, 4.0) precedes tempoB (120, 8.0),
264 * there will be no ramp between the two even if set to ramped.
265 * in other words a ramp should only place a curve on note_types_per_minute.
266 * we should be able to use Tempo note type here, but the above
267 * complicates things a bit.
268 * we would ideally like to use arbitrary Tempo structs here.
271 TempoSection::minute_at_ntpm (const double& ntpm, const double& p) const
273 if (_type == Constant || _c_func == 0.0) {
274 return ((p - pulse()) / pulses_per_minute()) + minute();
277 return _time_at_tempo (ntpm) + minute();
280 /** returns the Tempo at the supplied whole-note pulse.
283 TempoSection::tempo_at_pulse (const double& p) const
286 if (_type == Constant || _c_func == 0.0) {
287 return Tempo (note_types_per_minute(), note_type());
290 return Tempo (_tempo_at_pulse (p - pulse()), _note_type);
293 /** returns the whole-note pulse where a tempo in note types per minute occurs.
294 * constant tempi require minute m.
295 * @param ntpm the note types per minute value used to calculate the returned pulse
296 * @param m the minute used to calculate the returned pulse if the tempo is constant
297 * @return the whole-note pulse at the supplied tempo
299 * note that note_type is currently ignored in this function. see minute_at_tempo().
301 * for constant tempi, this is anaologous to pulse_at_minute().
304 TempoSection::pulse_at_ntpm (const double& ntpm, const double& m) const
306 if (_type == Constant || _c_func == 0.0) {
307 return ((m - minute()) * pulses_per_minute()) + pulse();
310 return _pulse_at_tempo (ntpm) + pulse();
313 /** returns the whole-note pulse at the supplied session-relative minute.
316 TempoSection::pulse_at_minute (const double& m) const
318 if (_type == Constant || _c_func == 0.0) {
319 return ((m - minute()) * pulses_per_minute()) + pulse();
322 return _pulse_at_time (m - minute()) + pulse();
325 /** returns the session-relative minute at the supplied whole-note pulse.
328 TempoSection::minute_at_pulse (const double& p) const
330 if (_type == Constant || _c_func == 0.0) {
331 return ((p - pulse()) / pulses_per_minute()) + minute();
334 return _time_at_pulse (p - pulse()) + minute();
342 Tt----|-----------------*|
343 Ta----|--------------|* |
349 _______________|___|____
350 time a t (next tempo)
353 Duration in beats at time a is the integral of some Tempo function.
354 In our case, the Tempo function (Tempo at time t) is
357 with function constant
362 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
363 b(t) = T0(e^(ct) - 1) / c
365 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:
366 t(b) = log((c.b / T0) + 1) / c
368 The time t at which Tempo T occurs is a as above:
369 t(T) = log(T / T0) / c
371 The beat at which a Tempo T occurs is:
374 The Tempo at which beat b occurs is:
377 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
378 Our problem is that we usually don't know t.
379 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.
380 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
381 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
383 By substituting our expanded t as a in the c function above, our problem is reduced to:
384 c = T0 (e^(log (Ta / T0)) - 1) / b
386 Of course the word 'beat' has been left loosely defined above.
387 In music, a beat is defined by the musical pulse (which comes from the tempo)
388 and the meter in use at a particular time (how many pulse divisions there are in one bar).
389 It would be more accurate to substitute the work 'pulse' for 'beat' above.
393 We can now store c for future time calculations.
394 If the following tempo section (the one that defines c in conjunction with this one)
395 is changed or moved, c is no longer valid.
397 The public methods are session-relative.
399 Most of this stuff is taken from this paper:
402 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
405 Zurich University of Arts
406 Institute for Computer Music and Sound Technology
408 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
412 /** compute this ramp's function constant from some tempo-pulse point
413 * @param end_npm end tempo (in note types per minute)
414 * @param end_pulse duration (pulses into global start) of some other position.
415 * @return the calculated function constant
418 TempoSection::compute_c_func_pulse (const double& end_npm, const double& end_pulse) const
420 double const log_tempo_ratio = log (end_npm / note_types_per_minute());
421 return (note_types_per_minute() * expm1 (log_tempo_ratio)) / ((end_pulse - pulse()) * _note_type);
424 /** compute the function constant from some tempo-time point.
425 * @param end_npm tempo (note types/min.)
426 * @param end_minute distance (in minutes) from session origin
427 * @return the calculated function constant
430 TempoSection::compute_c_func_minute (const double& end_npm, const double& end_minute) const
432 return c_func (end_npm, end_minute - minute());
435 /* position function */
437 TempoSection::a_func (double end_npm, double c_func) const
439 return log (end_npm / note_types_per_minute()) / c_func;
442 /*function constant*/
444 TempoSection::c_func (double end_npm, double end_time) const
446 return log (end_npm / note_types_per_minute()) / end_time;
449 /* tempo in note types per minute at time in minutes */
451 TempoSection::_tempo_at_time (const double& time) const
453 return exp (_c_func * time) * note_types_per_minute();
456 /* time in minutes at tempo in note types per minute */
458 TempoSection::_time_at_tempo (const double& npm) const
460 return log (npm / note_types_per_minute()) / _c_func;
463 /* pulse at tempo in note types per minute */
465 TempoSection::_pulse_at_tempo (const double& npm) const
467 return ((npm - note_types_per_minute()) / _c_func) / _note_type;
470 /* tempo in note types per minute at pulse */
472 TempoSection::_tempo_at_pulse (const double& pulse) const
474 return (pulse * _note_type * _c_func) + note_types_per_minute();
477 /* pulse at time in minutes */
479 TempoSection::_pulse_at_time (const double& time) const
481 return (expm1 (_c_func * time) * (note_types_per_minute() / _c_func)) / _note_type;
484 /* time in minutes at pulse */
486 TempoSection::_time_at_pulse (const double& pulse) const
488 return log1p ((_c_func * pulse * _note_type) / note_types_per_minute()) / _c_func;
491 /***********************************************************************/
493 const string MeterSection::xml_state_node_name = "Meter";
495 MeterSection::MeterSection (const XMLNode& node, const framecnt_t sample_rate)
496 : MetricSection (0.0, 0, MusicTime, false, sample_rate), Meter (TempoMap::default_meter())
498 XMLProperty const * prop;
503 framepos_t frame = 0;
504 pair<double, BBT_Time> start;
507 if ((prop = node.property ("start")) != 0) {
508 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
512 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
514 /* legacy session - start used to be in bbt*/
515 info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
520 if ((prop = node.property ("pulse")) != 0) {
521 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
522 error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
527 if ((prop = node.property ("beat")) != 0) {
528 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
529 error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
535 if ((prop = node.property ("bbt")) == 0) {
536 warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
537 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
541 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
542 throw failed_constructor();
548 if ((prop = node.property ("frame")) != 0) {
549 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
550 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
552 set_minute (minute_at_frame (frame));
555 if ((prop = node.property ("minute")) != 0) {
556 if (sscanf (prop->value().c_str(), "%lf", &minute) != 1) {
557 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
563 /* beats-per-bar is old; divisions-per-bar is new */
565 if ((prop = node.property ("divisions-per-bar")) == 0) {
566 if ((prop = node.property ("beats-per-bar")) == 0) {
567 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
568 throw failed_constructor();
571 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
572 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
573 throw failed_constructor();
576 if ((prop = node.property ("note-type")) == 0) {
577 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
578 throw failed_constructor();
580 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
581 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
582 throw failed_constructor();
585 if ((prop = node.property ("movable")) == 0) {
586 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
587 throw failed_constructor();
590 set_movable (string_is_affirmative (prop->value()));
592 if ((prop = node.property ("lock-style")) == 0) {
593 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
595 set_position_lock_style (MusicTime);
597 set_position_lock_style (AudioTime);
600 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
605 MeterSection::get_state() const
607 XMLNode *root = new XMLNode (xml_state_node_name);
611 snprintf (buf, sizeof (buf), "%lf", pulse());
612 root->add_property ("pulse", buf);
613 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
617 root->add_property ("bbt", buf);
618 snprintf (buf, sizeof (buf), "%lf", beat());
619 root->add_property ("beat", buf);
620 snprintf (buf, sizeof (buf), "%lf", _note_type);
621 root->add_property ("note-type", buf);
622 snprintf (buf, sizeof (buf), "%li", frame());
623 root->add_property ("frame", buf);
624 snprintf (buf, sizeof (buf), "%lf", minute());
625 root->add_property ("minute", buf);
626 root->add_property ("lock-style", enum_2_string (position_lock_style()));
627 snprintf (buf, sizeof (buf), "%lf", _divisions_per_bar);
628 root->add_property ("divisions-per-bar", buf);
629 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
630 root->add_property ("movable", buf);
635 /***********************************************************************/
639 Tempo determines the rate of musical pulse determined by its components
640 note types per minute - the rate per minute of the whole note divisor _note_type
641 note type - the division of whole notes (pulses) which occur at the rate of note types per minute.
642 Meter divides the musical pulse into measures and beats according to its components
646 TempoSection - translates between time, musical pulse and tempo.
647 has a musical location in whole notes (pulses).
648 has a time location in minutes.
649 Note that 'beats' in Tempo::note_types_per_minute() are in fact note types per minute.
650 (In the rest of tempo map,'beat' usually refers to accumulated BBT beats (pulse and meter based).
652 MeterSection - translates between BBT, meter-based beat and musical pulse.
653 has a musical location in whole notes (pulses)
654 has a musical location in meter-based beats
655 has a musical location in BBT time
656 has a time location expressed in minutes.
658 TempoSection and MeterSection may be locked to either audio or music (position lock style).
659 The lock style determines the location type to be kept as a reference when location is recalculated.
661 The first tempo and meter are special. they must move together, and are locked to audio.
662 Audio locked tempi which lie before the first meter are made inactive.
664 Recomputing the map is the process where the 'missing' location types are calculated.
665 We construct the tempo map by first using the locked location type of each section
666 to determine non-locked location types (pulse or minute position).
667 We then use this map to find the pulse or minute position of each meter (again depending on lock style).
669 Having done this, we can now traverse the Metrics list by pulse or minute
670 to query its relevant meter/tempo.
672 It is important to keep the _metrics in an order that makes sense.
673 Because ramped MusicTime and AudioTime tempos can interact with each other,
674 reordering is frequent. Care must be taken to keep _metrics in a solved state.
675 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
679 Music and audio-locked objects may seem interchangeable on the surface, but when translating
680 between audio samples and beat, remember that a sample is only a quantised approximation
681 of the actual time (in minutes) of a beat.
682 Thus if a gui user points to the frame occupying the start of a music-locked object on 1|3|0, it does not
683 mean that this frame is the actual location in time of 1|3|0.
685 You cannot use a frame measurement to determine beat distance except under special circumstances
686 (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).
688 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
689 sample space the user is operating at to be translated correctly to the object.
691 The current approach is to interpret the supplied frame using the grid division the user has currently selected.
692 If the user has no musical grid set, they are actually operating in sample space (even SMPTE frames are rounded to audio frame), so
693 the supplied audio frame is interpreted as the desired musical location (beat_at_frame()).
695 tldr: Beat, being a function of time, has nothing to do with sample rate, but time quantization can get in the way of precision.
697 When frame_at_beat() is called, the position calculation is performed in pulses and minutes.
698 The result is rounded to audio frames.
699 When beat_at_frame() is called, the frame is converted to minutes, with no rounding performed on the result.
702 frame_at_beat (beat_at_frame (frame)) == frame
704 beat_at_frame (frame_at_beat (beat)) != beat due to the time quantization of frame_at_beat().
706 Doing the second one will result in a beat distance error of up to 0.5 audio samples.
707 frames_between_quarter_notes () eliminats this effect when determining time duration
708 from Beats distance, or instead work in quarter-notes and/or beats and convert to frames last.
710 The above pointless example could instead do:
711 beat_at_quarter_note (quarter_note_at_beat (beat)) to avoid rounding.
713 The Shaggs - Things I Wonder
714 https://www.youtube.com/watch?v=9wQK6zMJOoQ
717 struct MetricSectionSorter {
718 bool operator() (const MetricSection* a, const MetricSection* b) {
719 return a->pulse() < b->pulse();
723 struct MetricSectionFrameSorter {
724 bool operator() (const MetricSection* a, const MetricSection* b) {
725 return a->frame() < b->frame();
729 TempoMap::TempoMap (framecnt_t fr)
732 BBT_Time start (1, 1, 0);
734 TempoSection *t = new TempoSection (0.0, 0.0, _default_tempo.note_types_per_minute(), _default_tempo.note_type(), TempoSection::Ramp, AudioTime, fr);
735 MeterSection *m = new MeterSection (0.0, 0.0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime, fr);
737 t->set_movable (false);
738 m->set_movable (false);
740 /* note: frame time is correct (zero) for both of these */
742 _metrics.push_back (t);
743 _metrics.push_back (m);
747 TempoMap::~TempoMap ()
749 Metrics::const_iterator d = _metrics.begin();
750 while (d != _metrics.end()) {
758 TempoMap::frame_at_minute (const double time) const
760 return (framepos_t) floor ((time * 60.0 * _frame_rate) + 0.5);
764 TempoMap::minute_at_frame (const framepos_t frame) const
766 return (frame / (double) _frame_rate) / 60.0;
770 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
772 bool removed = false;
775 Glib::Threads::RWLock::WriterLock lm (lock);
776 if ((removed = remove_tempo_locked (tempo))) {
777 if (complete_operation) {
778 recompute_map (_metrics);
783 if (removed && complete_operation) {
784 PropertyChanged (PropertyChange ());
789 TempoMap::remove_tempo_locked (const TempoSection& tempo)
793 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
794 if (dynamic_cast<TempoSection*> (*i) != 0) {
795 if (tempo.frame() == (*i)->frame()) {
796 if ((*i)->movable()) {
809 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
811 bool removed = false;
814 Glib::Threads::RWLock::WriterLock lm (lock);
815 if ((removed = remove_meter_locked (tempo))) {
816 if (complete_operation) {
817 recompute_map (_metrics);
822 if (removed && complete_operation) {
823 PropertyChanged (PropertyChange ());
828 TempoMap::remove_meter_locked (const MeterSection& meter)
831 if (meter.position_lock_style() == AudioTime) {
832 /* remove meter-locked tempo */
833 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
835 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
836 if (t->locked_to_meter() && meter.frame() == (*i)->frame()) {
845 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
846 if (dynamic_cast<MeterSection*> (*i) != 0) {
847 if (meter.frame() == (*i)->frame()) {
848 if ((*i)->movable()) {
861 TempoMap::do_insert (MetricSection* section)
863 bool need_add = true;
864 /* we only allow new meters to be inserted on beat 1 of an existing
868 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
870 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
872 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
873 corrected.second.beats = 1;
874 corrected.second.ticks = 0;
875 corrected.first = beat_at_bbt_locked (_metrics, corrected.second);
876 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
877 m->bbt(), corrected.second) << endmsg;
878 //m->set_pulse (corrected);
882 /* Look for any existing MetricSection that is of the same type and
883 in the same bar as the new one, and remove it before adding
884 the new one. Note that this means that if we find a matching,
885 existing section, we can break out of the loop since we're
886 guaranteed that there is only one such match.
889 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
891 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
892 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
893 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
894 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
896 if (tempo && insert_tempo) {
899 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
900 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
902 if (!tempo->movable()) {
904 /* can't (re)move this section, so overwrite
905 * its data content (but not its properties as
909 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
910 (*i)->set_position_lock_style (AudioTime);
912 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
913 t->set_type (insert_tempo->type());
923 } else if (meter && insert_meter) {
927 bool const ipm = insert_meter->position_lock_style() == MusicTime;
929 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
931 if (!meter->movable()) {
933 /* can't (re)move this section, so overwrite
934 * its data content (but not its properties as
938 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
939 (*i)->set_position_lock_style (AudioTime);
949 /* non-matching types, so we don't care */
953 /* Add the given MetricSection, if we didn't just reset an existing
958 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
959 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
962 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
963 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
966 bool const ipm = insert_meter->position_lock_style() == MusicTime;
967 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
972 } else if (insert_tempo) {
973 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
974 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
977 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
978 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
985 _metrics.insert (i, section);
986 //dump (_metrics, std::cout);
989 /* user supplies the exact pulse if pls == MusicTime */
991 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, ARDOUR::TempoSection::Type type, PositionLockStyle pls)
993 TempoSection* ts = 0;
995 Glib::Threads::RWLock::WriterLock lm (lock);
996 ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), type, pls, true);
1000 PropertyChanged (PropertyChange ());
1006 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, TempoSection::Type type, PositionLockStyle pls)
1008 const bool locked_to_meter = ts.locked_to_meter();
1011 Glib::Threads::RWLock::WriterLock lm (lock);
1012 TempoSection& first (first_tempo());
1013 if (ts.frame() != first.frame()) {
1014 remove_tempo_locked (ts);
1015 add_tempo_locked (tempo, pulse, minute_at_frame (frame), type, pls, true, locked_to_meter);
1017 first.set_type (type);
1018 first.set_pulse (0.0);
1019 first.set_minute (minute_at_frame (frame));
1020 first.set_position_lock_style (AudioTime);
1022 /* cannot move the first tempo section */
1023 *static_cast<Tempo*>(&first) = tempo;
1024 recompute_map (_metrics);
1029 PropertyChanged (PropertyChange ());
1033 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, double minute
1034 , TempoSection::Type type, PositionLockStyle pls, bool recompute, bool locked_to_meter)
1036 TempoSection* t = new TempoSection (pulse, minute, tempo.note_types_per_minute(), tempo.note_type(), type, pls, _frame_rate);
1037 t->set_locked_to_meter (locked_to_meter);
1038 bool solved = false;
1043 if (pls == AudioTime) {
1044 solved = solve_map_minute (_metrics, t, t->minute());
1046 solved = solve_map_pulse (_metrics, t, t->pulse());
1048 recompute_meters (_metrics);
1051 if (!solved && recompute) {
1052 recompute_map (_metrics);
1059 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, PositionLockStyle pls)
1061 MeterSection* m = 0;
1063 Glib::Threads::RWLock::WriterLock lm (lock);
1064 m = add_meter_locked (meter, beat, where, pls, true);
1069 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1070 dump (_metrics, std::cerr);
1074 PropertyChanged (PropertyChange ());
1079 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, PositionLockStyle pls)
1082 Glib::Threads::RWLock::WriterLock lm (lock);
1083 const double beat = beat_at_bbt_locked (_metrics, where);
1086 remove_meter_locked (ms);
1087 add_meter_locked (meter, beat, where, pls, true);
1089 MeterSection& first (first_meter());
1090 TempoSection& first_t (first_tempo());
1091 /* cannot move the first meter section */
1092 *static_cast<Meter*>(&first) = meter;
1093 first.set_position_lock_style (AudioTime);
1094 first.set_pulse (0.0);
1095 //first.set_minute (minute_at_frame (frame));
1096 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1097 first.set_beat (beat);
1098 first_t.set_minute (first.minute());
1099 first_t.set_pulse (0.0);
1100 first_t.set_position_lock_style (AudioTime);
1101 recompute_map (_metrics);
1105 PropertyChanged (PropertyChange ());
1109 TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, PositionLockStyle pls, bool recompute)
1111 const MeterSection& prev_m = meter_section_at_minute_locked (_metrics, minute_at_beat_locked (_metrics, beat) - minute_at_frame (1));
1112 const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1113 const double time_minutes = minute_at_pulse_locked (_metrics, pulse);
1114 TempoSection* mlt = 0;
1116 if (pls == AudioTime) {
1117 /* add meter-locked tempo */
1118 mlt = add_tempo_locked (tempo_at_minute_locked (_metrics, time_minutes), pulse, time_minutes, TempoSection::Ramp, AudioTime, true, true);
1126 MeterSection* new_meter = new MeterSection (pulse, time_minutes, beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls, _frame_rate);
1127 bool solved = false;
1129 do_insert (new_meter);
1133 if (pls == AudioTime) {
1134 solved = solve_map_minute (_metrics, new_meter, time_minutes);
1136 solved = solve_map_bbt (_metrics, new_meter, where);
1137 /* required due to resetting the pulse of meter-locked tempi above.
1138 Arguably solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
1139 but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
1141 recompute_map (_metrics);
1145 if (!solved && recompute) {
1146 /* if this has failed to solve, there is little we can do other than to ensure that
1147 the new map is recalculated.
1149 warning << "Adding meter may have left the tempo map unsolved." << endmsg;
1150 recompute_map (_metrics);
1157 TempoMap::change_initial_tempo (double note_types_per_minute, double note_type)
1159 Tempo newtempo (note_types_per_minute, note_type);
1162 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1163 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1168 Glib::Threads::RWLock::WriterLock lm (lock);
1169 *((Tempo*) t) = newtempo;
1170 recompute_map (_metrics);
1172 PropertyChanged (PropertyChange ());
1179 TempoMap::change_existing_tempo_at (framepos_t where, double note_types_per_minute, double note_type)
1181 Tempo newtempo (note_types_per_minute, note_type);
1184 TempoSection* first;
1185 Metrics::iterator i;
1187 /* find the TempoSection immediately preceding "where"
1190 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1192 if ((*i)->frame() > where) {
1198 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1211 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1221 Glib::Threads::RWLock::WriterLock lm (lock);
1222 /* cannot move the first tempo section */
1223 *((Tempo*)prev) = newtempo;
1224 recompute_map (_metrics);
1227 PropertyChanged (PropertyChange ());
1231 TempoMap::first_meter () const
1233 const MeterSection *m = 0;
1235 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1236 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1241 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1242 abort(); /*NOTREACHED*/
1247 TempoMap::first_meter ()
1249 MeterSection *m = 0;
1251 /* CALLER MUST HOLD LOCK */
1253 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1254 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1259 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1260 abort(); /*NOTREACHED*/
1265 TempoMap::first_tempo () const
1267 const TempoSection *t = 0;
1269 /* CALLER MUST HOLD LOCK */
1271 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1272 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1276 if (!t->movable()) {
1282 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1283 abort(); /*NOTREACHED*/
1288 TempoMap::first_tempo ()
1290 TempoSection *t = 0;
1292 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1293 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1297 if (!t->movable()) {
1303 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1304 abort(); /*NOTREACHED*/
1308 TempoMap::recompute_tempi (Metrics& metrics)
1310 TempoSection* prev_t = 0;
1312 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1315 if ((*i)->is_tempo()) {
1316 t = static_cast<TempoSection*> (*i);
1320 if (!t->movable()) {
1328 if (t->position_lock_style() == AudioTime) {
1329 prev_t->set_c_func (prev_t->compute_c_func_minute (t->note_types_per_minute(), t->minute()));
1330 if (!t->locked_to_meter()) {
1331 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
1335 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->note_types_per_minute(), t->pulse()));
1336 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
1343 prev_t->set_c_func (0.0);
1346 /* tempos must be positioned correctly.
1347 the current approach is to use a meter's bbt time as its base position unit.
1348 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1349 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1352 TempoMap::recompute_meters (Metrics& metrics)
1354 MeterSection* meter = 0;
1355 MeterSection* prev_m = 0;
1357 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1358 if (!(*mi)->is_tempo()) {
1359 meter = static_cast<MeterSection*> (*mi);
1360 if (meter->position_lock_style() == AudioTime) {
1362 pair<double, BBT_Time> b_bbt;
1363 TempoSection* meter_locked_tempo = 0;
1364 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1366 if ((*ii)->is_tempo()) {
1367 t = static_cast<TempoSection*> (*ii);
1368 if ((t->locked_to_meter() || !t->movable()) && t->frame() == meter->frame()) {
1369 meter_locked_tempo = t;
1376 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1377 if (beats + prev_m->beat() != meter->beat()) {
1378 /* reordering caused a bbt change */
1379 b_bbt = make_pair (beats + prev_m->beat()
1380 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1381 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1383 } else if (meter->movable()) {
1384 b_bbt = make_pair (meter->beat(), meter->bbt());
1385 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1388 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1390 if (meter_locked_tempo) {
1391 meter_locked_tempo->set_pulse (pulse);
1393 meter->set_beat (b_bbt);
1394 meter->set_pulse (pulse);
1399 pair<double, BBT_Time> b_bbt;
1401 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1402 if (beats + prev_m->beat() != meter->beat()) {
1403 /* reordering caused a bbt change */
1404 b_bbt = make_pair (beats + prev_m->beat()
1405 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1407 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1409 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1411 /* shouldn't happen - the first is audio-locked */
1412 pulse = pulse_at_beat_locked (metrics, meter->beat());
1413 b_bbt = make_pair (meter->beat(), meter->bbt());
1416 meter->set_beat (b_bbt);
1417 meter->set_pulse (pulse);
1418 meter->set_minute (minute_at_pulse_locked (metrics, pulse));
1427 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1429 /* CALLER MUST HOLD WRITE LOCK */
1431 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1434 /* silly call from Session::process() during startup
1439 recompute_tempi (metrics);
1440 recompute_meters (metrics);
1444 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1446 Glib::Threads::RWLock::ReaderLock lm (lock);
1447 TempoMetric m (first_meter(), first_tempo());
1449 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1450 at something, because we insert the default tempo and meter during
1451 TempoMap construction.
1453 now see if we can find better candidates.
1456 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1458 if ((*i)->frame() > frame) {
1472 /* XX meters only */
1474 TempoMap::metric_at (BBT_Time bbt) const
1476 Glib::Threads::RWLock::ReaderLock lm (lock);
1477 TempoMetric m (first_meter(), first_tempo());
1479 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1480 at something, because we insert the default tempo and meter during
1481 TempoMap construction.
1483 now see if we can find better candidates.
1486 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1488 if (!(*i)->is_tempo()) {
1489 mw = static_cast<MeterSection*> (*i);
1490 BBT_Time section_start (mw->bbt());
1492 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1503 /** Returns the BBT (meter-based) beat corresponding to the supplied frame, possibly returning a negative value.
1504 * @param frame The session frame position.
1505 * @return The beat duration according to the tempo map at the supplied frame.
1507 * If the supplied frame lies before the first meter, the returned beat duration will be negative.
1508 * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
1510 * This function uses both tempo and meter.
1513 TempoMap::beat_at_frame (const framecnt_t& frame) const
1515 Glib::Threads::RWLock::ReaderLock lm (lock);
1517 return beat_at_minute_locked (_metrics, minute_at_frame (frame));
1520 /* This function uses both tempo and meter.*/
1522 TempoMap::beat_at_minute_locked (const Metrics& metrics, const double& minute) const
1524 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
1525 MeterSection* prev_m = 0;
1526 MeterSection* next_m = 0;
1528 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1529 if (!(*i)->is_tempo()) {
1530 if (prev_m && (*i)->minute() > minute) {
1531 next_m = static_cast<MeterSection*> (*i);
1534 prev_m = static_cast<MeterSection*> (*i);
1538 const double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
1540 /* audio locked meters fake their beat */
1541 if (next_m && next_m->beat() < beat) {
1542 return next_m->beat();
1548 /** Returns the frame corresponding to the supplied BBT (meter-based) beat.
1549 * @param beat The BBT (meter-based) beat.
1550 * @return The frame duration according to the tempo map at the supplied BBT (meter-based) beat.
1552 * This function uses both tempo and meter.
1555 TempoMap::frame_at_beat (const double& beat) const
1557 Glib::Threads::RWLock::ReaderLock lm (lock);
1559 return frame_at_minute (minute_at_beat_locked (_metrics, beat));
1562 /* meter & tempo section based */
1564 TempoMap::minute_at_beat_locked (const Metrics& metrics, const double& beat) const
1566 MeterSection* prev_m = 0;
1567 TempoSection* prev_t = 0;
1571 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1572 if (!(*i)->is_tempo()) {
1573 m = static_cast<MeterSection*> (*i);
1574 if (prev_m && m->beat() > beat) {
1583 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1584 if ((*i)->is_tempo()) {
1585 t = static_cast<TempoSection*> (*i);
1586 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1594 return prev_t->minute_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse());
1597 /** Returns a Tempo corresponding to the supplied frame position.
1598 * @param frame The audio frame.
1599 * @return a Tempo according to the tempo map at the supplied frame.
1603 TempoMap::tempo_at_frame (const framepos_t& frame) const
1605 Glib::Threads::RWLock::ReaderLock lm (lock);
1607 return tempo_at_minute_locked (_metrics, minute_at_frame (frame));
1611 TempoMap::tempo_at_minute_locked (const Metrics& metrics, const double& minute) const
1613 TempoSection* prev_t = 0;
1617 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1618 if ((*i)->is_tempo()) {
1619 t = static_cast<TempoSection*> (*i);
1623 if ((prev_t) && t->minute() > minute) {
1624 /* t is the section past frame */
1625 return prev_t->tempo_at_minute (minute);
1631 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type());
1634 /** returns the frame at which the supplied tempo occurs, or
1635 * the frame of the last tempo section (search exhausted)
1636 * only the position of the first occurence will be returned
1640 TempoMap::frame_at_tempo (const Tempo& tempo) const
1642 Glib::Threads::RWLock::ReaderLock lm (lock);
1644 return frame_at_minute (minute_at_tempo_locked (_metrics, tempo));
1648 TempoMap::minute_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1650 TempoSection* prev_t = 0;
1651 const double tempo_bpm = tempo.note_types_per_minute();
1653 Metrics::const_iterator i;
1655 for (i = metrics.begin(); i != metrics.end(); ++i) {
1657 if ((*i)->is_tempo()) {
1658 t = static_cast<TempoSection*> (*i);
1664 const double t_bpm = t->note_types_per_minute();
1666 if (t_bpm == tempo_bpm) {
1671 const double prev_t_bpm = prev_t->note_types_per_minute();
1673 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1674 return prev_t->minute_at_ntpm (prev_t->note_types_per_minute(), prev_t->pulse());
1681 return prev_t->minute();
1685 TempoMap::tempo_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1687 TempoSection* prev_t = 0;
1691 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1692 if ((*i)->is_tempo()) {
1693 t = static_cast<TempoSection*> (*i);
1697 if ((prev_t) && t->pulse() > pulse) {
1698 /* t is the section past frame */
1699 return prev_t->tempo_at_pulse (pulse);
1705 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type());
1709 TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1711 TempoSection* prev_t = 0;
1712 const double tempo_bpm = tempo.note_types_per_minute();
1714 Metrics::const_iterator i;
1716 for (i = metrics.begin(); i != metrics.end(); ++i) {
1718 if ((*i)->is_tempo()) {
1719 t = static_cast<TempoSection*> (*i);
1725 const double t_bpm = t->note_types_per_minute();
1727 if (t_bpm == tempo_bpm) {
1732 const double prev_t_bpm = prev_t->note_types_per_minute();
1734 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1735 return prev_t->pulse_at_ntpm (prev_t->note_types_per_minute(), prev_t->minute());
1742 return prev_t->pulse();
1745 /** Returns a Tempo corresponding to the supplied position in quarter-note beats.
1746 * @param qn the position in quarter note beats.
1747 * @return the Tempo at the supplied quarter-note.
1750 TempoMap::tempo_at_quarter_note (const double& qn) const
1752 Glib::Threads::RWLock::ReaderLock lm (lock);
1754 return tempo_at_pulse_locked (_metrics, qn / 4.0);
1757 /** Returns the position in quarter-note beats corresponding to the supplied Tempo.
1758 * @param tempo the tempo.
1759 * @return the position in quarter-note beats where the map bpm
1760 * is equal to that of the Tempo. currently ignores note_type.
1763 TempoMap::quarter_note_at_tempo (const Tempo& tempo) const
1765 Glib::Threads::RWLock::ReaderLock lm (lock);
1767 return pulse_at_tempo_locked (_metrics, tempo) * 4.0;;
1770 /** Returns the whole-note pulse corresponding to the supplied BBT (meter-based) beat.
1771 * @param metrics the list of metric sections used to calculate the pulse.
1772 * @param beat The BBT (meter-based) beat.
1773 * @return the whole-note pulse at the supplied BBT (meter-based) beat.
1775 * a pulse or whole note is the base musical position of a MetricSection.
1776 * it is equivalent to four quarter notes.
1780 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1782 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1784 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1787 /** Returns the BBT (meter-based) beat corresponding to the supplied whole-note pulse .
1788 * @param metrics the list of metric sections used to calculate the beat.
1789 * @param pulse the whole-note pulse.
1790 * @return the meter-based beat at the supplied whole-note pulse.
1792 * a pulse or whole note is the base musical position of a MetricSection.
1793 * it is equivalent to four quarter notes.
1796 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1798 MeterSection* prev_m = 0;
1800 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1802 if (!(*i)->is_tempo()) {
1803 m = static_cast<MeterSection*> (*i);
1804 if (prev_m && m->pulse() > pulse) {
1811 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1815 /* tempo section based */
1817 TempoMap::pulse_at_minute_locked (const Metrics& metrics, const double& minute) const
1819 /* HOLD (at least) THE READER LOCK */
1820 TempoSection* prev_t = 0;
1822 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1824 if ((*i)->is_tempo()) {
1825 t = static_cast<TempoSection*> (*i);
1829 if (prev_t && t->minute() > minute) {
1830 /*the previous ts is the one containing the frame */
1831 const double ret = prev_t->pulse_at_minute (minute);
1832 /* audio locked section in new meter*/
1833 if (t->pulse() < ret) {
1842 /* treated as constant for this ts */
1843 const double pulses_in_section = ((minute - prev_t->minute()) * prev_t->note_types_per_minute()) / prev_t->note_type();
1845 return pulses_in_section + prev_t->pulse();
1848 /* tempo section based */
1850 TempoMap::minute_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1852 /* HOLD THE READER LOCK */
1854 const TempoSection* prev_t = 0;
1856 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1859 if ((*i)->is_tempo()) {
1860 t = static_cast<TempoSection*> (*i);
1864 if (prev_t && t->pulse() > pulse) {
1865 return prev_t->minute_at_pulse (pulse);
1871 /* must be treated as constant, irrespective of _type */
1872 double const dtime = ((pulse - prev_t->pulse()) * prev_t->note_type()) / prev_t->note_types_per_minute();
1874 return dtime + prev_t->minute();
1877 /** Returns the BBT (meter-based) beat corresponding to the supplied BBT time.
1878 * @param bbt The BBT time (meter-based).
1879 * @return bbt The BBT beat (meter-based) at the supplied BBT time.
1883 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
1885 Glib::Threads::RWLock::ReaderLock lm (lock);
1886 return beat_at_bbt_locked (_metrics, bbt);
1891 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1893 /* CALLER HOLDS READ LOCK */
1895 MeterSection* prev_m = 0;
1897 /* because audio-locked meters have 'fake' integral beats,
1898 there is no pulse offset here.
1902 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1903 if (!(*i)->is_tempo()) {
1904 m = static_cast<MeterSection*> (*i);
1906 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1907 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1915 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1916 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1917 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1922 /** Returns the BBT time corresponding to the supplied BBT (meter-based) beat.
1923 * @param beat The BBT (meter-based) beat.
1924 * @return The BBT time (meter-based) at the supplied meter-based beat.
1928 TempoMap::bbt_at_beat (const double& beat)
1930 Glib::Threads::RWLock::ReaderLock lm (lock);
1931 return bbt_at_beat_locked (_metrics, beat);
1935 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
1937 /* CALLER HOLDS READ LOCK */
1938 MeterSection* prev_m = 0;
1939 const double beats = max (0.0, b);
1941 MeterSection* m = 0;
1943 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1944 if (!(*i)->is_tempo()) {
1945 m = static_cast<MeterSection*> (*i);
1947 if (m->beat() > beats) {
1948 /* this is the meter after the one our beat is on*/
1957 const double beats_in_ms = beats - prev_m->beat();
1958 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1959 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1960 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1961 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1965 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1966 ret.beats = (uint32_t) floor (remaining_beats);
1967 ret.bars = total_bars;
1969 /* 0 0 0 to 1 1 0 - based mapping*/
1973 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1975 ret.ticks -= BBT_Time::ticks_per_beat;
1978 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
1986 /** Returns the quarter-note beat corresponding to the supplied BBT time (meter-based).
1987 * @param bbt The BBT time (meter-based).
1988 * @return the quarter note beat at the supplied BBT time
1990 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
1992 * while the input uses meter, the output does not.
1995 TempoMap::quarter_note_at_bbt (const Timecode::BBT_Time& bbt)
1997 Glib::Threads::RWLock::ReaderLock lm (lock);
1999 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2003 TempoMap::quarter_note_at_bbt_rt (const Timecode::BBT_Time& bbt)
2005 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2008 throw std::logic_error ("TempoMap::quarter_note_at_bbt_rt() could not lock tempo map");
2011 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2015 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2017 /* CALLER HOLDS READ LOCK */
2019 MeterSection* prev_m = 0;
2021 /* because audio-locked meters have 'fake' integral beats,
2022 there is no pulse offset here.
2026 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2027 if (!(*i)->is_tempo()) {
2028 m = static_cast<MeterSection*> (*i);
2030 if (m->bbt().bars > bbt.bars) {
2038 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2039 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
2040 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
2045 /** Returns the BBT time corresponding to the supplied quarter-note beat.
2046 * @param qn the quarter-note beat.
2047 * @return The BBT time (meter-based) at the supplied meter-based beat.
2049 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2053 TempoMap::bbt_at_quarter_note (const double& qn)
2055 Glib::Threads::RWLock::ReaderLock lm (lock);
2057 return bbt_at_pulse_locked (_metrics, qn / 4.0);
2060 /** Returns the BBT time (meter-based) corresponding to the supplied whole-note pulse position.
2061 * @param metrics The list of metric sections used to determine the result.
2062 * @param pulse The whole-note pulse.
2063 * @return The BBT time at the supplied whole-note pulse.
2065 * a pulse or whole note is the basic musical position of a MetricSection.
2066 * it is equivalent to four quarter notes.
2067 * while the output uses meter, the input does not.
2070 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2072 MeterSection* prev_m = 0;
2074 MeterSection* m = 0;
2076 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2078 if (!(*i)->is_tempo()) {
2079 m = static_cast<MeterSection*> (*i);
2082 double const pulses_to_m = m->pulse() - prev_m->pulse();
2083 if (prev_m->pulse() + pulses_to_m > pulse) {
2084 /* this is the meter after the one our beat is on*/
2093 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
2094 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2095 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2096 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2097 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2101 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2102 ret.beats = (uint32_t) floor (remaining_beats);
2103 ret.bars = total_bars;
2105 /* 0 0 0 to 1 1 0 mapping*/
2109 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2111 ret.ticks -= BBT_Time::ticks_per_beat;
2114 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2122 /** Returns the BBT time corresponding to the supplied frame position.
2123 * @param frame the position in audio samples.
2124 * @return the BBT time at the frame position .
2128 TempoMap::bbt_at_frame (framepos_t frame)
2135 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
2138 Glib::Threads::RWLock::ReaderLock lm (lock);
2140 return bbt_at_minute_locked (_metrics, minute_at_frame (frame));
2144 TempoMap::bbt_at_frame_rt (framepos_t frame)
2146 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2149 throw std::logic_error ("TempoMap::bbt_at_frame_rt() could not lock tempo map");
2152 return bbt_at_minute_locked (_metrics, minute_at_frame (frame));
2156 TempoMap::bbt_at_minute_locked (const Metrics& metrics, const double& minute) const
2166 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
2167 MeterSection* prev_m = 0;
2168 MeterSection* next_m = 0;
2172 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2173 if (!(*i)->is_tempo()) {
2174 m = static_cast<MeterSection*> (*i);
2175 if (prev_m && m->minute() > minute) {
2183 double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
2185 /* handle frame before first meter */
2186 if (minute < prev_m->minute()) {
2189 /* audio locked meters fake their beat */
2190 if (next_m && next_m->beat() < beat) {
2191 beat = next_m->beat();
2194 beat = max (0.0, beat);
2196 const double beats_in_ms = beat - prev_m->beat();
2197 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2198 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2199 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2200 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2204 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2205 ret.beats = (uint32_t) floor (remaining_beats);
2206 ret.bars = total_bars;
2208 /* 0 0 0 to 1 1 0 - based mapping*/
2212 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2214 ret.ticks -= BBT_Time::ticks_per_beat;
2217 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2225 /** Returns the frame position corresponding to the supplied BBT time.
2226 * @param bbt the position in BBT time.
2227 * @return the frame position at bbt.
2231 TempoMap::frame_at_bbt (const BBT_Time& bbt)
2234 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
2238 if (bbt.beats < 1) {
2239 throw std::logic_error ("beats are counted from one");
2241 Glib::Threads::RWLock::ReaderLock lm (lock);
2243 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
2246 /* meter & tempo section based */
2248 TempoMap::minute_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2250 /* HOLD THE READER LOCK */
2252 const double ret = minute_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2257 * Returns the quarter-note beat position corresponding to the supplied frame.
2259 * @param frame the position in frames.
2260 * @return The quarter-note position of the supplied frame. Ignores meter.
2264 TempoMap::quarter_note_at_frame (const framepos_t frame) const
2266 Glib::Threads::RWLock::ReaderLock lm (lock);
2268 const double ret = quarter_note_at_minute_locked (_metrics, minute_at_frame (frame));
2274 TempoMap::quarter_note_at_minute_locked (const Metrics& metrics, const double minute) const
2276 const double ret = pulse_at_minute_locked (metrics, minute) * 4.0;
2282 TempoMap::quarter_note_at_frame_rt (const framepos_t frame) const
2284 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2287 throw std::logic_error ("TempoMap::quarter_note_at_frame_rt() could not lock tempo map");
2290 const double ret = pulse_at_minute_locked (_metrics, minute_at_frame (frame)) * 4.0;
2296 * Returns the frame position corresponding to the supplied quarter-note beat.
2298 * @param quarter_note the quarter-note position.
2299 * @return the frame position of the supplied quarter-note. Ignores meter.
2304 TempoMap::frame_at_quarter_note (const double quarter_note) const
2306 Glib::Threads::RWLock::ReaderLock lm (lock);
2308 const framepos_t ret = frame_at_minute (minute_at_quarter_note_locked (_metrics, quarter_note));
2314 TempoMap::minute_at_quarter_note_locked (const Metrics& metrics, const double quarter_note) const
2316 const double ret = minute_at_pulse_locked (metrics, quarter_note / 4.0);
2321 /** Returns the quarter-note beats corresponding to the supplied BBT (meter-based) beat.
2322 * @param beat The BBT (meter-based) beat.
2323 * @return The quarter-note position of the supplied BBT (meter-based) beat.
2325 * a quarter-note may be compared with and assigned to Evoral::Beats.
2329 TempoMap::quarter_note_at_beat (const double beat)
2331 Glib::Threads::RWLock::ReaderLock lm (lock);
2333 const double ret = quarter_note_at_beat_locked (_metrics, beat);
2339 TempoMap::quarter_note_at_beat_locked (const Metrics& metrics, const double beat) const
2341 const double ret = pulse_at_beat_locked (metrics, beat) * 4.0;
2346 /** Returns the BBT (meter-based) beat position corresponding to the supplied quarter-note beats.
2347 * @param quarter_note The position in quarter-note beats.
2348 * @return the BBT (meter-based) beat position of the supplied quarter-note beats.
2350 * a quarter-note is the musical unit of Evoral::Beats.
2354 TempoMap::beat_at_quarter_note (const double quarter_note)
2356 Glib::Threads::RWLock::ReaderLock lm (lock);
2358 const double ret = beat_at_quarter_note_locked (_metrics, quarter_note);
2364 TempoMap::beat_at_quarter_note_locked (const Metrics& metrics, const double quarter_note) const
2367 return beat_at_pulse_locked (metrics, quarter_note / 4.0);
2370 /** Returns the duration in frames between two supplied quarter-note beat positions.
2371 * @param start the first position in quarter-note beats.
2372 * @param end the end position in quarter-note beats.
2373 * @return the frame distance ober the quarter-note beats duration.
2375 * use this rather than e.g.
2376 * frame_at-quarter_note (end_beats) - frame_at_quarter_note (start_beats).
2377 * frames_between_quarter_notes() doesn't round to audio frames as an intermediate step,
2381 TempoMap::frames_between_quarter_notes (const double start, const double end) const
2383 Glib::Threads::RWLock::ReaderLock lm (lock);
2385 return frame_at_minute (minutes_between_quarter_notes_locked (_metrics, start, end));
2389 TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end) const
2392 return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
2396 TempoMap::check_solved (const Metrics& metrics) const
2398 TempoSection* prev_t = 0;
2399 MeterSection* prev_m = 0;
2401 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2404 if ((*i)->is_tempo()) {
2405 t = static_cast<TempoSection*> (*i);
2410 /* check ordering */
2411 if ((t->minute() <= prev_t->minute()) || (t->pulse() <= prev_t->pulse())) {
2415 /* precision check ensures tempo and frames align.*/
2416 if (t->frame() != frame_at_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()))) {
2417 if (!t->locked_to_meter()) {
2422 /* gradient limit - who knows what it should be?
2423 things are also ok (if a little chaotic) without this
2425 if (fabs (prev_t->c_func()) > 1000.0) {
2426 //std::cout << "c : " << prev_t->c_func() << std::endl;
2433 if (!(*i)->is_tempo()) {
2434 m = static_cast<MeterSection*> (*i);
2435 if (prev_m && m->position_lock_style() == AudioTime) {
2436 const TempoSection* t = &tempo_section_at_minute_locked (metrics, minute_at_frame (m->frame() - 1));
2437 const double nascent_m_minute = t->minute_at_pulse (m->pulse());
2438 /* Here we check that a preceding section of music doesn't overlap a subsequent one.
2440 if (t && (nascent_m_minute > m->minute() || nascent_m_minute < 0.0)) {
2454 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
2456 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2458 if ((*i)->is_tempo()) {
2459 t = static_cast<TempoSection*> (*i);
2460 if (!t->movable()) {
2461 t->set_active (true);
2464 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
2465 t->set_active (false);
2467 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
2468 t->set_active (true);
2469 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
2478 TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const double& minute)
2480 TempoSection* prev_t = 0;
2481 TempoSection* section_prev = 0;
2482 double first_m_minute = 0.0;
2484 /* can't move a tempo before the first meter */
2485 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2487 if (!(*i)->is_tempo()) {
2488 m = static_cast<MeterSection*> (*i);
2489 if (!m->movable()) {
2490 first_m_minute = m->minute();
2495 if (section->movable() && minute <= first_m_minute) {
2499 section->set_active (true);
2500 section->set_minute (minute);
2502 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2504 if ((*i)->is_tempo()) {
2505 t = static_cast<TempoSection*> (*i);
2512 section_prev = prev_t;
2513 if (t->locked_to_meter()) {
2518 if (t->position_lock_style() == MusicTime) {
2519 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->note_types_per_minute(), t->pulse()));
2520 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
2522 prev_t->set_c_func (prev_t->compute_c_func_minute (t->note_types_per_minute(), t->minute()));
2523 if (!t->locked_to_meter()) {
2524 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
2533 section_prev->set_c_func (section_prev->compute_c_func_minute (section->note_types_per_minute(), minute));
2534 if (!section->locked_to_meter()) {
2535 section->set_pulse (section_prev->pulse_at_ntpm (section->note_types_per_minute(), minute));
2540 recompute_tempi (imaginary);
2542 if (check_solved (imaginary)) {
2545 dunp (imaginary, std::cout);
2549 MetricSectionFrameSorter fcmp;
2550 imaginary.sort (fcmp);
2552 recompute_tempi (imaginary);
2554 if (check_solved (imaginary)) {
2562 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2564 TempoSection* prev_t = 0;
2565 TempoSection* section_prev = 0;
2567 section->set_pulse (pulse);
2569 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2571 if ((*i)->is_tempo()) {
2572 t = static_cast<TempoSection*> (*i);
2576 if (!t->movable()) {
2583 section_prev = prev_t;
2586 if (t->position_lock_style() == MusicTime) {
2587 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->note_types_per_minute(), t->pulse()));
2588 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
2590 prev_t->set_c_func (prev_t->compute_c_func_minute (t->note_types_per_minute(), t->minute()));
2591 if (!t->locked_to_meter()) {
2592 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
2601 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->note_types_per_minute(), pulse));
2602 section->set_minute (section_prev->minute_at_ntpm (section->note_types_per_minute(), pulse));
2606 recompute_tempi (imaginary);
2608 if (check_solved (imaginary)) {
2611 dunp (imaginary, std::cout);
2615 MetricSectionSorter cmp;
2616 imaginary.sort (cmp);
2618 recompute_tempi (imaginary);
2620 * XX need a restriction here, but only for this case,
2621 * as audio locked tempos don't interact in the same way.
2623 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2625 * |50 bpm |250 bpm |60 bpm
2626 * drag 250 to the pulse after 60->
2627 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2629 if (check_solved (imaginary)) {
2637 TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const double& minute)
2639 /* disallow moving first meter past any subsequent one, and any movable meter before the first one */
2640 const MeterSection* other = &meter_section_at_minute_locked (imaginary, minute);
2641 if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->minute() >= minute)) {
2645 if (!section->movable()) {
2646 /* lock the first tempo to our first meter */
2647 if (!set_active_tempos (imaginary, section->frame_at_minute (minute))) {
2652 TempoSection* meter_locked_tempo = 0;
2654 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2656 if ((*ii)->is_tempo()) {
2657 t = static_cast<TempoSection*> (*ii);
2658 if ((t->locked_to_meter() || !t->movable()) && t->minute() == section->minute()) {
2659 meter_locked_tempo = t;
2665 if (!meter_locked_tempo) {
2669 MeterSection* prev_m = 0;
2671 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2672 bool solved = false;
2674 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2676 if (!(*i)->is_tempo()) {
2677 m = static_cast<MeterSection*> (*i);
2679 if (prev_m && section->movable()) {
2680 const double beats = (pulse_at_minute_locked (imaginary, minute) - prev_m->pulse()) * prev_m->note_divisor();
2681 if (beats + prev_m->beat() < section->beat()) {
2682 /* set the section pulse according to its musical position,
2683 * as an earlier time than this has been requested.
2685 const double new_pulse = ((section->beat() - prev_m->beat())
2686 / prev_m->note_divisor()) + prev_m->pulse();
2688 tempo_copy->set_position_lock_style (MusicTime);
2689 if ((solved = solve_map_pulse (future_map, tempo_copy, new_pulse))) {
2690 meter_locked_tempo->set_position_lock_style (MusicTime);
2691 section->set_position_lock_style (MusicTime);
2692 section->set_pulse (new_pulse);
2693 solve_map_pulse (imaginary, meter_locked_tempo, new_pulse);
2694 meter_locked_tempo->set_position_lock_style (AudioTime);
2695 section->set_position_lock_style (AudioTime);
2696 section->set_minute (meter_locked_tempo->minute());
2702 Metrics::const_iterator d = future_map.begin();
2703 while (d != future_map.end()) {
2712 /* all is ok. set section's locked tempo if allowed.
2713 possibly disallowed if there is an adjacent audio-locked tempo.
2714 XX this check could possibly go. its never actually happened here.
2716 MeterSection* meter_copy = const_cast<MeterSection*>
2717 (&meter_section_at_minute_locked (future_map, section->minute()));
2719 meter_copy->set_minute (minute);
2721 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2722 section->set_minute (minute);
2723 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2724 / prev_m->note_divisor()) + prev_m->pulse());
2725 solve_map_minute (imaginary, meter_locked_tempo, minute);
2730 Metrics::const_iterator d = future_map.begin();
2731 while (d != future_map.end()) {
2741 /* not movable (first meter atm) */
2743 tempo_copy->set_minute (minute);
2744 tempo_copy->set_pulse (0.0);
2746 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2747 section->set_minute (minute);
2748 meter_locked_tempo->set_minute (minute);
2749 meter_locked_tempo->set_pulse (0.0);
2750 solve_map_minute (imaginary, meter_locked_tempo, minute);
2755 Metrics::const_iterator d = future_map.begin();
2756 while (d != future_map.end()) {
2765 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2766 section->set_beat (b_bbt);
2767 section->set_pulse (0.0);
2777 MetricSectionFrameSorter fcmp;
2778 imaginary.sort (fcmp);
2780 recompute_meters (imaginary);
2786 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2788 /* disallow setting section to an existing meter's bbt */
2789 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2791 if (!(*i)->is_tempo()) {
2792 m = static_cast<MeterSection*> (*i);
2793 if (m != section && m->bbt().bars == when.bars) {
2799 MeterSection* prev_m = 0;
2800 MeterSection* section_prev = 0;
2802 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2804 if (!(*i)->is_tempo()) {
2805 m = static_cast<MeterSection*> (*i);
2806 pair<double, BBT_Time> b_bbt;
2807 double new_pulse = 0.0;
2809 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2810 section_prev = prev_m;
2811 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2812 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2813 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2815 section->set_beat (b_bbt);
2816 section->set_pulse (pulse);
2817 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
2822 if (m->position_lock_style() == AudioTime) {
2823 TempoSection* meter_locked_tempo = 0;
2825 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2827 if ((*ii)->is_tempo()) {
2828 t = static_cast<TempoSection*> (*ii);
2829 if ((t->locked_to_meter() || !t->movable()) && t->frame() == m->frame()) {
2830 meter_locked_tempo = t;
2836 if (!meter_locked_tempo) {
2841 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2843 if (beats + prev_m->beat() != m->beat()) {
2844 /* tempo/ meter change caused a change in beat (bar). */
2845 b_bbt = make_pair (beats + prev_m->beat()
2846 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2847 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2848 } else if (m->movable()) {
2849 b_bbt = make_pair (m->beat(), m->bbt());
2850 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2853 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2856 meter_locked_tempo->set_pulse (new_pulse);
2857 m->set_beat (b_bbt);
2858 m->set_pulse (new_pulse);
2862 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2863 if (beats + prev_m->beat() != m->beat()) {
2864 /* tempo/ meter change caused a change in beat (bar). */
2865 b_bbt = make_pair (beats + prev_m->beat()
2866 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2868 b_bbt = make_pair (beats + prev_m->beat()
2871 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2872 m->set_beat (b_bbt);
2873 m->set_pulse (new_pulse);
2874 m->set_minute (minute_at_pulse_locked (imaginary, new_pulse));
2881 if (!section_prev) {
2883 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
2884 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2885 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
2887 section->set_beat (b_bbt);
2888 section->set_pulse (pulse);
2889 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
2892 MetricSectionSorter cmp;
2893 imaginary.sort (cmp);
2895 recompute_meters (imaginary);
2900 /** places a copy of _metrics into copy and returns a pointer
2901 * to section's equivalent in copy.
2904 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
2906 TempoSection* ret = 0;
2908 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2911 if ((*i)->is_tempo()) {
2912 t = static_cast<TempoSection*> (*i);
2914 ret = new TempoSection (*t);
2915 copy.push_back (ret);
2919 TempoSection* cp = new TempoSection (*t);
2920 copy.push_back (cp);
2922 if (!(*i)->is_tempo()) {
2923 m = static_cast<MeterSection *> (*i);
2924 MeterSection* cp = new MeterSection (*m);
2925 copy.push_back (cp);
2933 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
2935 MeterSection* ret = 0;
2937 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2940 if ((*i)->is_tempo()) {
2941 t = static_cast<TempoSection*> (*i);
2942 TempoSection* cp = new TempoSection (*t);
2943 copy.push_back (cp);
2946 if (!(*i)->is_tempo()) {
2947 m = static_cast<MeterSection *> (*i);
2949 ret = new MeterSection (*m);
2950 copy.push_back (ret);
2953 MeterSection* cp = new MeterSection (*m);
2954 copy.push_back (cp);
2961 /** answers the question "is this a valid beat position for this tempo section?".
2962 * it returns true if the tempo section can be moved to the requested bbt position,
2963 * leaving the tempo map in a solved state.
2964 * @param ts the tempo section to be moved
2965 * @param bbt the requested new position for the tempo section
2966 * @return true if the tempo section can be moved to the position, otherwise false.
2969 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2972 TempoSection* tempo_copy = 0;
2975 Glib::Threads::RWLock::ReaderLock lm (lock);
2976 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
2982 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
2984 Metrics::const_iterator d = copy.begin();
2985 while (d != copy.end()) {
2994 * 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,
2995 * taking any possible reordering as a consequence of this into account.
2996 * @param section - the section to be altered
2997 * @param bbt - the BBT time where the altered tempo will fall
2998 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
3000 pair<double, framepos_t>
3001 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
3004 pair<double, framepos_t> ret = make_pair (0.0, 0);
3006 Glib::Threads::RWLock::ReaderLock lm (lock);
3008 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
3010 const double beat = beat_at_bbt_locked (future_map, bbt);
3012 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
3013 ret.first = tempo_copy->pulse();
3014 ret.second = tempo_copy->frame();
3016 ret.first = section->pulse();
3017 ret.second = section->frame();
3020 Metrics::const_iterator d = future_map.begin();
3021 while (d != future_map.end()) {
3028 /** moves a TempoSection to a specified position.
3029 * @param ts - the section to be moved
3030 * @param frame - the new position in frames for the tempo
3031 * @param sub_num - the snap division to use if using musical time.
3033 * if sub_num is non-zero, the frame position is used to calculate an exact
3036 * -1 | snap to bars (meter-based)
3037 * 0 | no snap - use audio frame for musical position
3038 * 1 | snap to meter-based (BBT) beat
3039 * >1 | snap to quarter-note subdivision (i.e. 4 will snap to sixteenth notes)
3041 * this follows the snap convention in the gui.
3042 * if sub_num is zero, the musical position will be taken from the supplied frame.
3045 TempoMap::gui_move_tempo (TempoSection* ts, const framepos_t& frame, const int& sub_num)
3049 if (ts->position_lock_style() == MusicTime) {
3051 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
3052 Glib::Threads::RWLock::WriterLock lm (lock);
3053 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3055 tempo_copy->set_position_lock_style (AudioTime);
3057 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3058 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
3059 const double pulse = pulse_at_beat_locked (future_map, beat);
3061 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3062 solve_map_pulse (_metrics, ts, pulse);
3063 recompute_meters (_metrics);
3071 Glib::Threads::RWLock::WriterLock lm (lock);
3072 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3074 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3076 /* We're moving the object that defines the grid while snapping to it...
3077 * Placing the ts at the beat corresponding to the requested frame may shift the
3078 * grid in such a way that the mouse is left hovering over a completerly different division,
3079 * causing jittering when the mouse next moves (esp. large tempo deltas).
3080 * To avoid this, place the ts at the requested frame in a dummy map
3081 * then find the closest beat subdivision to that frame in the dummy.
3082 * This alters the snap behaviour slightly in that we snap to beat divisions
3083 * in the future map rather than the existing one.
3085 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
3086 const double pulse = pulse_at_beat_locked (future_map, beat);
3088 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3089 /* snapping to a grid. force MusicTime temporarily. */
3090 ts->set_position_lock_style (MusicTime);
3091 solve_map_pulse (_metrics, ts, pulse);
3092 ts->set_position_lock_style (AudioTime);
3094 recompute_meters (_metrics);
3097 solve_map_minute (_metrics, ts, minute_at_frame (frame));
3098 recompute_meters (_metrics);
3104 Metrics::const_iterator d = future_map.begin();
3105 while (d != future_map.end()) {
3110 MetricPositionChanged (); // Emit Signal
3113 /** moves a MeterSection to a specified position.
3114 * @param ms - the section to be moved
3115 * @param frame - the new position in frames for the meter
3117 * as a meter cannot snap to anything but bars,
3118 * the supplied frame is rounded to the nearest bar, possibly
3119 * leaving the meter position unchanged.
3122 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
3126 if (ms->position_lock_style() == AudioTime) {
3129 Glib::Threads::RWLock::WriterLock lm (lock);
3130 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3132 if (solve_map_minute (future_map, copy, minute_at_frame (frame))) {
3133 solve_map_minute (_metrics, ms, minute_at_frame (frame));
3134 recompute_tempi (_metrics);
3139 Glib::Threads::RWLock::WriterLock lm (lock);
3140 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3142 const double beat = beat_at_minute_locked (_metrics, minute_at_frame (frame));
3143 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
3145 if (solve_map_bbt (future_map, copy, bbt)) {
3146 solve_map_bbt (_metrics, ms, bbt);
3147 recompute_tempi (_metrics);
3152 Metrics::const_iterator d = future_map.begin();
3153 while (d != future_map.end()) {
3158 MetricPositionChanged (); // Emit Signal
3162 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
3165 bool can_solve = false;
3167 Glib::Threads::RWLock::WriterLock lm (lock);
3168 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3169 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3170 recompute_tempi (future_map);
3172 if (check_solved (future_map)) {
3173 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3174 recompute_map (_metrics);
3179 Metrics::const_iterator d = future_map.begin();
3180 while (d != future_map.end()) {
3185 MetricPositionChanged (); // Emit Signal
3191 TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame)
3194 Ts (future prev_t) Tnext
3197 |----------|----------
3204 Glib::Threads::RWLock::WriterLock lm (lock);
3210 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3211 TempoSection* prev_to_prev_t = 0;
3212 const frameoffset_t fr_off = end_frame - frame;
3214 if (prev_t && prev_t->pulse() > 0.0) {
3215 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_minute_locked (future_map, minute_at_frame (prev_t->frame() - 1)));
3218 TempoSection* next_t = 0;
3219 for (Metrics::iterator i = future_map.begin(); i != future_map.end(); ++i) {
3220 TempoSection* t = 0;
3221 if ((*i)->is_tempo()) {
3222 t = static_cast<TempoSection*> (*i);
3223 if (t->frame() > ts->frame()) {
3229 /* minimum allowed measurement distance in frames */
3230 const framepos_t min_dframe = 2;
3232 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
3233 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
3235 double contribution = 0.0;
3237 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3238 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3241 const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
3243 const double start_pulse = prev_t->pulse_at_minute (minute_at_frame (frame));
3244 const double end_pulse = prev_t->pulse_at_minute (minute_at_frame (end_frame));
3248 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
3250 if (prev_t->position_lock_style() == MusicTime) {
3251 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3252 if (frame > prev_to_prev_t->frame() + min_dframe && (frame + prev_t_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
3254 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame())
3255 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
3257 new_bpm = prev_t->note_types_per_minute();
3260 /* prev to prev is irrelevant */
3262 if (start_pulse > prev_t->pulse() && end_pulse > prev_t->pulse()) {
3263 new_bpm = prev_t->note_types_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
3265 new_bpm = prev_t->note_types_per_minute();
3270 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3271 if (frame > prev_to_prev_t->frame() + min_dframe && end_frame > prev_to_prev_t->frame() + min_dframe) {
3273 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame())
3274 / (double) ((end_frame) - prev_to_prev_t->frame()));
3276 new_bpm = prev_t->note_types_per_minute();
3279 /* prev_to_prev_t is irrelevant */
3281 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3282 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
3284 new_bpm = prev_t->note_types_per_minute();
3290 double frame_ratio = 1.0;
3291 double pulse_ratio = 1.0;
3292 const double pulse_pos = frame;
3294 if (prev_to_prev_t) {
3295 if (pulse_pos > prev_to_prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_to_prev_t->frame() + min_dframe) {
3296 frame_ratio = (((pulse_pos - fr_off) - prev_to_prev_t->frame()) / (double) ((pulse_pos) - prev_to_prev_t->frame()));
3298 if (end_pulse > prev_to_prev_t->pulse() && start_pulse > prev_to_prev_t->pulse()) {
3299 pulse_ratio = ((start_pulse - prev_to_prev_t->pulse()) / (end_pulse - prev_to_prev_t->pulse()));
3302 if (pulse_pos > prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_t->frame() + min_dframe) {
3303 frame_ratio = (((pulse_pos - fr_off) - prev_t->frame()) / (double) ((pulse_pos) - prev_t->frame()));
3305 pulse_ratio = (start_pulse / end_pulse);
3307 new_bpm = prev_t->note_types_per_minute() * (pulse_ratio * frame_ratio);
3310 /* don't clamp and proceed here.
3311 testing has revealed that this can go negative,
3312 which is an entirely different thing to just being too low.
3314 if (new_bpm < 0.5) {
3317 new_bpm = min (new_bpm, (double) 1000.0);
3318 prev_t->set_note_types_per_minute (new_bpm);
3319 recompute_tempi (future_map);
3320 recompute_meters (future_map);
3322 if (check_solved (future_map)) {
3323 ts->set_note_types_per_minute (new_bpm);
3324 recompute_tempi (_metrics);
3325 recompute_meters (_metrics);
3329 Metrics::const_iterator d = future_map.begin();
3330 while (d != future_map.end()) {
3335 MetricPositionChanged (); // Emit Signal
3338 /** Returns the exact bbt-based beat corresponding to the bar, beat or quarter note subdivision nearest to
3339 * the supplied frame, possibly returning a negative value.
3341 * @param frame The session frame position.
3342 * @param sub_num The subdivision to use when rounding the beat.
3343 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3344 * Positive integers indicate quarter note (non BBT) divisions.
3345 * 0 indicates that the returned beat should not be rounded (equivalent to quarter_note_at_frame()).
3346 * @return The beat position of the supplied frame.
3348 * when working to a musical grid, the use of sub_nom indicates that
3349 * the position should be interpreted musically.
3351 * it effectively snaps to meter bars, meter beats or quarter note divisions
3352 * (as per current gui convention) and returns a musical position independent of frame rate.
3354 * If the supplied frame lies before the first meter, the return will be negative,
3355 * in which case the returned beat uses the first meter (for BBT subdivisions) and
3356 * the continuation of the tempo curve (backwards).
3358 * This function is sensitive to tempo and meter.
3361 TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num)
3363 Glib::Threads::RWLock::ReaderLock lm (lock);
3365 return exact_beat_at_frame_locked (_metrics, frame, sub_num);
3369 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t divisions)
3371 return beat_at_pulse_locked (_metrics, exact_qn_at_frame_locked (metrics, frame, divisions) / 4.0);
3374 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
3375 * the supplied frame, possibly returning a negative value.
3377 * @param frame The session frame position.
3378 * @param sub_num The subdivision to use when rounding the quarter note.
3379 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3380 * Positive integers indicate quarter note (non BBT) divisions.
3381 * 0 indicates that the returned quarter note should not be rounded (equivalent to quarter_note_at_frame()).
3382 * @return The quarter note position of the supplied frame.
3384 * When working to a musical grid, the use of sub_nom indicates that
3385 * the frame position should be interpreted musically.
3387 * it effectively snaps to meter bars, meter beats or quarter note divisions
3388 * (as per current gui convention) and returns a musical position independent of frame rate.
3390 * If the supplied frame lies before the first meter, the return will be negative,
3391 * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
3392 * the continuation of the tempo curve (backwards).
3394 * This function is tempo-sensitive.
3397 TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num)
3399 Glib::Threads::RWLock::ReaderLock lm (lock);
3401 return exact_qn_at_frame_locked (_metrics, frame, sub_num);
3405 TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num)
3407 double qn = quarter_note_at_minute_locked (metrics, minute_at_frame (frame));
3410 qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
3411 } else if (sub_num == 1) {
3412 /* the gui requested exact musical (BBT) beat */
3413 qn = quarter_note_at_beat_locked (metrics, floor (beat_at_minute_locked (metrics, minute_at_frame (frame)) + 0.5));
3414 } else if (sub_num == -1) {
3416 Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
3420 const double prev_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3422 const double next_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3424 if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
3434 /** returns the frame duration of the supplied BBT time at a specified frame position in the tempo map.
3435 * @param pos the frame position in the tempo map.
3436 * @param bbt the distance in BBT time from pos to calculate.
3437 * @param dir the rounding direction..
3438 * @return the duration in frames between pos and bbt
3441 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
3443 Glib::Threads::RWLock::ReaderLock lm (lock);
3445 BBT_Time pos_bbt = bbt_at_minute_locked (_metrics, minute_at_frame (pos));
3446 const framecnt_t offset = frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3447 const double divisions = meter_section_at_minute_locked (_metrics, minute_at_frame (pos)).divisions_per_bar();
3450 pos_bbt.bars += bbt.bars;
3452 pos_bbt.ticks += bbt.ticks;
3453 if ((double) pos_bbt.ticks > BBT_Time::ticks_per_beat) {
3455 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3458 pos_bbt.beats += bbt.beats;
3459 if ((double) pos_bbt.beats > divisions) {
3461 pos_bbt.beats -= divisions;
3464 return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt)) - offset;
3466 pos_bbt.bars -= bbt.bars;
3468 if (pos_bbt.ticks < bbt.ticks) {
3469 if (pos_bbt.beats == 1) {
3471 pos_bbt.beats = divisions;
3475 pos_bbt.ticks = BBT_Time::ticks_per_beat - (bbt.ticks - pos_bbt.ticks);
3477 pos_bbt.ticks -= bbt.ticks;
3480 if (pos_bbt.beats <= bbt.beats) {
3482 pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
3484 pos_bbt.beats -= bbt.beats;
3487 return offset - frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3494 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
3496 return round_to_type (fr, dir, Bar);
3500 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
3502 return round_to_type (fr, dir, Beat);
3506 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3508 Glib::Threads::RWLock::ReaderLock lm (lock);
3509 uint32_t ticks = (uint32_t) floor (max (0.0, beat_at_minute_locked (_metrics, minute_at_frame (fr))) * BBT_Time::ticks_per_beat);
3510 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3511 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3513 ticks -= beats * BBT_Time::ticks_per_beat;
3516 /* round to next (or same iff dir == RoundUpMaybe) */
3518 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3520 if (mod == 0 && dir == RoundUpMaybe) {
3521 /* right on the subdivision, which is fine, so do nothing */
3523 } else if (mod == 0) {
3524 /* right on the subdivision, so the difference is just the subdivision ticks */
3525 ticks += ticks_one_subdivisions_worth;
3528 /* not on subdivision, compute distance to next subdivision */
3530 ticks += ticks_one_subdivisions_worth - mod;
3533 if (ticks >= BBT_Time::ticks_per_beat) {
3534 ticks -= BBT_Time::ticks_per_beat;
3536 } else if (dir < 0) {
3538 /* round to previous (or same iff dir == RoundDownMaybe) */
3540 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3542 if (difference == 0 && dir == RoundDownAlways) {
3543 /* right on the subdivision, but force-rounding down,
3544 so the difference is just the subdivision ticks */
3545 difference = ticks_one_subdivisions_worth;
3548 if (ticks < difference) {
3549 ticks = BBT_Time::ticks_per_beat - ticks;
3551 ticks -= difference;
3555 /* round to nearest */
3558 /* compute the distance to the previous and next subdivision */
3560 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3562 /* closer to the next subdivision, so shift forward */
3564 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3566 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3568 if (ticks > BBT_Time::ticks_per_beat) {
3570 ticks -= BBT_Time::ticks_per_beat;
3571 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3574 } else if (rem > 0) {
3576 /* closer to previous subdivision, so shift backward */
3580 /* can't go backwards past zero, so ... */
3583 /* step back to previous beat */
3585 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3586 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3588 ticks = lrint (ticks - rem);
3589 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3592 /* on the subdivision, do nothing */
3596 const framepos_t ret_frame = frame_at_minute (minute_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat)));
3602 TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3604 Glib::Threads::RWLock::ReaderLock lm (lock);
3605 uint32_t ticks = (uint32_t) floor (max (0.0, quarter_note_at_minute_locked (_metrics, minute_at_frame (fr))) * BBT_Time::ticks_per_beat);
3606 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3607 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3609 ticks -= beats * BBT_Time::ticks_per_beat;
3612 /* round to next (or same iff dir == RoundUpMaybe) */
3614 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3616 if (mod == 0 && dir == RoundUpMaybe) {
3617 /* right on the subdivision, which is fine, so do nothing */
3619 } else if (mod == 0) {
3620 /* right on the subdivision, so the difference is just the subdivision ticks */
3621 ticks += ticks_one_subdivisions_worth;
3624 /* not on subdivision, compute distance to next subdivision */
3626 ticks += ticks_one_subdivisions_worth - mod;
3629 if (ticks >= BBT_Time::ticks_per_beat) {
3630 ticks -= BBT_Time::ticks_per_beat;
3632 } else if (dir < 0) {
3634 /* round to previous (or same iff dir == RoundDownMaybe) */
3636 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3638 if (difference == 0 && dir == RoundDownAlways) {
3639 /* right on the subdivision, but force-rounding down,
3640 so the difference is just the subdivision ticks */
3641 difference = ticks_one_subdivisions_worth;
3644 if (ticks < difference) {
3645 ticks = BBT_Time::ticks_per_beat - ticks;
3647 ticks -= difference;
3651 /* round to nearest */
3654 /* compute the distance to the previous and next subdivision */
3656 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3658 /* closer to the next subdivision, so shift forward */
3660 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3662 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3664 if (ticks > BBT_Time::ticks_per_beat) {
3666 ticks -= BBT_Time::ticks_per_beat;
3667 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3670 } else if (rem > 0) {
3672 /* closer to previous subdivision, so shift backward */
3676 /* can't go backwards past zero, so ... */
3679 /* step back to previous beat */
3681 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3682 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3684 ticks = lrint (ticks - rem);
3685 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3688 /* on the subdivision, do nothing */
3692 const framepos_t ret_frame = frame_at_minute (minute_at_quarter_note_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat)));
3698 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
3700 Glib::Threads::RWLock::ReaderLock lm (lock);
3702 const double beat_at_framepos = max (0.0, beat_at_minute_locked (_metrics, minute_at_frame (frame)));
3703 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
3708 /* find bar previous to 'frame' */
3711 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3713 } else if (dir > 0) {
3714 /* find bar following 'frame' */
3718 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3720 /* true rounding: find nearest bar */
3721 framepos_t raw_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3724 framepos_t prev_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3726 framepos_t next_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3728 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
3739 return frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos)));
3740 } else if (dir > 0) {
3741 return frame_at_minute (minute_at_beat_locked (_metrics, ceil (beat_at_framepos)));
3743 return frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5)));
3752 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
3753 framepos_t lower, framepos_t upper, uint32_t bar_mod)
3755 Glib::Threads::RWLock::ReaderLock lm (lock);
3756 int32_t cnt = ceil (beat_at_minute_locked (_metrics, minute_at_frame (lower)));
3758 /* although the map handles negative beats, bbt doesn't. */
3763 if (minute_at_beat_locked (_metrics, cnt) >= minute_at_frame (upper)) {
3767 while (pos >= 0 && pos < upper) {
3768 pos = frame_at_minute (minute_at_beat_locked (_metrics, cnt));
3769 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
3770 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
3771 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
3772 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c_func()));
3776 BBT_Time bbt = bbt_at_minute_locked (_metrics, minute_at_frame (lower));
3781 bbt.bars -= bbt.bars % bar_mod;
3785 while (pos >= 0 && pos < upper) {
3786 pos = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3787 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
3788 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
3789 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c_func()));
3790 bbt.bars += bar_mod;
3796 TempoMap::tempo_section_at_frame (framepos_t frame) const
3798 Glib::Threads::RWLock::ReaderLock lm (lock);
3800 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
3804 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute) const
3806 TempoSection* prev = 0;
3810 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3812 if ((*i)->is_tempo()) {
3813 t = static_cast<TempoSection*> (*i);
3817 if (prev && t->minute() > minute) {
3827 abort(); /*NOTREACHED*/
3834 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3836 TempoSection* prev_t = 0;
3837 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
3841 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3842 if ((*i)->is_tempo()) {
3843 t = static_cast<TempoSection*> (*i);
3844 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
3854 /* don't use this to calculate length (the tempo is only correct for this frame).
3855 do that stuff based on the beat_at_frame and frame_at_beat api
3858 TempoMap::frames_per_quarter_note_at (const framepos_t& frame, const framecnt_t& sr) const
3860 Glib::Threads::RWLock::ReaderLock lm (lock);
3862 const TempoSection* ts_at = 0;
3863 const TempoSection* ts_after = 0;
3864 Metrics::const_iterator i;
3867 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3869 if ((*i)->is_tempo()) {
3870 t = static_cast<TempoSection*> (*i);
3874 if (ts_at && (*i)->frame() > frame) {
3883 return (60.0 * _frame_rate) / ts_at->tempo_at_minute (minute_at_frame (frame)).quarter_notes_per_minute();
3885 /* must be treated as constant tempo */
3886 return ts_at->frames_per_quarter_note (_frame_rate);
3890 TempoMap::meter_section_at_frame (framepos_t frame) const
3892 Glib::Threads::RWLock::ReaderLock lm (lock);
3893 return meter_section_at_minute_locked (_metrics, minute_at_frame (frame));
3897 TempoMap::meter_section_at_minute_locked (const Metrics& metrics, double minute) const
3899 Metrics::const_iterator i;
3900 MeterSection* prev = 0;
3904 for (i = metrics.begin(); i != metrics.end(); ++i) {
3906 if (!(*i)->is_tempo()) {
3907 m = static_cast<MeterSection*> (*i);
3909 if (prev && (*i)->minute() > minute) {
3919 abort(); /*NOTREACHED*/
3926 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3928 MeterSection* prev_m = 0;
3930 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3932 if (!(*i)->is_tempo()) {
3933 m = static_cast<MeterSection*> (*i);
3934 if (prev_m && m->beat() > beat) {
3945 TempoMap::meter_section_at_beat (double beat) const
3947 Glib::Threads::RWLock::ReaderLock lm (lock);
3948 return meter_section_at_beat_locked (_metrics, beat);
3952 TempoMap::meter_at_frame (framepos_t frame) const
3954 TempoMetric m (metric_at (frame));
3959 TempoMap::fix_legacy_session ()
3961 MeterSection* prev_m = 0;
3962 TempoSection* prev_t = 0;
3964 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3968 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3969 if (!m->movable()) {
3970 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3973 m->set_minute (0.0);
3974 m->set_position_lock_style (AudioTime);
3979 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
3980 + (m->bbt().beats - 1)
3981 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
3983 m->set_beat (start);
3984 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
3985 + (m->bbt().beats - 1)
3986 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
3987 m->set_pulse (start_beat / prev_m->note_divisor());
3990 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3996 if (!t->movable()) {
3998 t->set_minute (0.0);
3999 t->set_position_lock_style (AudioTime);
4005 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
4006 + (t->legacy_bbt().beats - 1)
4007 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
4009 t->set_pulse (beat / prev_m->note_divisor());
4011 /* really shouldn't happen but.. */
4012 t->set_pulse (beat / 4.0);
4021 TempoMap::get_state ()
4023 Metrics::const_iterator i;
4024 XMLNode *root = new XMLNode ("TempoMap");
4027 Glib::Threads::RWLock::ReaderLock lm (lock);
4028 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4029 root->add_child_nocopy ((*i)->get_state());
4037 TempoMap::set_state (const XMLNode& node, int /*version*/)
4040 Glib::Threads::RWLock::WriterLock lm (lock);
4043 XMLNodeConstIterator niter;
4044 Metrics old_metrics (_metrics);
4047 nlist = node.children();
4049 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
4050 XMLNode* child = *niter;
4052 if (child->name() == TempoSection::xml_state_node_name) {
4055 TempoSection* ts = new TempoSection (*child, _frame_rate);
4056 _metrics.push_back (ts);
4059 catch (failed_constructor& err){
4060 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4061 _metrics = old_metrics;
4062 old_metrics.clear();
4066 } else if (child->name() == MeterSection::xml_state_node_name) {
4069 MeterSection* ms = new MeterSection (*child, _frame_rate);
4070 _metrics.push_back (ms);
4073 catch (failed_constructor& err) {
4074 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4075 _metrics = old_metrics;
4076 old_metrics.clear();
4082 if (niter == nlist.end()) {
4083 MetricSectionSorter cmp;
4084 _metrics.sort (cmp);
4087 /* check for legacy sessions where bbt was the base musical unit for tempo */
4088 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4090 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
4091 if (t->legacy_bbt().bars != 0) {
4092 fix_legacy_session();
4099 /* check for multiple tempo/meters at the same location, which
4100 ardour2 somehow allowed.
4103 Metrics::iterator prev = _metrics.end();
4104 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4105 if (prev != _metrics.end()) {
4107 MeterSection* prev_m;
4109 TempoSection* prev_t;
4110 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
4111 if (prev_m->pulse() == ms->pulse()) {
4112 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4113 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4116 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
4117 if (prev_t->pulse() == ts->pulse()) {
4118 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4119 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4127 recompute_map (_metrics);
4129 Metrics::const_iterator d = old_metrics.begin();
4130 while (d != old_metrics.end()) {
4134 old_metrics.clear ();
4137 PropertyChanged (PropertyChange ());
4143 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
4145 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
4146 const MeterSection* m;
4147 const TempoSection* t;
4148 const TempoSection* prev_t = 0;
4150 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4152 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
4153 o << "Tempo @ " << *i << t->note_types_per_minute() << " BPM (pulse = 1/" << t->note_type()
4154 << " type= " << enum_2_string (t->type()) << ") " << " at pulse= " << t->pulse()
4155 << " minute= " << t->minute() << " frame= " << t->frame() << " (movable? " << t->movable() << ')'
4156 << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
4158 o << std::setprecision (17) << " current : " << t->note_types_per_minute()
4159 << " | " << t->pulse() << " | " << t->frame() << " | " << t->minute() << std::endl;
4160 o << " previous : " << prev_t->note_types_per_minute()
4161 << " | " << prev_t->pulse() << " | " << prev_t->frame() << " | " << prev_t->minute() << std::endl;
4162 o << " calculated : " << prev_t->tempo_at_pulse (t->pulse())
4163 << " | " << prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute())
4164 << " | " << frame_at_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()))
4165 << " | " << prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()) << std::endl;
4168 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
4169 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt()
4170 << " frame= " << m->frame() << " pulse: " << m->pulse() << " beat : " << m->beat()
4171 << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
4174 o << "------" << std::endl;
4178 TempoMap::n_tempos() const
4180 Glib::Threads::RWLock::ReaderLock lm (lock);
4183 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4184 if ((*i)->is_tempo()) {
4193 TempoMap::n_meters() const
4195 Glib::Threads::RWLock::ReaderLock lm (lock);
4198 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4199 if (!(*i)->is_tempo()) {
4208 TempoMap::insert_time (framepos_t where, framecnt_t amount)
4210 for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
4211 if ((*i)->frame() >= where && (*i)->movable ()) {
4215 if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
4216 gui_move_meter (ms, (*i)->frame() + amount);
4219 if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
4220 gui_move_tempo (ts, (*i)->frame() + amount, 0);
4225 PropertyChanged (PropertyChange ());
4229 TempoMap::remove_time (framepos_t where, framecnt_t amount)
4233 std::list<MetricSection*> metric_kill_list;
4235 TempoSection* last_tempo = NULL;
4236 MeterSection* last_meter = NULL;
4237 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
4238 bool meter_after = false; // is there a meter marker likewise?
4240 Glib::Threads::RWLock::WriterLock lm (lock);
4241 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4242 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
4243 metric_kill_list.push_back(*i);
4244 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
4247 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
4251 else if ((*i)->frame() >= where) {
4252 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
4253 (*i)->set_minute ((*i)->minute() - minute_at_frame (amount));
4254 if ((*i)->frame() == where) {
4255 // marker was immediately after end of range
4256 tempo_after = dynamic_cast<TempoSection*> (*i);
4257 meter_after = dynamic_cast<MeterSection*> (*i);
4263 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
4264 if (last_tempo && !tempo_after) {
4265 metric_kill_list.remove(last_tempo);
4266 last_tempo->set_minute (minute_at_frame (where));
4269 if (last_meter && !meter_after) {
4270 metric_kill_list.remove(last_meter);
4271 last_meter->set_minute (minute_at_frame (where));
4275 //remove all the remaining metrics
4276 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
4277 _metrics.remove(*i);
4282 recompute_map (_metrics);
4285 PropertyChanged (PropertyChange ());
4289 /** Add some (fractional) Beats to a session frame position, and return the result in frames.
4290 * pos can be -ve, if required.
4293 TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats quarter_note) const
4295 Glib::Threads::RWLock::ReaderLock lm (lock);
4297 return frame_at_minute (minute_at_quarter_note_locked (_metrics, quarter_note_at_minute_locked (_metrics, minute_at_frame (frame)) + quarter_note.to_double()));
4301 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
4303 Glib::Threads::RWLock::ReaderLock lm (lock);
4305 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_minute_locked (_metrics, minute_at_frame (pos)));
4306 pos_bbt.ticks += op.ticks;
4307 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
4309 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
4311 pos_bbt.beats += op.beats;
4312 /* the meter in effect will start on the bar */
4313 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();
4314 while (pos_bbt.beats >= divisions_per_bar + 1) {
4316 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4317 pos_bbt.beats -= divisions_per_bar;
4319 pos_bbt.bars += op.bars;
4321 return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
4324 /** Count the number of beats that are equivalent to distance when going forward,
4328 TempoMap::framewalk_to_qn (framepos_t pos, framecnt_t distance) const
4330 Glib::Threads::RWLock::ReaderLock lm (lock);
4332 return Evoral::Beats (quarter_note_at_minute_locked (_metrics, minute_at_frame (pos + distance)) - quarter_note_at_minute_locked (_metrics, minute_at_frame (pos)));
4336 bool operator() (const BBT_Time& a, const BBT_Time& b) {
4342 operator<< (std::ostream& o, const Meter& m) {
4343 return o << m.divisions_per_bar() << '/' << m.note_divisor();
4347 operator<< (std::ostream& o, const Tempo& t) {
4348 return o << t.note_types_per_minute() << " 1/" << t.note_type() << "'s per minute";
4352 operator<< (std::ostream& o, const MetricSection& section) {
4354 o << "MetricSection @ " << section.frame() << ' ';
4356 const TempoSection* ts;
4357 const MeterSection* ms;
4359 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
4360 o << *((const Tempo*) ts);
4361 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
4362 o << *((const Meter*) ms);