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();
338 TempoSection::pulse_at_frame (const framepos_t& f) const
340 if (_type == Constant || _c_func == 0.0) {
341 return (minute_at_frame (f - frame()) * pulses_per_minute()) + pulse();
344 return _pulse_at_time (minute_at_frame (f - frame())) + pulse();
348 TempoSection::frame_at_pulse (const double& p) const
350 if (_type == Constant || _c_func == 0.0) {
351 return frame_at_minute (((p - pulse()) / pulses_per_minute()) + minute());
354 return frame_at_minute (_time_at_pulse (p - pulse()) + minute());
362 Tt----|-----------------*|
363 Ta----|--------------|* |
369 _______________|___|____
370 time a t (next tempo)
373 Duration in beats at time a is the integral of some Tempo function.
374 In our case, the Tempo function (Tempo at time t) is
377 with function constant
382 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
383 b(t) = T0(e^(ct) - 1) / c
385 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:
386 t(b) = log((c.b / T0) + 1) / c
388 The time t at which Tempo T occurs is a as above:
389 t(T) = log(T / T0) / c
391 The beat at which a Tempo T occurs is:
394 The Tempo at which beat b occurs is:
397 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
398 Our problem is that we usually don't know t.
399 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.
400 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
401 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
403 By substituting our expanded t as a in the c function above, our problem is reduced to:
404 c = T0 (e^(log (Ta / T0)) - 1) / b
406 Of course the word 'beat' has been left loosely defined above.
407 In music, a beat is defined by the musical pulse (which comes from the tempo)
408 and the meter in use at a particular time (how many pulse divisions there are in one bar).
409 It would be more accurate to substitute the work 'pulse' for 'beat' above.
413 We can now store c for future time calculations.
414 If the following tempo section (the one that defines c in conjunction with this one)
415 is changed or moved, c is no longer valid.
417 The public methods are session-relative.
419 Most of this stuff is taken from this paper:
422 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
425 Zurich University of Arts
426 Institute for Computer Music and Sound Technology
428 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
432 /** compute this ramp's function constant from some tempo-pulse point
433 * @param end_npm end tempo (in note types per minute)
434 * @param end_pulse duration (pulses into global start) of some other position.
435 * @return the calculated function constant
438 TempoSection::compute_c_func_pulse (const double& end_npm, const double& end_pulse) const
440 double const log_tempo_ratio = log (end_npm / note_types_per_minute());
441 return (note_types_per_minute() * expm1 (log_tempo_ratio)) / ((end_pulse - pulse()) * _note_type);
444 /** compute the function constant from some tempo-time point.
445 * @param end_npm tempo (note types/min.)
446 * @param end_minute distance (in minutes) from session origin
447 * @return the calculated function constant
450 TempoSection::compute_c_func_minute (const double& end_npm, const double& end_minute) const
452 return c_func (end_npm, end_minute - minute());
455 /* position function */
457 TempoSection::a_func (double end_npm, double c_func) const
459 return log (end_npm / note_types_per_minute()) / c_func;
462 /*function constant*/
464 TempoSection::c_func (double end_npm, double end_time) const
466 return log (end_npm / note_types_per_minute()) / end_time;
469 /* tempo in note types per minute at time in minutes */
471 TempoSection::_tempo_at_time (const double& time) const
473 return exp (_c_func * time) * note_types_per_minute();
476 /* time in minutes at tempo in note types per minute */
478 TempoSection::_time_at_tempo (const double& npm) const
480 return log (npm / note_types_per_minute()) / _c_func;
483 /* pulse at tempo in note types per minute */
485 TempoSection::_pulse_at_tempo (const double& npm) const
487 return ((npm - note_types_per_minute()) / _c_func) / _note_type;
490 /* tempo in note types per minute at pulse */
492 TempoSection::_tempo_at_pulse (const double& pulse) const
494 return (pulse * _note_type * _c_func) + note_types_per_minute();
497 /* pulse at time in minutes */
499 TempoSection::_pulse_at_time (const double& time) const
501 return (expm1 (_c_func * time) * (note_types_per_minute() / _c_func)) / _note_type;
504 /* time in minutes at pulse */
506 TempoSection::_time_at_pulse (const double& pulse) const
508 return log1p ((_c_func * pulse * _note_type) / note_types_per_minute()) / _c_func;
511 /***********************************************************************/
513 const string MeterSection::xml_state_node_name = "Meter";
515 MeterSection::MeterSection (const XMLNode& node, const framecnt_t sample_rate)
516 : MetricSection (0.0, 0, MusicTime, false, sample_rate), Meter (TempoMap::default_meter())
518 XMLProperty const * prop;
523 framepos_t frame = 0;
524 pair<double, BBT_Time> start;
527 if ((prop = node.property ("start")) != 0) {
528 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
532 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
534 /* legacy session - start used to be in bbt*/
535 info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
540 if ((prop = node.property ("pulse")) != 0) {
541 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
542 error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
547 if ((prop = node.property ("beat")) != 0) {
548 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
549 error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
555 if ((prop = node.property ("bbt")) == 0) {
556 warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
557 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
561 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
562 throw failed_constructor();
568 if ((prop = node.property ("frame")) != 0) {
569 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
570 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
572 set_minute (minute_at_frame (frame));
575 if ((prop = node.property ("minute")) != 0) {
576 if (sscanf (prop->value().c_str(), "%lf", &minute) != 1) {
577 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
583 /* beats-per-bar is old; divisions-per-bar is new */
585 if ((prop = node.property ("divisions-per-bar")) == 0) {
586 if ((prop = node.property ("beats-per-bar")) == 0) {
587 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
588 throw failed_constructor();
591 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
592 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
593 throw failed_constructor();
596 if ((prop = node.property ("note-type")) == 0) {
597 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
598 throw failed_constructor();
600 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
601 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
602 throw failed_constructor();
605 if ((prop = node.property ("movable")) == 0) {
606 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
607 throw failed_constructor();
610 set_movable (string_is_affirmative (prop->value()));
612 if ((prop = node.property ("lock-style")) == 0) {
613 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
615 set_position_lock_style (MusicTime);
617 set_position_lock_style (AudioTime);
620 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
625 MeterSection::get_state() const
627 XMLNode *root = new XMLNode (xml_state_node_name);
631 snprintf (buf, sizeof (buf), "%lf", pulse());
632 root->add_property ("pulse", buf);
633 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
637 root->add_property ("bbt", buf);
638 snprintf (buf, sizeof (buf), "%lf", beat());
639 root->add_property ("beat", buf);
640 snprintf (buf, sizeof (buf), "%lf", _note_type);
641 root->add_property ("note-type", buf);
642 snprintf (buf, sizeof (buf), "%li", frame());
643 root->add_property ("frame", buf);
644 snprintf (buf, sizeof (buf), "%lf", minute());
645 root->add_property ("minute", buf);
646 root->add_property ("lock-style", enum_2_string (position_lock_style()));
647 snprintf (buf, sizeof (buf), "%lf", _divisions_per_bar);
648 root->add_property ("divisions-per-bar", buf);
649 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
650 root->add_property ("movable", buf);
655 /***********************************************************************/
659 Tempo determines the rate of musical pulse determined by its components
660 note types per minute - the rate per minute of the whole note divisor _note_type
661 note type - the division of whole notes (pulses) which occur at the rate of note types per minute.
662 Meter divides the musical pulse into measures and beats according to its components
666 TempoSection - translates between time, musical pulse and tempo.
667 has a musical location in whole notes (pulses).
668 has a time location in minutes.
669 Note that 'beats' in Tempo::note_types_per_minute() are in fact note types per minute.
670 (In the rest of tempo map,'beat' usually refers to accumulated BBT beats (pulse and meter based).
672 MeterSection - translates between BBT, meter-based beat and musical pulse.
673 has a musical location in whole notes (pulses)
674 has a musical location in meter-based beats
675 has a musical location in BBT time
676 has a time location expressed in minutes.
678 TempoSection and MeterSection may be locked to either audio or music (position lock style).
679 The lock style determines the location type to be kept as a reference when location is recalculated.
681 The first tempo and meter are special. they must move together, and are locked to audio.
682 Audio locked tempi which lie before the first meter are made inactive.
684 Recomputing the map is the process where the 'missing' location types are calculated.
685 We construct the tempo map by first using the locked location type of each section
686 to determine non-locked location types (pulse or minute position).
687 We then use this map to find the pulse or minute position of each meter (again depending on lock style).
689 Having done this, we can now traverse the Metrics list by pulse or minute
690 to query its relevant meter/tempo.
692 It is important to keep the _metrics in an order that makes sense.
693 Because ramped MusicTime and AudioTime tempos can interact with each other,
694 reordering is frequent. Care must be taken to keep _metrics in a solved state.
695 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
699 Music and audio-locked objects may seem interchangeable on the surface, but when translating
700 between audio samples and beat, remember that a sample is only a quantised approximation
701 of the actual time (in minutes) of a beat.
702 Thus if a gui user points to the frame occupying the start of a music-locked object on 1|3|0, it does not
703 mean that this frame is the actual location in time of 1|3|0.
705 You cannot use a frame measurement to determine beat distance except under special circumstances
706 (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).
708 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
709 sample space the user is operating at to be translated correctly to the object.
711 The current approach is to interpret the supplied frame using the grid division the user has currently selected.
712 If the user has no musical grid set, they are actually operating in sample space (even SMPTE frames are rounded to audio frame), so
713 the supplied audio frame is interpreted as the desired musical location (beat_at_frame()).
715 tldr: Beat, being a function of time, has nothing to do with sample rate, but time quantization can get in the way of precision.
717 When frame_at_beat() is called, the position calculation is performed in pulses and minutes.
718 The result is rounded to audio frames.
719 When beat_at_frame() is called, the frame is converted to minutes, with no rounding performed on the result.
722 frame_at_beat (beat_at_frame (frame)) == frame
724 beat_at_frame (frame_at_beat (beat)) != beat due to the time quantization of frame_at_beat().
726 Doing the second one will result in a beat distance error of up to 0.5 audio samples.
727 frames_between_quarter_notes () eliminats this effect when determining time duration
728 from Beats distance, or instead work in quarter-notes and/or beats and convert to frames last.
730 The above pointless example could instead do:
731 beat_at_quarter_note (quarter_note_at_beat (beat)) to avoid rounding.
733 The Shaggs - Things I Wonder
734 https://www.youtube.com/watch?v=9wQK6zMJOoQ
737 struct MetricSectionSorter {
738 bool operator() (const MetricSection* a, const MetricSection* b) {
739 return a->pulse() < b->pulse();
743 struct MetricSectionFrameSorter {
744 bool operator() (const MetricSection* a, const MetricSection* b) {
745 return a->frame() < b->frame();
749 TempoMap::TempoMap (framecnt_t fr)
752 BBT_Time start (1, 1, 0);
754 TempoSection *t = new TempoSection (0.0, 0.0, _default_tempo.note_types_per_minute(), _default_tempo.note_type(), TempoSection::Ramp, AudioTime, fr);
755 MeterSection *m = new MeterSection (0.0, 0.0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime, fr);
757 t->set_movable (false);
758 m->set_movable (false);
760 /* note: frame time is correct (zero) for both of these */
762 _metrics.push_back (t);
763 _metrics.push_back (m);
767 TempoMap::~TempoMap ()
769 Metrics::const_iterator d = _metrics.begin();
770 while (d != _metrics.end()) {
778 TempoMap::frame_at_minute (const double time) const
780 return (framepos_t) floor ((time * 60.0 * _frame_rate) + 0.5);
784 TempoMap::minute_at_frame (const framepos_t frame) const
786 return (frame / (double) _frame_rate) / 60.0;
790 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
792 bool removed = false;
795 Glib::Threads::RWLock::WriterLock lm (lock);
796 if ((removed = remove_tempo_locked (tempo))) {
797 if (complete_operation) {
798 recompute_map (_metrics);
803 if (removed && complete_operation) {
804 PropertyChanged (PropertyChange ());
809 TempoMap::remove_tempo_locked (const TempoSection& tempo)
813 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
814 if (dynamic_cast<TempoSection*> (*i) != 0) {
815 if (tempo.frame() == (*i)->frame()) {
816 if ((*i)->movable()) {
829 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
831 bool removed = false;
834 Glib::Threads::RWLock::WriterLock lm (lock);
835 if ((removed = remove_meter_locked (tempo))) {
836 if (complete_operation) {
837 recompute_map (_metrics);
842 if (removed && complete_operation) {
843 PropertyChanged (PropertyChange ());
848 TempoMap::remove_meter_locked (const MeterSection& meter)
851 if (meter.position_lock_style() == AudioTime) {
852 /* remove meter-locked tempo */
853 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
855 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
856 if (t->locked_to_meter() && meter.frame() == (*i)->frame()) {
865 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
866 if (dynamic_cast<MeterSection*> (*i) != 0) {
867 if (meter.frame() == (*i)->frame()) {
868 if ((*i)->movable()) {
881 TempoMap::do_insert (MetricSection* section)
883 bool need_add = true;
884 /* we only allow new meters to be inserted on beat 1 of an existing
888 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
890 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
892 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
893 corrected.second.beats = 1;
894 corrected.second.ticks = 0;
895 corrected.first = beat_at_bbt_locked (_metrics, corrected.second);
896 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
897 m->bbt(), corrected.second) << endmsg;
898 //m->set_pulse (corrected);
902 /* Look for any existing MetricSection that is of the same type and
903 in the same bar as the new one, and remove it before adding
904 the new one. Note that this means that if we find a matching,
905 existing section, we can break out of the loop since we're
906 guaranteed that there is only one such match.
909 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
911 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
912 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
913 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
914 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
916 if (tempo && insert_tempo) {
919 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
920 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
922 if (!tempo->movable()) {
924 /* can't (re)move this section, so overwrite
925 * its data content (but not its properties as
929 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
930 (*i)->set_position_lock_style (AudioTime);
932 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
933 t->set_type (insert_tempo->type());
943 } else if (meter && insert_meter) {
947 bool const ipm = insert_meter->position_lock_style() == MusicTime;
949 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
951 if (!meter->movable()) {
953 /* can't (re)move this section, so overwrite
954 * its data content (but not its properties as
958 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
959 (*i)->set_position_lock_style (AudioTime);
969 /* non-matching types, so we don't care */
973 /* Add the given MetricSection, if we didn't just reset an existing
978 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
979 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
982 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
983 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
986 bool const ipm = insert_meter->position_lock_style() == MusicTime;
987 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
992 } else if (insert_tempo) {
993 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
994 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
997 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
998 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
1005 _metrics.insert (i, section);
1006 //dump (_metrics, std::cout);
1009 /* user supplies the exact pulse if pls == MusicTime */
1011 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, ARDOUR::TempoSection::Type type, PositionLockStyle pls)
1013 TempoSection* ts = 0;
1015 Glib::Threads::RWLock::WriterLock lm (lock);
1016 ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), type, pls, true);
1020 PropertyChanged (PropertyChange ());
1026 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, TempoSection::Type type, PositionLockStyle pls)
1028 const bool locked_to_meter = ts.locked_to_meter();
1031 Glib::Threads::RWLock::WriterLock lm (lock);
1032 TempoSection& first (first_tempo());
1033 if (ts.frame() != first.frame()) {
1034 remove_tempo_locked (ts);
1035 add_tempo_locked (tempo, pulse, minute_at_frame (frame), type, pls, true, locked_to_meter);
1037 first.set_type (type);
1038 first.set_pulse (0.0);
1039 first.set_minute (minute_at_frame (frame));
1040 first.set_position_lock_style (AudioTime);
1042 /* cannot move the first tempo section */
1043 *static_cast<Tempo*>(&first) = tempo;
1044 recompute_map (_metrics);
1049 PropertyChanged (PropertyChange ());
1053 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, double minute
1054 , TempoSection::Type type, PositionLockStyle pls, bool recompute, bool locked_to_meter)
1056 TempoSection* t = new TempoSection (pulse, minute, tempo.note_types_per_minute(), tempo.note_type(), type, pls, _frame_rate);
1057 t->set_locked_to_meter (locked_to_meter);
1058 bool solved = false;
1063 if (pls == AudioTime) {
1064 solved = solve_map_minute (_metrics, t, t->minute());
1066 solved = solve_map_pulse (_metrics, t, t->pulse());
1068 recompute_meters (_metrics);
1071 if (!solved && recompute) {
1072 recompute_map (_metrics);
1079 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, PositionLockStyle pls)
1081 MeterSection* m = 0;
1083 Glib::Threads::RWLock::WriterLock lm (lock);
1084 m = add_meter_locked (meter, beat, where, pls, true);
1089 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1090 dump (_metrics, std::cerr);
1094 PropertyChanged (PropertyChange ());
1099 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, PositionLockStyle pls)
1102 Glib::Threads::RWLock::WriterLock lm (lock);
1103 const double beat = beat_at_bbt_locked (_metrics, where);
1106 remove_meter_locked (ms);
1107 add_meter_locked (meter, beat, where, pls, true);
1109 MeterSection& first (first_meter());
1110 TempoSection& first_t (first_tempo());
1111 /* cannot move the first meter section */
1112 *static_cast<Meter*>(&first) = meter;
1113 first.set_position_lock_style (AudioTime);
1114 first.set_pulse (0.0);
1115 //first.set_minute (minute_at_frame (frame));
1116 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1117 first.set_beat (beat);
1118 first_t.set_minute (first.minute());
1119 first_t.set_pulse (0.0);
1120 first_t.set_position_lock_style (AudioTime);
1121 recompute_map (_metrics);
1125 PropertyChanged (PropertyChange ());
1129 TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, PositionLockStyle pls, bool recompute)
1131 const MeterSection& prev_m = meter_section_at_minute_locked (_metrics, minute_at_beat_locked (_metrics, beat) - minute_at_frame (1));
1132 const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1133 const double time_minutes = minute_at_pulse_locked (_metrics, pulse);
1134 TempoSection* mlt = 0;
1136 if (pls == AudioTime) {
1137 /* add meter-locked tempo */
1138 mlt = add_tempo_locked (tempo_at_minute_locked (_metrics, time_minutes), pulse, time_minutes, TempoSection::Ramp, AudioTime, true, true);
1146 MeterSection* new_meter = new MeterSection (pulse, time_minutes, beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls, _frame_rate);
1147 bool solved = false;
1149 do_insert (new_meter);
1153 if (pls == AudioTime) {
1154 solved = solve_map_minute (_metrics, new_meter, time_minutes);
1156 solved = solve_map_bbt (_metrics, new_meter, where);
1157 /* required due to resetting the pulse of meter-locked tempi above.
1158 Arguably solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
1159 but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
1161 recompute_map (_metrics);
1165 if (!solved && recompute) {
1166 /* if this has failed to solve, there is little we can do other than to ensure that
1167 the new map is recalculated.
1169 warning << "Adding meter may have left the tempo map unsolved." << endmsg;
1170 recompute_map (_metrics);
1177 TempoMap::change_initial_tempo (double note_types_per_minute, double note_type)
1179 Tempo newtempo (note_types_per_minute, note_type);
1182 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1183 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1188 Glib::Threads::RWLock::WriterLock lm (lock);
1189 *((Tempo*) t) = newtempo;
1190 recompute_map (_metrics);
1192 PropertyChanged (PropertyChange ());
1199 TempoMap::change_existing_tempo_at (framepos_t where, double note_types_per_minute, double note_type)
1201 Tempo newtempo (note_types_per_minute, note_type);
1204 TempoSection* first;
1205 Metrics::iterator i;
1207 /* find the TempoSection immediately preceding "where"
1210 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1212 if ((*i)->frame() > where) {
1218 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1231 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1241 Glib::Threads::RWLock::WriterLock lm (lock);
1242 /* cannot move the first tempo section */
1243 *((Tempo*)prev) = newtempo;
1244 recompute_map (_metrics);
1247 PropertyChanged (PropertyChange ());
1251 TempoMap::first_meter () const
1253 const MeterSection *m = 0;
1255 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1256 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1261 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1262 abort(); /*NOTREACHED*/
1267 TempoMap::first_meter ()
1269 MeterSection *m = 0;
1271 /* CALLER MUST HOLD LOCK */
1273 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1274 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1279 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1280 abort(); /*NOTREACHED*/
1285 TempoMap::first_tempo () const
1287 const TempoSection *t = 0;
1289 /* CALLER MUST HOLD LOCK */
1291 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1292 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1296 if (!t->movable()) {
1302 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1303 abort(); /*NOTREACHED*/
1308 TempoMap::first_tempo ()
1310 TempoSection *t = 0;
1312 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1313 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1317 if (!t->movable()) {
1323 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1324 abort(); /*NOTREACHED*/
1328 TempoMap::recompute_tempi (Metrics& metrics)
1330 TempoSection* prev_t = 0;
1332 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1335 if ((*i)->is_tempo()) {
1336 t = static_cast<TempoSection*> (*i);
1340 if (!t->movable()) {
1348 if (t->position_lock_style() == AudioTime) {
1349 prev_t->set_c_func (prev_t->compute_c_func_minute (t->note_types_per_minute(), t->minute()));
1350 if (!t->locked_to_meter()) {
1351 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
1355 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->note_types_per_minute(), t->pulse()));
1356 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
1363 prev_t->set_c_func (0.0);
1366 /* tempos must be positioned correctly.
1367 the current approach is to use a meter's bbt time as its base position unit.
1368 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1369 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1372 TempoMap::recompute_meters (Metrics& metrics)
1374 MeterSection* meter = 0;
1375 MeterSection* prev_m = 0;
1377 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1378 if (!(*mi)->is_tempo()) {
1379 meter = static_cast<MeterSection*> (*mi);
1380 if (meter->position_lock_style() == AudioTime) {
1382 pair<double, BBT_Time> b_bbt;
1383 TempoSection* meter_locked_tempo = 0;
1384 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1386 if ((*ii)->is_tempo()) {
1387 t = static_cast<TempoSection*> (*ii);
1388 if ((t->locked_to_meter() || !t->movable()) && t->frame() == meter->frame()) {
1389 meter_locked_tempo = t;
1396 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1397 if (beats + prev_m->beat() != meter->beat()) {
1398 /* reordering caused a bbt change */
1399 b_bbt = make_pair (beats + prev_m->beat()
1400 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1401 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1403 } else if (meter->movable()) {
1404 b_bbt = make_pair (meter->beat(), meter->bbt());
1405 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1408 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1410 if (meter_locked_tempo) {
1411 meter_locked_tempo->set_pulse (pulse);
1413 meter->set_beat (b_bbt);
1414 meter->set_pulse (pulse);
1419 pair<double, BBT_Time> b_bbt;
1421 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1422 if (beats + prev_m->beat() != meter->beat()) {
1423 /* reordering caused a bbt change */
1424 b_bbt = make_pair (beats + prev_m->beat()
1425 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1427 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1429 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1431 /* shouldn't happen - the first is audio-locked */
1432 pulse = pulse_at_beat_locked (metrics, meter->beat());
1433 b_bbt = make_pair (meter->beat(), meter->bbt());
1436 meter->set_beat (b_bbt);
1437 meter->set_pulse (pulse);
1438 meter->set_minute (minute_at_pulse_locked (metrics, pulse));
1447 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1449 /* CALLER MUST HOLD WRITE LOCK */
1451 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1454 /* silly call from Session::process() during startup
1459 recompute_tempi (metrics);
1460 recompute_meters (metrics);
1464 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1466 Glib::Threads::RWLock::ReaderLock lm (lock);
1467 TempoMetric m (first_meter(), first_tempo());
1469 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1470 at something, because we insert the default tempo and meter during
1471 TempoMap construction.
1473 now see if we can find better candidates.
1476 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1478 if ((*i)->frame() > frame) {
1492 /* XX meters only */
1494 TempoMap::metric_at (BBT_Time bbt) const
1496 Glib::Threads::RWLock::ReaderLock lm (lock);
1497 TempoMetric m (first_meter(), first_tempo());
1499 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1500 at something, because we insert the default tempo and meter during
1501 TempoMap construction.
1503 now see if we can find better candidates.
1506 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1508 if (!(*i)->is_tempo()) {
1509 mw = static_cast<MeterSection*> (*i);
1510 BBT_Time section_start (mw->bbt());
1512 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1523 /** Returns the BBT (meter-based) beat corresponding to the supplied frame, possibly returning a negative value.
1524 * @param frame The session frame position.
1525 * @return The beat duration according to the tempo map at the supplied frame.
1527 * If the supplied frame lies before the first meter, the returned beat duration will be negative.
1528 * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
1530 * This function uses both tempo and meter.
1533 TempoMap::beat_at_frame (const framecnt_t& frame) const
1535 Glib::Threads::RWLock::ReaderLock lm (lock);
1537 return beat_at_minute_locked (_metrics, minute_at_frame (frame));
1540 /* This function uses both tempo and meter.*/
1542 TempoMap::beat_at_minute_locked (const Metrics& metrics, const double& minute) const
1544 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
1545 MeterSection* prev_m = 0;
1546 MeterSection* next_m = 0;
1548 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1549 if (!(*i)->is_tempo()) {
1550 if (prev_m && (*i)->minute() > minute) {
1551 next_m = static_cast<MeterSection*> (*i);
1554 prev_m = static_cast<MeterSection*> (*i);
1558 const double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
1560 /* audio locked meters fake their beat */
1561 if (next_m && next_m->beat() < beat) {
1562 return next_m->beat();
1568 /** Returns the frame corresponding to the supplied BBT (meter-based) beat.
1569 * @param beat The BBT (meter-based) beat.
1570 * @return The frame duration according to the tempo map at the supplied BBT (meter-based) beat.
1572 * This function uses both tempo and meter.
1575 TempoMap::frame_at_beat (const double& beat) const
1577 Glib::Threads::RWLock::ReaderLock lm (lock);
1579 return frame_at_minute (minute_at_beat_locked (_metrics, beat));
1582 /* meter & tempo section based */
1584 TempoMap::minute_at_beat_locked (const Metrics& metrics, const double& beat) const
1586 MeterSection* prev_m = 0;
1587 TempoSection* prev_t = 0;
1591 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1592 if (!(*i)->is_tempo()) {
1593 m = static_cast<MeterSection*> (*i);
1594 if (prev_m && m->beat() > beat) {
1603 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1604 if ((*i)->is_tempo()) {
1605 t = static_cast<TempoSection*> (*i);
1606 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1614 return prev_t->minute_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse());
1617 /** Returns a Tempo corresponding to the supplied frame position.
1618 * @param frame The audio frame.
1619 * @return a Tempo according to the tempo map at the supplied frame.
1623 TempoMap::tempo_at_frame (const framepos_t& frame) const
1625 Glib::Threads::RWLock::ReaderLock lm (lock);
1627 return tempo_at_minute_locked (_metrics, minute_at_frame (frame));
1631 TempoMap::tempo_at_minute_locked (const Metrics& metrics, const double& minute) const
1633 TempoSection* prev_t = 0;
1637 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1638 if ((*i)->is_tempo()) {
1639 t = static_cast<TempoSection*> (*i);
1643 if ((prev_t) && t->minute() > minute) {
1644 /* t is the section past frame */
1645 return prev_t->tempo_at_minute (minute);
1651 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type());
1654 /** returns the frame at which the supplied tempo occurs, or
1655 * the frame of the last tempo section (search exhausted)
1656 * only the position of the first occurence will be returned
1660 TempoMap::frame_at_tempo (const Tempo& tempo) const
1662 Glib::Threads::RWLock::ReaderLock lm (lock);
1664 return frame_at_minute (minute_at_tempo_locked (_metrics, tempo));
1668 TempoMap::minute_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1670 TempoSection* prev_t = 0;
1671 const double tempo_bpm = tempo.note_types_per_minute();
1673 Metrics::const_iterator i;
1675 for (i = metrics.begin(); i != metrics.end(); ++i) {
1677 if ((*i)->is_tempo()) {
1678 t = static_cast<TempoSection*> (*i);
1684 const double t_bpm = t->note_types_per_minute();
1686 if (t_bpm == tempo_bpm) {
1691 const double prev_t_bpm = prev_t->note_types_per_minute();
1693 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1694 return prev_t->minute_at_ntpm (prev_t->note_types_per_minute(), prev_t->pulse());
1701 return prev_t->minute();
1705 TempoMap::tempo_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1707 TempoSection* prev_t = 0;
1711 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1712 if ((*i)->is_tempo()) {
1713 t = static_cast<TempoSection*> (*i);
1717 if ((prev_t) && t->pulse() > pulse) {
1718 /* t is the section past frame */
1719 return prev_t->tempo_at_pulse (pulse);
1725 return Tempo (prev_t->note_types_per_minute(), prev_t->note_type());
1729 TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1731 TempoSection* prev_t = 0;
1732 const double tempo_bpm = tempo.note_types_per_minute();
1734 Metrics::const_iterator i;
1736 for (i = metrics.begin(); i != metrics.end(); ++i) {
1738 if ((*i)->is_tempo()) {
1739 t = static_cast<TempoSection*> (*i);
1745 const double t_bpm = t->note_types_per_minute();
1747 if (t_bpm == tempo_bpm) {
1752 const double prev_t_bpm = prev_t->note_types_per_minute();
1754 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1755 return prev_t->pulse_at_ntpm (prev_t->note_types_per_minute(), prev_t->minute());
1762 return prev_t->pulse();
1765 /** Returns a Tempo corresponding to the supplied position in quarter-note beats.
1766 * @param qn the position in quarter note beats.
1767 * @return the Tempo at the supplied quarter-note.
1770 TempoMap::tempo_at_quarter_note (const double& qn) const
1772 Glib::Threads::RWLock::ReaderLock lm (lock);
1774 return tempo_at_pulse_locked (_metrics, qn / 4.0);
1777 /** Returns the position in quarter-note beats corresponding to the supplied Tempo.
1778 * @param tempo the tempo.
1779 * @return the position in quarter-note beats where the map bpm
1780 * is equal to that of the Tempo. currently ignores note_type.
1783 TempoMap::quarter_note_at_tempo (const Tempo& tempo) const
1785 Glib::Threads::RWLock::ReaderLock lm (lock);
1787 return pulse_at_tempo_locked (_metrics, tempo) * 4.0;;
1790 /** Returns the whole-note pulse corresponding to the supplied BBT (meter-based) beat.
1791 * @param metrics the list of metric sections used to calculate the pulse.
1792 * @param beat The BBT (meter-based) beat.
1793 * @return the whole-note pulse at the supplied BBT (meter-based) beat.
1795 * a pulse or whole note is the base musical position of a MetricSection.
1796 * it is equivalent to four quarter notes.
1800 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1802 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1804 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1807 /** Returns the BBT (meter-based) beat corresponding to the supplied whole-note pulse .
1808 * @param metrics the list of metric sections used to calculate the beat.
1809 * @param pulse the whole-note pulse.
1810 * @return the meter-based beat at the supplied whole-note pulse.
1812 * a pulse or whole note is the base musical position of a MetricSection.
1813 * it is equivalent to four quarter notes.
1816 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1818 MeterSection* prev_m = 0;
1820 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1822 if (!(*i)->is_tempo()) {
1823 m = static_cast<MeterSection*> (*i);
1824 if (prev_m && m->pulse() > pulse) {
1831 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1835 /* tempo section based */
1837 TempoMap::pulse_at_minute_locked (const Metrics& metrics, const double& minute) const
1839 /* HOLD (at least) THE READER LOCK */
1840 TempoSection* prev_t = 0;
1842 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1844 if ((*i)->is_tempo()) {
1845 t = static_cast<TempoSection*> (*i);
1849 if (prev_t && t->minute() > minute) {
1850 /*the previous ts is the one containing the frame */
1851 const double ret = prev_t->pulse_at_minute (minute);
1852 /* audio locked section in new meter*/
1853 if (t->pulse() < ret) {
1862 /* treated as constant for this ts */
1863 const double pulses_in_section = ((minute - prev_t->minute()) * prev_t->note_types_per_minute()) / prev_t->note_type();
1865 return pulses_in_section + prev_t->pulse();
1868 /* tempo section based */
1870 TempoMap::minute_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1872 /* HOLD THE READER LOCK */
1874 const TempoSection* prev_t = 0;
1876 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1879 if ((*i)->is_tempo()) {
1880 t = static_cast<TempoSection*> (*i);
1884 if (prev_t && t->pulse() > pulse) {
1885 return prev_t->minute_at_pulse (pulse);
1891 /* must be treated as constant, irrespective of _type */
1892 double const dtime = ((pulse - prev_t->pulse()) * prev_t->note_type()) / prev_t->note_types_per_minute();
1894 return dtime + prev_t->minute();
1897 /** Returns the BBT (meter-based) beat corresponding to the supplied BBT time.
1898 * @param bbt The BBT time (meter-based).
1899 * @return bbt The BBT beat (meter-based) at the supplied BBT time.
1903 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
1905 Glib::Threads::RWLock::ReaderLock lm (lock);
1906 return beat_at_bbt_locked (_metrics, bbt);
1911 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1913 /* CALLER HOLDS READ LOCK */
1915 MeterSection* prev_m = 0;
1917 /* because audio-locked meters have 'fake' integral beats,
1918 there is no pulse offset here.
1922 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1923 if (!(*i)->is_tempo()) {
1924 m = static_cast<MeterSection*> (*i);
1926 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1927 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1935 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1936 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1937 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1942 /** Returns the BBT time corresponding to the supplied BBT (meter-based) beat.
1943 * @param beat The BBT (meter-based) beat.
1944 * @return The BBT time (meter-based) at the supplied meter-based beat.
1948 TempoMap::bbt_at_beat (const double& beat)
1950 Glib::Threads::RWLock::ReaderLock lm (lock);
1951 return bbt_at_beat_locked (_metrics, beat);
1955 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
1957 /* CALLER HOLDS READ LOCK */
1958 MeterSection* prev_m = 0;
1959 const double beats = max (0.0, b);
1961 MeterSection* m = 0;
1963 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1964 if (!(*i)->is_tempo()) {
1965 m = static_cast<MeterSection*> (*i);
1967 if (m->beat() > beats) {
1968 /* this is the meter after the one our beat is on*/
1977 const double beats_in_ms = beats - prev_m->beat();
1978 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1979 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1980 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1981 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1985 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1986 ret.beats = (uint32_t) floor (remaining_beats);
1987 ret.bars = total_bars;
1989 /* 0 0 0 to 1 1 0 - based mapping*/
1993 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1995 ret.ticks -= BBT_Time::ticks_per_beat;
1998 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2006 /** Returns the quarter-note beat corresponding to the supplied BBT time (meter-based).
2007 * @param bbt The BBT time (meter-based).
2008 * @return the quarter note beat at the supplied BBT time
2010 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2012 * while the input uses meter, the output does not.
2015 TempoMap::quarter_note_at_bbt (const Timecode::BBT_Time& bbt)
2017 Glib::Threads::RWLock::ReaderLock lm (lock);
2019 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2023 TempoMap::quarter_note_at_bbt_rt (const Timecode::BBT_Time& bbt)
2025 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2028 throw std::logic_error ("TempoMap::quarter_note_at_bbt_rt() could not lock tempo map");
2031 return pulse_at_bbt_locked (_metrics, bbt) * 4.0;
2035 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2037 /* CALLER HOLDS READ LOCK */
2039 MeterSection* prev_m = 0;
2041 /* because audio-locked meters have 'fake' integral beats,
2042 there is no pulse offset here.
2046 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2047 if (!(*i)->is_tempo()) {
2048 m = static_cast<MeterSection*> (*i);
2050 if (m->bbt().bars > bbt.bars) {
2058 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2059 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
2060 const double ret = remaining_pulses + prev_m->pulse() + (((bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat)) / prev_m->note_divisor());
2065 /** Returns the BBT time corresponding to the supplied quarter-note beat.
2066 * @param qn the quarter-note beat.
2067 * @return The BBT time (meter-based) at the supplied meter-based beat.
2069 * quarter-notes ignore meter and are based on pulse (the musical unit of MetricSection).
2073 TempoMap::bbt_at_quarter_note (const double& qn)
2075 Glib::Threads::RWLock::ReaderLock lm (lock);
2077 return bbt_at_pulse_locked (_metrics, qn / 4.0);
2080 /** Returns the BBT time (meter-based) corresponding to the supplied whole-note pulse position.
2081 * @param metrics The list of metric sections used to determine the result.
2082 * @param pulse The whole-note pulse.
2083 * @return The BBT time at the supplied whole-note pulse.
2085 * a pulse or whole note is the basic musical position of a MetricSection.
2086 * it is equivalent to four quarter notes.
2087 * while the output uses meter, the input does not.
2090 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2092 MeterSection* prev_m = 0;
2094 MeterSection* m = 0;
2096 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2098 if (!(*i)->is_tempo()) {
2099 m = static_cast<MeterSection*> (*i);
2102 double const pulses_to_m = m->pulse() - prev_m->pulse();
2103 if (prev_m->pulse() + pulses_to_m > pulse) {
2104 /* this is the meter after the one our beat is on*/
2113 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
2114 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2115 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2116 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2117 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2121 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2122 ret.beats = (uint32_t) floor (remaining_beats);
2123 ret.bars = total_bars;
2125 /* 0 0 0 to 1 1 0 mapping*/
2129 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2131 ret.ticks -= BBT_Time::ticks_per_beat;
2134 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2142 /** Returns the BBT time corresponding to the supplied frame position.
2143 * @param frame the position in audio samples.
2144 * @return the BBT time at the frame position .
2148 TempoMap::bbt_at_frame (framepos_t frame)
2155 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
2158 Glib::Threads::RWLock::ReaderLock lm (lock);
2160 return bbt_at_minute_locked (_metrics, minute_at_frame (frame));
2164 TempoMap::bbt_at_frame_rt (framepos_t frame)
2166 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2169 throw std::logic_error ("TempoMap::bbt_at_frame_rt() could not lock tempo map");
2172 return bbt_at_minute_locked (_metrics, minute_at_frame (frame));
2176 TempoMap::bbt_at_minute_locked (const Metrics& metrics, const double& minute) const
2186 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
2187 MeterSection* prev_m = 0;
2188 MeterSection* next_m = 0;
2192 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2193 if (!(*i)->is_tempo()) {
2194 m = static_cast<MeterSection*> (*i);
2195 if (prev_m && m->minute() > minute) {
2203 double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
2205 /* handle frame before first meter */
2206 if (minute < prev_m->minute()) {
2209 /* audio locked meters fake their beat */
2210 if (next_m && next_m->beat() < beat) {
2211 beat = next_m->beat();
2214 beat = max (0.0, beat);
2216 const double beats_in_ms = beat - prev_m->beat();
2217 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2218 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2219 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2220 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2224 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2225 ret.beats = (uint32_t) floor (remaining_beats);
2226 ret.bars = total_bars;
2228 /* 0 0 0 to 1 1 0 - based mapping*/
2232 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2234 ret.ticks -= BBT_Time::ticks_per_beat;
2237 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2245 /** Returns the frame position corresponding to the supplied BBT time.
2246 * @param bbt the position in BBT time.
2247 * @return the frame position at bbt.
2251 TempoMap::frame_at_bbt (const BBT_Time& bbt)
2254 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
2258 if (bbt.beats < 1) {
2259 throw std::logic_error ("beats are counted from one");
2261 Glib::Threads::RWLock::ReaderLock lm (lock);
2263 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
2266 /* meter & tempo section based */
2268 TempoMap::minute_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2270 /* HOLD THE READER LOCK */
2272 const double ret = minute_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2277 * Returns the quarter-note beat position corresponding to the supplied frame.
2279 * @param frame the position in frames.
2280 * @return The quarter-note position of the supplied frame. Ignores meter.
2284 TempoMap::quarter_note_at_frame (const framepos_t frame) const
2286 Glib::Threads::RWLock::ReaderLock lm (lock);
2288 const double ret = quarter_note_at_minute_locked (_metrics, minute_at_frame (frame));
2294 TempoMap::quarter_note_at_minute_locked (const Metrics& metrics, const double minute) const
2296 const double ret = pulse_at_minute_locked (metrics, minute) * 4.0;
2302 TempoMap::quarter_note_at_frame_rt (const framepos_t frame) const
2304 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2307 throw std::logic_error ("TempoMap::quarter_note_at_frame_rt() could not lock tempo map");
2310 const double ret = pulse_at_minute_locked (_metrics, minute_at_frame (frame)) * 4.0;
2316 * Returns the frame position corresponding to the supplied quarter-note beat.
2318 * @param quarter_note the quarter-note position.
2319 * @return the frame position of the supplied quarter-note. Ignores meter.
2324 TempoMap::frame_at_quarter_note (const double quarter_note) const
2326 Glib::Threads::RWLock::ReaderLock lm (lock);
2328 const framepos_t ret = frame_at_minute (minute_at_quarter_note_locked (_metrics, quarter_note));
2334 TempoMap::minute_at_quarter_note_locked (const Metrics& metrics, const double quarter_note) const
2336 const double ret = minute_at_pulse_locked (metrics, quarter_note / 4.0);
2341 /** Returns the quarter-note beats corresponding to the supplied BBT (meter-based) beat.
2342 * @param beat The BBT (meter-based) beat.
2343 * @return The quarter-note position of the supplied BBT (meter-based) beat.
2345 * a quarter-note may be compared with and assigned to Evoral::Beats.
2349 TempoMap::quarter_note_at_beat (const double beat)
2351 Glib::Threads::RWLock::ReaderLock lm (lock);
2353 const double ret = quarter_note_at_beat_locked (_metrics, beat);
2359 TempoMap::quarter_note_at_beat_locked (const Metrics& metrics, const double beat) const
2361 const double ret = pulse_at_beat_locked (metrics, beat) * 4.0;
2366 /** Returns the BBT (meter-based) beat position corresponding to the supplied quarter-note beats.
2367 * @param quarter_note The position in quarter-note beats.
2368 * @return the BBT (meter-based) beat position of the supplied quarter-note beats.
2370 * a quarter-note is the musical unit of Evoral::Beats.
2374 TempoMap::beat_at_quarter_note (const double quarter_note)
2376 Glib::Threads::RWLock::ReaderLock lm (lock);
2378 const double ret = beat_at_quarter_note_locked (_metrics, quarter_note);
2384 TempoMap::beat_at_quarter_note_locked (const Metrics& metrics, const double quarter_note) const
2387 return beat_at_pulse_locked (metrics, quarter_note / 4.0);
2390 /** Returns the duration in frames between two supplied quarter-note beat positions.
2391 * @param start the first position in quarter-note beats.
2392 * @param end the end position in quarter-note beats.
2393 * @return the frame distance ober the quarter-note beats duration.
2395 * use this rather than e.g.
2396 * frame_at-quarter_note (end_beats) - frame_at_quarter_note (start_beats).
2397 * frames_between_quarter_notes() doesn't round to audio frames as an intermediate step,
2401 TempoMap::frames_between_quarter_notes (const double start, const double end) const
2403 Glib::Threads::RWLock::ReaderLock lm (lock);
2405 return frame_at_minute (minutes_between_quarter_notes_locked (_metrics, start, end));
2409 TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end) const
2412 return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
2416 TempoMap::quarter_notes_between_frames (const framecnt_t start, const framecnt_t end) const
2418 Glib::Threads::RWLock::ReaderLock lm (lock);
2420 return quarter_notes_between_frames_locked (_metrics, start, end);
2424 TempoMap::quarter_notes_between_frames_locked (const Metrics& metrics, const framecnt_t start, const framecnt_t end) const
2426 const TempoSection* prev_t = 0;
2428 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2431 if ((*i)->is_tempo()) {
2432 t = static_cast<TempoSection*> (*i);
2436 if (prev_t && t->frame() > start) {
2442 const double start_qn = prev_t->pulse_at_frame (start);
2444 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2447 if ((*i)->is_tempo()) {
2448 t = static_cast<TempoSection*> (*i);
2452 if (prev_t && t->frame() > end) {
2458 const double end_qn = prev_t->pulse_at_frame (end);
2460 return (end_qn - start_qn) * 4.0;
2464 TempoMap::check_solved (const Metrics& metrics) const
2466 TempoSection* prev_t = 0;
2467 MeterSection* prev_m = 0;
2469 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2472 if ((*i)->is_tempo()) {
2473 t = static_cast<TempoSection*> (*i);
2478 /* check ordering */
2479 if ((t->minute() <= prev_t->minute()) || (t->pulse() <= prev_t->pulse())) {
2483 /* precision check ensures tempo and frames align.*/
2484 if (t->frame() != frame_at_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()))) {
2485 if (!t->locked_to_meter()) {
2490 /* gradient limit - who knows what it should be?
2491 things are also ok (if a little chaotic) without this
2493 if (fabs (prev_t->c_func()) > 1000.0) {
2494 //std::cout << "c : " << prev_t->c_func() << std::endl;
2501 if (!(*i)->is_tempo()) {
2502 m = static_cast<MeterSection*> (*i);
2503 if (prev_m && m->position_lock_style() == AudioTime) {
2504 const TempoSection* t = &tempo_section_at_minute_locked (metrics, minute_at_frame (m->frame() - 1));
2505 const double nascent_m_minute = t->minute_at_pulse (m->pulse());
2506 /* Here we check that a preceding section of music doesn't overlap a subsequent one.
2508 if (t && (nascent_m_minute > m->minute() || nascent_m_minute < 0.0)) {
2522 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
2524 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2526 if ((*i)->is_tempo()) {
2527 t = static_cast<TempoSection*> (*i);
2528 if (!t->movable()) {
2529 t->set_active (true);
2532 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
2533 t->set_active (false);
2535 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
2536 t->set_active (true);
2537 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
2546 TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const double& minute)
2548 TempoSection* prev_t = 0;
2549 TempoSection* section_prev = 0;
2550 double first_m_minute = 0.0;
2552 /* can't move a tempo before the first meter */
2553 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2555 if (!(*i)->is_tempo()) {
2556 m = static_cast<MeterSection*> (*i);
2557 if (!m->movable()) {
2558 first_m_minute = m->minute();
2563 if (section->movable() && minute <= first_m_minute) {
2567 section->set_active (true);
2568 section->set_minute (minute);
2570 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2572 if ((*i)->is_tempo()) {
2573 t = static_cast<TempoSection*> (*i);
2580 section_prev = prev_t;
2581 if (t->locked_to_meter()) {
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_minute (section->note_types_per_minute(), minute));
2602 if (!section->locked_to_meter()) {
2603 section->set_pulse (section_prev->pulse_at_ntpm (section->note_types_per_minute(), minute));
2608 recompute_tempi (imaginary);
2610 if (check_solved (imaginary)) {
2613 dunp (imaginary, std::cout);
2617 MetricSectionFrameSorter fcmp;
2618 imaginary.sort (fcmp);
2620 recompute_tempi (imaginary);
2622 if (check_solved (imaginary)) {
2630 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2632 TempoSection* prev_t = 0;
2633 TempoSection* section_prev = 0;
2635 section->set_pulse (pulse);
2637 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2639 if ((*i)->is_tempo()) {
2640 t = static_cast<TempoSection*> (*i);
2644 if (!t->movable()) {
2651 section_prev = prev_t;
2654 if (t->position_lock_style() == MusicTime) {
2655 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->note_types_per_minute(), t->pulse()));
2656 t->set_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()));
2658 prev_t->set_c_func (prev_t->compute_c_func_minute (t->note_types_per_minute(), t->minute()));
2659 if (!t->locked_to_meter()) {
2660 t->set_pulse (prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute()));
2669 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->note_types_per_minute(), pulse));
2670 section->set_minute (section_prev->minute_at_ntpm (section->note_types_per_minute(), pulse));
2674 recompute_tempi (imaginary);
2676 if (check_solved (imaginary)) {
2679 dunp (imaginary, std::cout);
2683 MetricSectionSorter cmp;
2684 imaginary.sort (cmp);
2686 recompute_tempi (imaginary);
2688 * XX need a restriction here, but only for this case,
2689 * as audio locked tempos don't interact in the same way.
2691 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2693 * |50 bpm |250 bpm |60 bpm
2694 * drag 250 to the pulse after 60->
2695 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2697 if (check_solved (imaginary)) {
2705 TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const double& minute)
2707 /* disallow moving first meter past any subsequent one, and any movable meter before the first one */
2708 const MeterSection* other = &meter_section_at_minute_locked (imaginary, minute);
2709 if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->minute() >= minute)) {
2713 if (!section->movable()) {
2714 /* lock the first tempo to our first meter */
2715 if (!set_active_tempos (imaginary, section->frame_at_minute (minute))) {
2720 TempoSection* meter_locked_tempo = 0;
2722 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2724 if ((*ii)->is_tempo()) {
2725 t = static_cast<TempoSection*> (*ii);
2726 if ((t->locked_to_meter() || !t->movable()) && t->minute() == section->minute()) {
2727 meter_locked_tempo = t;
2733 if (!meter_locked_tempo) {
2737 MeterSection* prev_m = 0;
2739 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2740 bool solved = false;
2742 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2744 if (!(*i)->is_tempo()) {
2745 m = static_cast<MeterSection*> (*i);
2747 if (prev_m && section->movable()) {
2748 const double beats = (pulse_at_minute_locked (imaginary, minute) - prev_m->pulse()) * prev_m->note_divisor();
2749 if (beats + prev_m->beat() < section->beat()) {
2750 /* set the section pulse according to its musical position,
2751 * as an earlier time than this has been requested.
2753 const double new_pulse = ((section->beat() - prev_m->beat())
2754 / prev_m->note_divisor()) + prev_m->pulse();
2756 tempo_copy->set_position_lock_style (MusicTime);
2757 if ((solved = solve_map_pulse (future_map, tempo_copy, new_pulse))) {
2758 meter_locked_tempo->set_position_lock_style (MusicTime);
2759 section->set_position_lock_style (MusicTime);
2760 section->set_pulse (new_pulse);
2761 solve_map_pulse (imaginary, meter_locked_tempo, new_pulse);
2762 meter_locked_tempo->set_position_lock_style (AudioTime);
2763 section->set_position_lock_style (AudioTime);
2764 section->set_minute (meter_locked_tempo->minute());
2770 Metrics::const_iterator d = future_map.begin();
2771 while (d != future_map.end()) {
2780 /* all is ok. set section's locked tempo if allowed.
2781 possibly disallowed if there is an adjacent audio-locked tempo.
2782 XX this check could possibly go. its never actually happened here.
2784 MeterSection* meter_copy = const_cast<MeterSection*>
2785 (&meter_section_at_minute_locked (future_map, section->minute()));
2787 meter_copy->set_minute (minute);
2789 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2790 section->set_minute (minute);
2791 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2792 / prev_m->note_divisor()) + prev_m->pulse());
2793 solve_map_minute (imaginary, meter_locked_tempo, minute);
2798 Metrics::const_iterator d = future_map.begin();
2799 while (d != future_map.end()) {
2809 /* not movable (first meter atm) */
2811 tempo_copy->set_minute (minute);
2812 tempo_copy->set_pulse (0.0);
2814 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2815 section->set_minute (minute);
2816 meter_locked_tempo->set_minute (minute);
2817 meter_locked_tempo->set_pulse (0.0);
2818 solve_map_minute (imaginary, meter_locked_tempo, minute);
2823 Metrics::const_iterator d = future_map.begin();
2824 while (d != future_map.end()) {
2833 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2834 section->set_beat (b_bbt);
2835 section->set_pulse (0.0);
2845 MetricSectionFrameSorter fcmp;
2846 imaginary.sort (fcmp);
2848 recompute_meters (imaginary);
2854 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2856 /* disallow setting section to an existing meter's bbt */
2857 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2859 if (!(*i)->is_tempo()) {
2860 m = static_cast<MeterSection*> (*i);
2861 if (m != section && m->bbt().bars == when.bars) {
2867 MeterSection* prev_m = 0;
2868 MeterSection* section_prev = 0;
2870 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2872 if (!(*i)->is_tempo()) {
2873 m = static_cast<MeterSection*> (*i);
2874 pair<double, BBT_Time> b_bbt;
2875 double new_pulse = 0.0;
2877 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2878 section_prev = prev_m;
2879 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2880 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2881 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2883 section->set_beat (b_bbt);
2884 section->set_pulse (pulse);
2885 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
2890 if (m->position_lock_style() == AudioTime) {
2891 TempoSection* meter_locked_tempo = 0;
2893 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2895 if ((*ii)->is_tempo()) {
2896 t = static_cast<TempoSection*> (*ii);
2897 if ((t->locked_to_meter() || !t->movable()) && t->frame() == m->frame()) {
2898 meter_locked_tempo = t;
2904 if (!meter_locked_tempo) {
2909 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2911 if (beats + prev_m->beat() != m->beat()) {
2912 /* tempo/ meter change caused a change in beat (bar). */
2913 b_bbt = make_pair (beats + prev_m->beat()
2914 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2915 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2916 } else if (m->movable()) {
2917 b_bbt = make_pair (m->beat(), m->bbt());
2918 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2921 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2924 meter_locked_tempo->set_pulse (new_pulse);
2925 m->set_beat (b_bbt);
2926 m->set_pulse (new_pulse);
2930 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2931 if (beats + prev_m->beat() != m->beat()) {
2932 /* tempo/ meter change caused a change in beat (bar). */
2933 b_bbt = make_pair (beats + prev_m->beat()
2934 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2936 b_bbt = make_pair (beats + prev_m->beat()
2939 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2940 m->set_beat (b_bbt);
2941 m->set_pulse (new_pulse);
2942 m->set_minute (minute_at_pulse_locked (imaginary, new_pulse));
2949 if (!section_prev) {
2951 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
2952 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2953 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
2955 section->set_beat (b_bbt);
2956 section->set_pulse (pulse);
2957 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
2960 MetricSectionSorter cmp;
2961 imaginary.sort (cmp);
2963 recompute_meters (imaginary);
2968 /** places a copy of _metrics into copy and returns a pointer
2969 * to section's equivalent in copy.
2972 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
2974 TempoSection* ret = 0;
2976 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2979 if ((*i)->is_tempo()) {
2980 t = static_cast<TempoSection*> (*i);
2982 ret = new TempoSection (*t);
2983 copy.push_back (ret);
2987 TempoSection* cp = new TempoSection (*t);
2988 copy.push_back (cp);
2990 if (!(*i)->is_tempo()) {
2991 m = static_cast<MeterSection *> (*i);
2992 MeterSection* cp = new MeterSection (*m);
2993 copy.push_back (cp);
3001 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
3003 MeterSection* ret = 0;
3005 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3008 if ((*i)->is_tempo()) {
3009 t = static_cast<TempoSection*> (*i);
3010 TempoSection* cp = new TempoSection (*t);
3011 copy.push_back (cp);
3014 if (!(*i)->is_tempo()) {
3015 m = static_cast<MeterSection *> (*i);
3017 ret = new MeterSection (*m);
3018 copy.push_back (ret);
3021 MeterSection* cp = new MeterSection (*m);
3022 copy.push_back (cp);
3029 /** answers the question "is this a valid beat position for this tempo section?".
3030 * it returns true if the tempo section can be moved to the requested bbt position,
3031 * leaving the tempo map in a solved state.
3032 * @param ts the tempo section to be moved
3033 * @param bbt the requested new position for the tempo section
3034 * @return true if the tempo section can be moved to the position, otherwise false.
3037 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
3040 TempoSection* tempo_copy = 0;
3043 Glib::Threads::RWLock::ReaderLock lm (lock);
3044 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
3050 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
3052 Metrics::const_iterator d = copy.begin();
3053 while (d != copy.end()) {
3062 * 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,
3063 * taking any possible reordering as a consequence of this into account.
3064 * @param section - the section to be altered
3065 * @param bbt - the BBT time where the altered tempo will fall
3066 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
3068 pair<double, framepos_t>
3069 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
3072 pair<double, framepos_t> ret = make_pair (0.0, 0);
3074 Glib::Threads::RWLock::ReaderLock lm (lock);
3076 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
3078 const double beat = beat_at_bbt_locked (future_map, bbt);
3080 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
3081 ret.first = tempo_copy->pulse();
3082 ret.second = tempo_copy->frame();
3084 ret.first = section->pulse();
3085 ret.second = section->frame();
3088 Metrics::const_iterator d = future_map.begin();
3089 while (d != future_map.end()) {
3096 /** moves a TempoSection to a specified position.
3097 * @param ts - the section to be moved
3098 * @param frame - the new position in frames for the tempo
3099 * @param sub_num - the snap division to use if using musical time.
3101 * if sub_num is non-zero, the frame position is used to calculate an exact
3104 * -1 | snap to bars (meter-based)
3105 * 0 | no snap - use audio frame for musical position
3106 * 1 | snap to meter-based (BBT) beat
3107 * >1 | snap to quarter-note subdivision (i.e. 4 will snap to sixteenth notes)
3109 * this follows the snap convention in the gui.
3110 * if sub_num is zero, the musical position will be taken from the supplied frame.
3113 TempoMap::gui_move_tempo (TempoSection* ts, const framepos_t& frame, const int& sub_num)
3117 if (ts->position_lock_style() == MusicTime) {
3119 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
3120 Glib::Threads::RWLock::WriterLock lm (lock);
3121 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3123 tempo_copy->set_position_lock_style (AudioTime);
3125 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3126 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
3127 const double pulse = pulse_at_beat_locked (future_map, beat);
3129 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3130 solve_map_pulse (_metrics, ts, pulse);
3131 recompute_meters (_metrics);
3139 Glib::Threads::RWLock::WriterLock lm (lock);
3140 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3142 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3144 /* We're moving the object that defines the grid while snapping to it...
3145 * Placing the ts at the beat corresponding to the requested frame may shift the
3146 * grid in such a way that the mouse is left hovering over a completerly different division,
3147 * causing jittering when the mouse next moves (esp. large tempo deltas).
3148 * To avoid this, place the ts at the requested frame in a dummy map
3149 * then find the closest beat subdivision to that frame in the dummy.
3150 * This alters the snap behaviour slightly in that we snap to beat divisions
3151 * in the future map rather than the existing one.
3153 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
3154 const double pulse = pulse_at_beat_locked (future_map, beat);
3156 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3157 /* snapping to a grid. force MusicTime temporarily. */
3158 ts->set_position_lock_style (MusicTime);
3159 solve_map_pulse (_metrics, ts, pulse);
3160 ts->set_position_lock_style (AudioTime);
3162 recompute_meters (_metrics);
3165 solve_map_minute (_metrics, ts, minute_at_frame (frame));
3166 recompute_meters (_metrics);
3172 Metrics::const_iterator d = future_map.begin();
3173 while (d != future_map.end()) {
3178 MetricPositionChanged (); // Emit Signal
3181 /** moves a MeterSection to a specified position.
3182 * @param ms - the section to be moved
3183 * @param frame - the new position in frames for the meter
3185 * as a meter cannot snap to anything but bars,
3186 * the supplied frame is rounded to the nearest bar, possibly
3187 * leaving the meter position unchanged.
3190 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
3194 if (ms->position_lock_style() == AudioTime) {
3197 Glib::Threads::RWLock::WriterLock lm (lock);
3198 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3200 if (solve_map_minute (future_map, copy, minute_at_frame (frame))) {
3201 solve_map_minute (_metrics, ms, minute_at_frame (frame));
3202 recompute_tempi (_metrics);
3207 Glib::Threads::RWLock::WriterLock lm (lock);
3208 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3210 const double beat = beat_at_minute_locked (_metrics, minute_at_frame (frame));
3211 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
3213 if (solve_map_bbt (future_map, copy, bbt)) {
3214 solve_map_bbt (_metrics, ms, bbt);
3215 recompute_tempi (_metrics);
3220 Metrics::const_iterator d = future_map.begin();
3221 while (d != future_map.end()) {
3226 MetricPositionChanged (); // Emit Signal
3230 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
3233 bool can_solve = false;
3235 Glib::Threads::RWLock::WriterLock lm (lock);
3236 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3237 tempo_copy->set_note_types_per_minute (bpm.note_types_per_minute());
3238 recompute_tempi (future_map);
3240 if (check_solved (future_map)) {
3241 ts->set_note_types_per_minute (bpm.note_types_per_minute());
3242 recompute_map (_metrics);
3247 Metrics::const_iterator d = future_map.begin();
3248 while (d != future_map.end()) {
3253 MetricPositionChanged (); // Emit Signal
3259 TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame)
3262 Ts (future prev_t) Tnext
3265 |----------|----------
3272 Glib::Threads::RWLock::WriterLock lm (lock);
3278 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3279 TempoSection* prev_to_prev_t = 0;
3280 const frameoffset_t fr_off = end_frame - frame;
3282 if (prev_t && prev_t->pulse() > 0.0) {
3283 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_minute_locked (future_map, minute_at_frame (prev_t->frame() - 1)));
3286 TempoSection* next_t = 0;
3287 for (Metrics::iterator i = future_map.begin(); i != future_map.end(); ++i) {
3288 TempoSection* t = 0;
3289 if ((*i)->is_tempo()) {
3290 t = static_cast<TempoSection*> (*i);
3291 if (t->frame() > ts->frame()) {
3297 /* minimum allowed measurement distance in frames */
3298 const framepos_t min_dframe = 2;
3300 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
3301 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
3303 double contribution = 0.0;
3305 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3306 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3309 const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
3311 const double start_pulse = prev_t->pulse_at_minute (minute_at_frame (frame));
3312 const double end_pulse = prev_t->pulse_at_minute (minute_at_frame (end_frame));
3316 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
3318 if (prev_t->position_lock_style() == MusicTime) {
3319 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3320 if (frame > prev_to_prev_t->frame() + min_dframe && (frame + prev_t_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
3322 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame())
3323 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
3325 new_bpm = prev_t->note_types_per_minute();
3328 /* prev to prev is irrelevant */
3330 if (start_pulse > prev_t->pulse() && end_pulse > prev_t->pulse()) {
3331 new_bpm = prev_t->note_types_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
3333 new_bpm = prev_t->note_types_per_minute();
3338 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3339 if (frame > prev_to_prev_t->frame() + min_dframe && end_frame > prev_to_prev_t->frame() + min_dframe) {
3341 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_to_prev_t->frame())
3342 / (double) ((end_frame) - prev_to_prev_t->frame()));
3344 new_bpm = prev_t->note_types_per_minute();
3347 /* prev_to_prev_t is irrelevant */
3349 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3350 new_bpm = prev_t->note_types_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
3352 new_bpm = prev_t->note_types_per_minute();
3358 double frame_ratio = 1.0;
3359 double pulse_ratio = 1.0;
3360 const double pulse_pos = frame;
3362 if (prev_to_prev_t) {
3363 if (pulse_pos > prev_to_prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_to_prev_t->frame() + min_dframe) {
3364 frame_ratio = (((pulse_pos - fr_off) - prev_to_prev_t->frame()) / (double) ((pulse_pos) - prev_to_prev_t->frame()));
3366 if (end_pulse > prev_to_prev_t->pulse() && start_pulse > prev_to_prev_t->pulse()) {
3367 pulse_ratio = ((start_pulse - prev_to_prev_t->pulse()) / (end_pulse - prev_to_prev_t->pulse()));
3370 if (pulse_pos > prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_t->frame() + min_dframe) {
3371 frame_ratio = (((pulse_pos - fr_off) - prev_t->frame()) / (double) ((pulse_pos) - prev_t->frame()));
3373 pulse_ratio = (start_pulse / end_pulse);
3375 new_bpm = prev_t->note_types_per_minute() * (pulse_ratio * frame_ratio);
3378 /* don't clamp and proceed here.
3379 testing has revealed that this can go negative,
3380 which is an entirely different thing to just being too low.
3382 if (new_bpm < 0.5) {
3385 new_bpm = min (new_bpm, (double) 1000.0);
3386 prev_t->set_note_types_per_minute (new_bpm);
3387 recompute_tempi (future_map);
3388 recompute_meters (future_map);
3390 if (check_solved (future_map)) {
3391 ts->set_note_types_per_minute (new_bpm);
3392 recompute_tempi (_metrics);
3393 recompute_meters (_metrics);
3397 Metrics::const_iterator d = future_map.begin();
3398 while (d != future_map.end()) {
3403 MetricPositionChanged (); // Emit Signal
3406 /** Returns the exact bbt-based beat corresponding to the bar, beat or quarter note subdivision nearest to
3407 * the supplied frame, possibly returning a negative value.
3409 * @param frame The session frame position.
3410 * @param sub_num The subdivision to use when rounding the beat.
3411 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3412 * Positive integers indicate quarter note (non BBT) divisions.
3413 * 0 indicates that the returned beat should not be rounded (equivalent to quarter_note_at_frame()).
3414 * @return The beat position of the supplied frame.
3416 * when working to a musical grid, the use of sub_nom indicates that
3417 * the position should be interpreted musically.
3419 * it effectively snaps to meter bars, meter beats or quarter note divisions
3420 * (as per current gui convention) and returns a musical position independent of frame rate.
3422 * If the supplied frame lies before the first meter, the return will be negative,
3423 * in which case the returned beat uses the first meter (for BBT subdivisions) and
3424 * the continuation of the tempo curve (backwards).
3426 * This function is sensitive to tempo and meter.
3429 TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num)
3431 Glib::Threads::RWLock::ReaderLock lm (lock);
3433 return exact_beat_at_frame_locked (_metrics, frame, sub_num);
3437 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t divisions)
3439 return beat_at_pulse_locked (_metrics, exact_qn_at_frame_locked (metrics, frame, divisions) / 4.0);
3442 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
3443 * the supplied frame, possibly returning a negative value.
3445 * @param frame The session frame position.
3446 * @param sub_num The subdivision to use when rounding the quarter note.
3447 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3448 * Positive integers indicate quarter note (non BBT) divisions.
3449 * 0 indicates that the returned quarter note should not be rounded (equivalent to quarter_note_at_frame()).
3450 * @return The quarter note position of the supplied frame.
3452 * When working to a musical grid, the use of sub_nom indicates that
3453 * the frame position should be interpreted musically.
3455 * it effectively snaps to meter bars, meter beats or quarter note divisions
3456 * (as per current gui convention) and returns a musical position independent of frame rate.
3458 * If the supplied frame lies before the first meter, the return will be negative,
3459 * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
3460 * the continuation of the tempo curve (backwards).
3462 * This function is tempo-sensitive.
3465 TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num)
3467 Glib::Threads::RWLock::ReaderLock lm (lock);
3469 return exact_qn_at_frame_locked (_metrics, frame, sub_num);
3473 TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num)
3475 double qn = quarter_note_at_minute_locked (metrics, minute_at_frame (frame));
3478 qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
3479 } else if (sub_num == 1) {
3480 /* the gui requested exact musical (BBT) beat */
3481 qn = quarter_note_at_beat_locked (metrics, floor (beat_at_minute_locked (metrics, minute_at_frame (frame)) + 0.5));
3482 } else if (sub_num == -1) {
3484 Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
3488 const double prev_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3490 const double next_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3492 if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
3502 /** returns the frame duration of the supplied BBT time at a specified frame position in the tempo map.
3503 * @param pos the frame position in the tempo map.
3504 * @param bbt the distance in BBT time from pos to calculate.
3505 * @param dir the rounding direction..
3506 * @return the duration in frames between pos and bbt
3509 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
3511 Glib::Threads::RWLock::ReaderLock lm (lock);
3513 BBT_Time pos_bbt = bbt_at_minute_locked (_metrics, minute_at_frame (pos));
3514 const framecnt_t offset = frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3515 const double divisions = meter_section_at_minute_locked (_metrics, minute_at_frame (pos)).divisions_per_bar();
3518 pos_bbt.bars += bbt.bars;
3520 pos_bbt.ticks += bbt.ticks;
3521 if ((double) pos_bbt.ticks > BBT_Time::ticks_per_beat) {
3523 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3526 pos_bbt.beats += bbt.beats;
3527 if ((double) pos_bbt.beats > divisions) {
3529 pos_bbt.beats -= divisions;
3532 return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt)) - offset;
3534 pos_bbt.bars -= bbt.bars;
3536 if (pos_bbt.ticks < bbt.ticks) {
3537 if (pos_bbt.beats == 1) {
3539 pos_bbt.beats = divisions;
3543 pos_bbt.ticks = BBT_Time::ticks_per_beat - (bbt.ticks - pos_bbt.ticks);
3545 pos_bbt.ticks -= bbt.ticks;
3548 if (pos_bbt.beats <= bbt.beats) {
3550 pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
3552 pos_bbt.beats -= bbt.beats;
3555 return offset - frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3562 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
3564 return round_to_type (fr, dir, Bar);
3568 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
3570 return round_to_type (fr, dir, Beat);
3574 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3576 Glib::Threads::RWLock::ReaderLock lm (lock);
3577 uint32_t ticks = (uint32_t) floor (max (0.0, beat_at_minute_locked (_metrics, minute_at_frame (fr))) * BBT_Time::ticks_per_beat);
3578 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3579 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3581 ticks -= beats * BBT_Time::ticks_per_beat;
3584 /* round to next (or same iff dir == RoundUpMaybe) */
3586 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3588 if (mod == 0 && dir == RoundUpMaybe) {
3589 /* right on the subdivision, which is fine, so do nothing */
3591 } else if (mod == 0) {
3592 /* right on the subdivision, so the difference is just the subdivision ticks */
3593 ticks += ticks_one_subdivisions_worth;
3596 /* not on subdivision, compute distance to next subdivision */
3598 ticks += ticks_one_subdivisions_worth - mod;
3601 if (ticks >= BBT_Time::ticks_per_beat) {
3602 ticks -= BBT_Time::ticks_per_beat;
3604 } else if (dir < 0) {
3606 /* round to previous (or same iff dir == RoundDownMaybe) */
3608 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3610 if (difference == 0 && dir == RoundDownAlways) {
3611 /* right on the subdivision, but force-rounding down,
3612 so the difference is just the subdivision ticks */
3613 difference = ticks_one_subdivisions_worth;
3616 if (ticks < difference) {
3617 ticks = BBT_Time::ticks_per_beat - ticks;
3619 ticks -= difference;
3623 /* round to nearest */
3626 /* compute the distance to the previous and next subdivision */
3628 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3630 /* closer to the next subdivision, so shift forward */
3632 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3634 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3636 if (ticks > BBT_Time::ticks_per_beat) {
3638 ticks -= BBT_Time::ticks_per_beat;
3639 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3642 } else if (rem > 0) {
3644 /* closer to previous subdivision, so shift backward */
3648 /* can't go backwards past zero, so ... */
3651 /* step back to previous beat */
3653 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3654 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3656 ticks = lrint (ticks - rem);
3657 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3660 /* on the subdivision, do nothing */
3664 const framepos_t ret_frame = frame_at_minute (minute_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat)));
3670 TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3672 Glib::Threads::RWLock::ReaderLock lm (lock);
3673 uint32_t ticks = (uint32_t) floor (max (0.0, quarter_note_at_minute_locked (_metrics, minute_at_frame (fr))) * BBT_Time::ticks_per_beat);
3674 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3675 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3677 ticks -= beats * BBT_Time::ticks_per_beat;
3680 /* round to next (or same iff dir == RoundUpMaybe) */
3682 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3684 if (mod == 0 && dir == RoundUpMaybe) {
3685 /* right on the subdivision, which is fine, so do nothing */
3687 } else if (mod == 0) {
3688 /* right on the subdivision, so the difference is just the subdivision ticks */
3689 ticks += ticks_one_subdivisions_worth;
3692 /* not on subdivision, compute distance to next subdivision */
3694 ticks += ticks_one_subdivisions_worth - mod;
3697 if (ticks >= BBT_Time::ticks_per_beat) {
3698 ticks -= BBT_Time::ticks_per_beat;
3700 } else if (dir < 0) {
3702 /* round to previous (or same iff dir == RoundDownMaybe) */
3704 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3706 if (difference == 0 && dir == RoundDownAlways) {
3707 /* right on the subdivision, but force-rounding down,
3708 so the difference is just the subdivision ticks */
3709 difference = ticks_one_subdivisions_worth;
3712 if (ticks < difference) {
3713 ticks = BBT_Time::ticks_per_beat - ticks;
3715 ticks -= difference;
3719 /* round to nearest */
3722 /* compute the distance to the previous and next subdivision */
3724 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3726 /* closer to the next subdivision, so shift forward */
3728 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3730 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3732 if (ticks > BBT_Time::ticks_per_beat) {
3734 ticks -= BBT_Time::ticks_per_beat;
3735 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3738 } else if (rem > 0) {
3740 /* closer to previous subdivision, so shift backward */
3744 /* can't go backwards past zero, so ... */
3747 /* step back to previous beat */
3749 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3750 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3752 ticks = lrint (ticks - rem);
3753 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3756 /* on the subdivision, do nothing */
3760 const framepos_t ret_frame = frame_at_minute (minute_at_quarter_note_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat)));
3766 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
3768 Glib::Threads::RWLock::ReaderLock lm (lock);
3770 const double beat_at_framepos = max (0.0, beat_at_minute_locked (_metrics, minute_at_frame (frame)));
3771 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
3776 /* find bar previous to 'frame' */
3779 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3781 } else if (dir > 0) {
3782 /* find bar following 'frame' */
3786 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3788 /* true rounding: find nearest bar */
3789 framepos_t raw_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3792 framepos_t prev_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3794 framepos_t next_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3796 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
3807 return frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos)));
3808 } else if (dir > 0) {
3809 return frame_at_minute (minute_at_beat_locked (_metrics, ceil (beat_at_framepos)));
3811 return frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5)));
3820 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
3821 framepos_t lower, framepos_t upper, uint32_t bar_mod)
3823 Glib::Threads::RWLock::ReaderLock lm (lock);
3824 int32_t cnt = ceil (beat_at_minute_locked (_metrics, minute_at_frame (lower)));
3826 /* although the map handles negative beats, bbt doesn't. */
3831 if (minute_at_beat_locked (_metrics, cnt) >= minute_at_frame (upper)) {
3835 while (pos >= 0 && pos < upper) {
3836 pos = frame_at_minute (minute_at_beat_locked (_metrics, cnt));
3837 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
3838 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
3839 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
3840 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c_func()));
3844 BBT_Time bbt = bbt_at_minute_locked (_metrics, minute_at_frame (lower));
3849 bbt.bars -= bbt.bars % bar_mod;
3853 while (pos >= 0 && pos < upper) {
3854 pos = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3855 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
3856 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
3857 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c_func()));
3858 bbt.bars += bar_mod;
3864 TempoMap::tempo_section_at_frame (framepos_t frame) const
3866 Glib::Threads::RWLock::ReaderLock lm (lock);
3868 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
3872 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute) const
3874 TempoSection* prev = 0;
3878 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3880 if ((*i)->is_tempo()) {
3881 t = static_cast<TempoSection*> (*i);
3885 if (prev && t->minute() > minute) {
3895 abort(); /*NOTREACHED*/
3902 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3904 TempoSection* prev_t = 0;
3905 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
3909 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3910 if ((*i)->is_tempo()) {
3911 t = static_cast<TempoSection*> (*i);
3912 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
3922 /* don't use this to calculate length (the tempo is only correct for this frame).
3923 do that stuff based on the beat_at_frame and frame_at_beat api
3926 TempoMap::frames_per_quarter_note_at (const framepos_t& frame, const framecnt_t& sr) const
3928 Glib::Threads::RWLock::ReaderLock lm (lock);
3930 const TempoSection* ts_at = 0;
3931 const TempoSection* ts_after = 0;
3932 Metrics::const_iterator i;
3935 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3937 if ((*i)->is_tempo()) {
3938 t = static_cast<TempoSection*> (*i);
3942 if (ts_at && (*i)->frame() > frame) {
3951 return (60.0 * _frame_rate) / ts_at->tempo_at_minute (minute_at_frame (frame)).quarter_notes_per_minute();
3953 /* must be treated as constant tempo */
3954 return ts_at->frames_per_quarter_note (_frame_rate);
3958 TempoMap::meter_section_at_frame (framepos_t frame) const
3960 Glib::Threads::RWLock::ReaderLock lm (lock);
3961 return meter_section_at_minute_locked (_metrics, minute_at_frame (frame));
3965 TempoMap::meter_section_at_minute_locked (const Metrics& metrics, double minute) const
3967 Metrics::const_iterator i;
3968 MeterSection* prev = 0;
3972 for (i = metrics.begin(); i != metrics.end(); ++i) {
3974 if (!(*i)->is_tempo()) {
3975 m = static_cast<MeterSection*> (*i);
3977 if (prev && (*i)->minute() > minute) {
3987 abort(); /*NOTREACHED*/
3994 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3996 MeterSection* prev_m = 0;
3998 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4000 if (!(*i)->is_tempo()) {
4001 m = static_cast<MeterSection*> (*i);
4002 if (prev_m && m->beat() > beat) {
4013 TempoMap::meter_section_at_beat (double beat) const
4015 Glib::Threads::RWLock::ReaderLock lm (lock);
4016 return meter_section_at_beat_locked (_metrics, beat);
4020 TempoMap::meter_at_frame (framepos_t frame) const
4022 TempoMetric m (metric_at (frame));
4027 TempoMap::fix_legacy_session ()
4029 MeterSection* prev_m = 0;
4030 TempoSection* prev_t = 0;
4032 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4036 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
4037 if (!m->movable()) {
4038 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
4041 m->set_minute (0.0);
4042 m->set_position_lock_style (AudioTime);
4047 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
4048 + (m->bbt().beats - 1)
4049 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
4051 m->set_beat (start);
4052 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
4053 + (m->bbt().beats - 1)
4054 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
4055 m->set_pulse (start_beat / prev_m->note_divisor());
4058 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
4064 if (!t->movable()) {
4066 t->set_minute (0.0);
4067 t->set_position_lock_style (AudioTime);
4073 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
4074 + (t->legacy_bbt().beats - 1)
4075 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
4077 t->set_pulse (beat / prev_m->note_divisor());
4079 /* really shouldn't happen but.. */
4080 t->set_pulse (beat / 4.0);
4089 TempoMap::get_state ()
4091 Metrics::const_iterator i;
4092 XMLNode *root = new XMLNode ("TempoMap");
4095 Glib::Threads::RWLock::ReaderLock lm (lock);
4096 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4097 root->add_child_nocopy ((*i)->get_state());
4105 TempoMap::set_state (const XMLNode& node, int /*version*/)
4108 Glib::Threads::RWLock::WriterLock lm (lock);
4111 XMLNodeConstIterator niter;
4112 Metrics old_metrics (_metrics);
4115 nlist = node.children();
4117 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
4118 XMLNode* child = *niter;
4120 if (child->name() == TempoSection::xml_state_node_name) {
4123 TempoSection* ts = new TempoSection (*child, _frame_rate);
4124 _metrics.push_back (ts);
4127 catch (failed_constructor& err){
4128 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4129 _metrics = old_metrics;
4130 old_metrics.clear();
4134 } else if (child->name() == MeterSection::xml_state_node_name) {
4137 MeterSection* ms = new MeterSection (*child, _frame_rate);
4138 _metrics.push_back (ms);
4141 catch (failed_constructor& err) {
4142 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4143 _metrics = old_metrics;
4144 old_metrics.clear();
4150 if (niter == nlist.end()) {
4151 MetricSectionSorter cmp;
4152 _metrics.sort (cmp);
4155 /* check for legacy sessions where bbt was the base musical unit for tempo */
4156 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4158 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
4159 if (t->legacy_bbt().bars != 0) {
4160 fix_legacy_session();
4167 /* check for multiple tempo/meters at the same location, which
4168 ardour2 somehow allowed.
4171 Metrics::iterator prev = _metrics.end();
4172 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4173 if (prev != _metrics.end()) {
4175 MeterSection* prev_m;
4177 TempoSection* prev_t;
4178 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
4179 if (prev_m->pulse() == ms->pulse()) {
4180 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4181 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4184 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
4185 if (prev_t->pulse() == ts->pulse()) {
4186 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4187 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4195 recompute_map (_metrics);
4197 Metrics::const_iterator d = old_metrics.begin();
4198 while (d != old_metrics.end()) {
4202 old_metrics.clear ();
4205 PropertyChanged (PropertyChange ());
4211 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
4213 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
4214 const MeterSection* m;
4215 const TempoSection* t;
4216 const TempoSection* prev_t = 0;
4218 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4220 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
4221 o << "Tempo @ " << *i << t->note_types_per_minute() << " BPM (pulse = 1/" << t->note_type()
4222 << " type= " << enum_2_string (t->type()) << ") " << " at pulse= " << t->pulse()
4223 << " minute= " << t->minute() << " frame= " << t->frame() << " (movable? " << t->movable() << ')'
4224 << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
4226 o << std::setprecision (17) << " current : " << t->note_types_per_minute()
4227 << " | " << t->pulse() << " | " << t->frame() << " | " << t->minute() << std::endl;
4228 o << " previous : " << prev_t->note_types_per_minute()
4229 << " | " << prev_t->pulse() << " | " << prev_t->frame() << " | " << prev_t->minute() << std::endl;
4230 o << " calculated : " << prev_t->tempo_at_pulse (t->pulse())
4231 << " | " << prev_t->pulse_at_ntpm (t->note_types_per_minute(), t->minute())
4232 << " | " << frame_at_minute (prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()))
4233 << " | " << prev_t->minute_at_ntpm (t->note_types_per_minute(), t->pulse()) << std::endl;
4236 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
4237 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt()
4238 << " frame= " << m->frame() << " pulse: " << m->pulse() << " beat : " << m->beat()
4239 << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
4242 o << "------" << std::endl;
4246 TempoMap::n_tempos() const
4248 Glib::Threads::RWLock::ReaderLock lm (lock);
4251 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4252 if ((*i)->is_tempo()) {
4261 TempoMap::n_meters() const
4263 Glib::Threads::RWLock::ReaderLock lm (lock);
4266 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4267 if (!(*i)->is_tempo()) {
4276 TempoMap::insert_time (framepos_t where, framecnt_t amount)
4278 for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
4279 if ((*i)->frame() >= where && (*i)->movable ()) {
4283 if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
4284 gui_move_meter (ms, (*i)->frame() + amount);
4287 if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
4288 gui_move_tempo (ts, (*i)->frame() + amount, 0);
4293 PropertyChanged (PropertyChange ());
4297 TempoMap::remove_time (framepos_t where, framecnt_t amount)
4301 std::list<MetricSection*> metric_kill_list;
4303 TempoSection* last_tempo = NULL;
4304 MeterSection* last_meter = NULL;
4305 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
4306 bool meter_after = false; // is there a meter marker likewise?
4308 Glib::Threads::RWLock::WriterLock lm (lock);
4309 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4310 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
4311 metric_kill_list.push_back(*i);
4312 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
4315 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
4319 else if ((*i)->frame() >= where) {
4320 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
4321 (*i)->set_minute ((*i)->minute() - minute_at_frame (amount));
4322 if ((*i)->frame() == where) {
4323 // marker was immediately after end of range
4324 tempo_after = dynamic_cast<TempoSection*> (*i);
4325 meter_after = dynamic_cast<MeterSection*> (*i);
4331 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
4332 if (last_tempo && !tempo_after) {
4333 metric_kill_list.remove(last_tempo);
4334 last_tempo->set_minute (minute_at_frame (where));
4337 if (last_meter && !meter_after) {
4338 metric_kill_list.remove(last_meter);
4339 last_meter->set_minute (minute_at_frame (where));
4343 //remove all the remaining metrics
4344 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
4345 _metrics.remove(*i);
4350 recompute_map (_metrics);
4353 PropertyChanged (PropertyChange ());
4357 /** Add some (fractional) Beats to a session frame position, and return the result in frames.
4358 * pos can be -ve, if required.
4361 TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats quarter_note) const
4363 Glib::Threads::RWLock::ReaderLock lm (lock);
4365 return frame_at_minute (minute_at_quarter_note_locked (_metrics, quarter_note_at_minute_locked (_metrics, minute_at_frame (frame)) + quarter_note.to_double()));
4369 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
4371 Glib::Threads::RWLock::ReaderLock lm (lock);
4373 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_minute_locked (_metrics, minute_at_frame (pos)));
4374 pos_bbt.ticks += op.ticks;
4375 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
4377 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
4379 pos_bbt.beats += op.beats;
4380 /* the meter in effect will start on the bar */
4381 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();
4382 while (pos_bbt.beats >= divisions_per_bar + 1) {
4384 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4385 pos_bbt.beats -= divisions_per_bar;
4387 pos_bbt.bars += op.bars;
4389 return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
4392 /** Count the number of beats that are equivalent to distance when going forward,
4396 TempoMap::framewalk_to_qn (framepos_t pos, framecnt_t distance) const
4398 Glib::Threads::RWLock::ReaderLock lm (lock);
4400 return Evoral::Beats (quarter_notes_between_frames_locked (_metrics, pos, pos + distance));
4404 bool operator() (const BBT_Time& a, const BBT_Time& b) {
4410 operator<< (std::ostream& o, const Meter& m) {
4411 return o << m.divisions_per_bar() << '/' << m.note_divisor();
4415 operator<< (std::ostream& o, const Tempo& t) {
4416 return o << t.note_types_per_minute() << " 1/" << t.note_type() << "'s per minute";
4420 operator<< (std::ostream& o, const MetricSection& section) {
4422 o << "MetricSection @ " << section.frame() << ' ';
4424 const TempoSection* ts;
4425 const MeterSection* ms;
4427 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
4428 o << *((const Tempo*) ts);
4429 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
4430 o << *((const Meter*) ms);