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.beats_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;
101 _legacy_bbt = BBT_Time (0, 0, 0);
103 if ((prop = node.property ("start")) != 0) {
104 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
108 /* legacy session - start used to be in bbt*/
111 info << _("Legacy session detected. TempoSection XML node will be altered.") << endmsg;
115 if ((prop = node.property ("pulse")) != 0) {
116 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
117 error << _("TempoSection XML node has an illegal \"pulse\" value") << endmsg;
123 if ((prop = node.property ("frame")) != 0) {
124 if (sscanf (prop->value().c_str(), "%" PRIu32, &frame) != 1) {
125 error << _("TempoSection XML node has an illegal \"frame\" value") << endmsg;
127 set_minute (minute_at_frame (frame));
131 if ((prop = node.property ("minute")) != 0) {
132 if (sscanf (prop->value().c_str(), "%lf", &minute) != 1) {
133 error << _("TempoSection XML node has an illegal \"minute\" value") << endmsg;
139 if ((prop = node.property ("beats-per-minute")) == 0) {
140 error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
141 throw failed_constructor();
144 if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
145 error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
146 throw failed_constructor();
149 if ((prop = node.property ("note-type")) == 0) {
150 /* older session, make note type be quarter by default */
153 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
154 error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
155 throw failed_constructor();
159 if ((prop = node.property ("movable")) == 0) {
160 error << _("TempoSection XML node has no \"movable\" property") << endmsg;
161 throw failed_constructor();
164 set_movable (string_is_affirmative (prop->value()));
166 if ((prop = node.property ("active")) == 0) {
167 warning << _("TempoSection XML node has no \"active\" property") << endmsg;
170 set_active (string_is_affirmative (prop->value()));
173 if ((prop = node.property ("tempo-type")) == 0) {
176 _type = Type (string_2_enum (prop->value(), _type));
179 if ((prop = node.property ("lock-style")) == 0) {
181 set_position_lock_style (MusicTime);
183 set_position_lock_style (AudioTime);
186 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
189 if ((prop = node.property ("locked-to-meter")) == 0) {
190 set_locked_to_meter (false);
192 set_locked_to_meter (string_is_affirmative (prop->value()));
197 TempoSection::get_state() const
199 XMLNode *root = new XMLNode (xml_state_node_name);
203 snprintf (buf, sizeof (buf), "%lf", pulse());
204 root->add_property ("pulse", buf);
205 snprintf (buf, sizeof (buf), "%li", frame());
206 root->add_property ("frame", buf);
207 snprintf (buf, sizeof (buf), "%lf", minute());
208 root->add_property ("minute", buf);
209 snprintf (buf, sizeof (buf), "%lf", _beats_per_minute);
210 root->add_property ("beats-per-minute", buf);
211 snprintf (buf, sizeof (buf), "%lf", _note_type);
212 root->add_property ("note-type", buf);
213 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
214 root->add_property ("movable", buf);
215 snprintf (buf, sizeof (buf), "%s", active()?"yes":"no");
216 root->add_property ("active", buf);
217 root->add_property ("tempo-type", enum_2_string (_type));
218 root->add_property ("lock-style", enum_2_string (position_lock_style()));
219 root->add_property ("locked-to-meter", locked_to_meter()?"yes":"no");
225 TempoSection::set_type (Type type)
230 /** returns the tempo in beats per minute at the zero-based (relative to session) minute.
233 TempoSection::tempo_at_minute (const double& m) const
236 if (_type == Constant || _c_func == 0.0) {
237 return beats_per_minute();
240 return _tempo_at_time (m - minute());
243 /** returns the zero-based minute (relative to session)
244 where the tempo in beats per minute occurs in this section.
245 pulse p is only used for constant tempi.
246 note that the tempo map may have multiple such values.
249 TempoSection::minute_at_tempo (const double& bpm, const double& p) const
251 if (_type == Constant || _c_func == 0.0) {
252 return (((p - pulse()) * note_type()) / beats_per_minute()) + minute();
255 return _time_at_tempo (bpm) + minute();
257 /** returns the tempo in beats per minute at the zero-based (relative to session) pulse.
260 TempoSection::tempo_at_pulse (const double& p) const
263 if (_type == Constant || _c_func == 0.0) {
264 return beats_per_minute();
267 return _tempo_at_pulse (p - pulse());
270 /** returns the zero-based pulse (relative to session)
271 where the tempo in qn beats per minute occurs given frame f. frame f is only used for constant tempi.
272 note that the session tempo map may have multiple beats at a given tempo.
275 TempoSection::pulse_at_tempo (const double& bpm, const double& m) const
277 if (_type == Constant || _c_func == 0.0) {
278 const double pulses = (((m - minute()) * beats_per_minute()) / note_type()) + pulse();
282 return _pulse_at_tempo (bpm) + pulse();
285 /** returns the zero-based pulse (relative to session origin)
286 where the zero-based frame (relative to session)
290 TempoSection::pulse_at_frame (const framepos_t& f) const
292 return pulse_at_minute (minute_at_frame (f));
296 TempoSection::pulse_at_minute (const double& m) const
298 if (_type == Constant || _c_func == 0.0) {
299 return (((m - minute()) * beats_per_minute()) / _note_type) + pulse();
302 return _pulse_at_time (m - minute()) + pulse();
305 /** returns the zero-based frame (relative to session start frame)
306 where the zero-based pulse (relative to session start)
311 TempoSection::frame_at_pulse (const double& p) const
313 return frame_at_minute (minute_at_pulse (p));
317 TempoSection::minute_at_pulse (const double& p) const
319 if (_type == Constant || _c_func == 0.0) {
320 return (((p - pulse()) * note_type()) / beats_per_minute()) + minute();
323 return _time_at_pulse (p - pulse()) + minute();
331 Tt----|-----------------*|
332 Ta----|--------------|* |
338 _______________|___|____
339 time a t (next tempo)
342 Duration in beats at time a is the integral of some Tempo function.
343 In our case, the Tempo function (Tempo at time t) is
346 with function constant
351 The integral over t of our Tempo function (the beat function, which is the duration in beats at some time t) is:
352 b(t) = T0(e^(ct) - 1) / c
354 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:
355 t(b) = log((c.b / T0) + 1) / c
357 The time t at which Tempo T occurs is a as above:
358 t(T) = log(T / T0) / c
360 The beat at which a Tempo T occurs is:
363 The Tempo at which beat b occurs is:
366 We define c for this tempo ramp by placing a new tempo section at some time t after this one.
367 Our problem is that we usually don't know t.
368 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.
369 Where a = t (i.e. when a is equal to the time of the next tempo section), the beat function reveals:
370 t = b log (Ta / T0) / (T0 (e^(log (Ta / T0)) - 1))
372 By substituting our expanded t as a in the c function above, our problem is reduced to:
373 c = T0 (e^(log (Ta / T0)) - 1) / b
375 Of course the word 'beat' has been left loosely defined above.
376 In music, a beat is defined by the musical pulse (which comes from the tempo)
377 and the meter in use at a particular time (how many pulse divisions there are in one bar).
378 It would be more accurate to substitute the work 'pulse' for 'beat' above.
382 We can now store c for future time calculations.
383 If the following tempo section (the one that defines c in conjunction with this one)
384 is changed or moved, c is no longer valid.
386 The public methods are session-relative.
388 Most of this stuff is taken from this paper:
391 TOOLS FOR DYNAMIC TEMPO CALCULATIONS
394 Zurich University of Arts
395 Institute for Computer Music and Sound Technology
397 https://www.zhdk.ch/fileadmin/data_subsites/data_icst/Downloads/Timegrid/ICST_Tempopolyphony_ICMC07.pdf
402 compute this ramp's function constant using the end tempo (in qn beats per minute)
403 and duration (pulses into global start) of some later tempo section.
406 TempoSection::compute_c_func_pulse (const double& end_bpm, const double& end_pulse) const
408 double const log_tempo_ratio = log (end_bpm / beats_per_minute());
409 return (beats_per_minute() * expm1 (log_tempo_ratio)) / ((end_pulse - pulse()) * _note_type);
412 /* compute the function constant from some later tempo section, given tempo (quarter notes/min.) and distance (in frames) from session origin */
414 TempoSection::compute_c_func_minute (const double& end_bpm, const double& end_minute) const
416 return c_func (end_bpm, end_minute - minute());
419 /* position function */
421 TempoSection::a_func (double end_bpm, double c_func) const
423 return log (end_bpm / beats_per_minute()) / c_func;
426 /*function constant*/
428 TempoSection::c_func (double end_bpm, double end_time) const
430 return log (end_bpm / beats_per_minute()) / end_time;
433 /* tempo in bpm at time in minutes */
435 TempoSection::_tempo_at_time (const double& time) const
437 return exp (_c_func * time) * beats_per_minute();
440 /* time in minutes at tempo in bpm */
442 TempoSection::_time_at_tempo (const double& tempo) const
444 return log (tempo / beats_per_minute()) / _c_func;
447 /* pulse at tempo in bpm */
449 TempoSection::_pulse_at_tempo (const double& tempo) const
451 return ((tempo - beats_per_minute()) / _c_func) / _note_type;
454 /* tempo in bpm at pulse */
456 TempoSection::_tempo_at_pulse (const double& pulse) const
458 return (pulse * _note_type * _c_func) + beats_per_minute();
461 /* pulse at time in minutes */
463 TempoSection::_pulse_at_time (const double& time) const
465 return expm1 (_c_func * time) * (beats_per_minute() / (_c_func * _note_type));
468 /* time in minutes at pulse */
470 TempoSection::_time_at_pulse (const double& pulse) const
472 return log1p ((_c_func * pulse * _note_type) / beats_per_minute()) / _c_func;
475 /***********************************************************************/
477 const string MeterSection::xml_state_node_name = "Meter";
479 MeterSection::MeterSection (const XMLNode& node, const framecnt_t sample_rate)
480 : MetricSection (0.0, 0, MusicTime, false, sample_rate), Meter (TempoMap::default_meter())
482 XMLProperty const * prop;
487 framepos_t frame = 0;
488 pair<double, BBT_Time> start;
491 if ((prop = node.property ("start")) != 0) {
492 if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
496 error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
498 /* legacy session - start used to be in bbt*/
499 info << _("Legacy session detected - MeterSection XML node will be altered.") << endmsg;
504 if ((prop = node.property ("pulse")) != 0) {
505 if (sscanf (prop->value().c_str(), "%lf", &pulse) != 1) {
506 error << _("MeterSection XML node has an illegal \"pulse\" value") << endmsg;
511 if ((prop = node.property ("beat")) != 0) {
512 if (sscanf (prop->value().c_str(), "%lf", &beat) != 1) {
513 error << _("MeterSection XML node has an illegal \"beat\" value") << endmsg;
519 if ((prop = node.property ("bbt")) == 0) {
520 warning << _("MeterSection XML node has no \"bbt\" property") << endmsg;
521 } else if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
525 error << _("MeterSection XML node has an illegal \"bbt\" value") << endmsg;
526 throw failed_constructor();
532 if ((prop = node.property ("frame")) != 0) {
533 if (sscanf (prop->value().c_str(), "%li", &frame) != 1) {
534 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
536 set_minute (minute_at_frame (frame));
539 if ((prop = node.property ("minute")) != 0) {
540 if (sscanf (prop->value().c_str(), "%lf", &minute) != 1) {
541 error << _("MeterSection XML node has an illegal \"frame\" value") << endmsg;
547 /* beats-per-bar is old; divisions-per-bar is new */
549 if ((prop = node.property ("divisions-per-bar")) == 0) {
550 if ((prop = node.property ("beats-per-bar")) == 0) {
551 error << _("MeterSection XML node has no \"beats-per-bar\" or \"divisions-per-bar\" property") << endmsg;
552 throw failed_constructor();
555 if (sscanf (prop->value().c_str(), "%lf", &_divisions_per_bar) != 1 || _divisions_per_bar < 0.0) {
556 error << _("MeterSection XML node has an illegal \"divisions-per-bar\" value") << endmsg;
557 throw failed_constructor();
560 if ((prop = node.property ("note-type")) == 0) {
561 error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
562 throw failed_constructor();
564 if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
565 error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
566 throw failed_constructor();
569 if ((prop = node.property ("movable")) == 0) {
570 error << _("MeterSection XML node has no \"movable\" property") << endmsg;
571 throw failed_constructor();
574 set_movable (string_is_affirmative (prop->value()));
576 if ((prop = node.property ("lock-style")) == 0) {
577 warning << _("MeterSection XML node has no \"lock-style\" property") << endmsg;
579 set_position_lock_style (MusicTime);
581 set_position_lock_style (AudioTime);
584 set_position_lock_style (PositionLockStyle (string_2_enum (prop->value(), position_lock_style())));
589 MeterSection::get_state() const
591 XMLNode *root = new XMLNode (xml_state_node_name);
595 snprintf (buf, sizeof (buf), "%lf", pulse());
596 root->add_property ("pulse", buf);
597 snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
601 root->add_property ("bbt", buf);
602 snprintf (buf, sizeof (buf), "%lf", beat());
603 root->add_property ("beat", buf);
604 snprintf (buf, sizeof (buf), "%lf", _note_type);
605 root->add_property ("note-type", buf);
606 snprintf (buf, sizeof (buf), "%li", frame());
607 root->add_property ("frame", buf);
608 snprintf (buf, sizeof (buf), "%lf", minute());
609 root->add_property ("minute", buf);
610 root->add_property ("lock-style", enum_2_string (position_lock_style()));
611 snprintf (buf, sizeof (buf), "%lf", _divisions_per_bar);
612 root->add_property ("divisions-per-bar", buf);
613 snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
614 root->add_property ("movable", buf);
619 /***********************************************************************/
623 The Shaggs - Things I Wonder
624 https://www.youtube.com/watch?v=9wQK6zMJOoQ
626 Tempo is the rate of the musical pulse.
627 Meter divides pulse into measures and beats.
629 TempoSection - provides pulse in the form of beats_per_minute() - the number of quarter notes in one minute.
630 Note that 'beats' in Tempo::beats_per_minute() are quarter notes (pulse based). In the rest of tempo map,
631 'beat' usually refers to accumulated BBT beats (pulse and meter based).
633 MeterSecion - divides pulse into measures (via divisions_per_bar) and beats (via note_divisor).
635 Both tempo and meter have a pulse position and a frame position.
636 Meters also have a beat position, which is always 0.0 for the first one.
637 TempoSection and MeterSection may be locked to either audio or music (position lock style).
638 The lock style determines the 'true' position of the section wich is used to calculate the other postion parameters of the section.
640 The first tempo and first meter are special. they must move together, and must be locked to audio.
641 Audio locked tempos which lie before the first meter are made inactive.
642 They will be re-activated if the first meter is again placed before them.
644 With tempo sections potentially being ramped, meters provide a way of mapping beats to whole pulses without
645 referring to the tempo function(s) involved as the distance in whole pulses between a meter and a subsequent beat is
646 sb->beat() - meter->beat() / meter->note_divisor().
647 Because every meter falls on a known pulse, (derived from its bar), the rest is easy as the duration in pulses between
648 two meters is of course
649 (meater_b->bar - meter_a->bar) * meter_a->divisions_per_bar / meter_a->note_divisor.
651 Beat calculations are based on meter sections and all pulse and tempo calculations are based on tempo sections.
652 Beat to frame conversion of course requires the use of meter and tempo.
654 Remembering that ramped tempo sections interact, it is important to avoid referring to any other tempos when moving tempo sections,
655 Here, beats (meters) are used to determine the new pulse (see predict_tempo_position())
657 Recomputing the map is the process where the 'missing' position
658 (tempo pulse or meter pulse & beat in the case of AudioTime, frame for MusicTime) is calculated.
659 We construct the tempo map by first using the frame or pulse position (depending on position lock style) of each tempo.
660 We then use this tempo map (really just the tempos) to find the pulse or frame position of each meter (again depending on lock style).
662 Having done this, we can now find any musical duration by selecting the tempo and meter covering the position (or tempo) in question
663 and querying its appropriate meter/tempo.
665 It is important to keep the _metrics in an order that makes sense.
666 Because ramped MusicTime and AudioTime tempos can interact with each other,
667 reordering is frequent. Care must be taken to keep _metrics in a solved state.
668 Solved means ordered by frame or pulse with frame-accurate precision (see check_solved()).
672 Music and audio-locked objects may seem interchangeable on the surface, but when translating
673 between audio samples and beat, remember that a sample is only a quantised approximation
674 of the actual time (in minutes) of a beat.
675 Thus if a gui user points to the frame occupying the start of a music-locked object on 1|3|0, it does not
676 mean that this frame is the actual location in time of 1|3|0.
678 You cannot use a frame measurement to determine beat distance except under special circumstances
679 (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).
681 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
682 sample space the user is operating at to be translated correctly to the object.
684 The current approach is to interpret the supplied frame using the grid division the user has currently selected.
685 If the user has no musical grid set, they are actually operating in sample space (even SMPTE frames are rounded to audio frame), so
686 the supplied audio frame is interpreted as the desired musical location (beat_at_frame()).
688 tldr: Beat, being a function of time, has nothing to do with sample rate, but time quantization can get in the way of precision.
690 When frame_at_beat() is called, the position calculation is performed in pulses and minutes.
691 The result is rounded to audio frames.
692 When beat_at_frame() is called, the frame is converted to minutes, with no rounding performed on the result.
695 frame_at_beat (beat_at_frame (frame)) == frame
697 beat_at_frame (frame_at_beat (beat)) != beat due to the time quantization of frame_at_beat().
699 Doing the second one will result in a beat distance error of up to 0.5 audio samples.
700 So instead work in pulses and/or beats and only use beat position to caclulate frame position (e.g. after tempo change).
701 For audio-locked objects, use frame position to calculate beat position.
703 The above pointless example would then do:
704 beat_at_pulse (pulse_at_beat (beat)) to avoid rounding.
707 struct MetricSectionSorter {
708 bool operator() (const MetricSection* a, const MetricSection* b) {
709 return a->pulse() < b->pulse();
713 struct MetricSectionFrameSorter {
714 bool operator() (const MetricSection* a, const MetricSection* b) {
715 return a->frame() < b->frame();
719 TempoMap::TempoMap (framecnt_t fr)
722 BBT_Time start (1, 1, 0);
724 TempoSection *t = new TempoSection (0.0, 0.0, _default_tempo.beats_per_minute(), _default_tempo.note_type(), TempoSection::Ramp, AudioTime, fr);
725 MeterSection *m = new MeterSection (0.0, 0.0, 0.0, start, _default_meter.divisions_per_bar(), _default_meter.note_divisor(), AudioTime, fr);
727 t->set_movable (false);
728 m->set_movable (false);
730 /* note: frame time is correct (zero) for both of these */
732 _metrics.push_back (t);
733 _metrics.push_back (m);
737 TempoMap::~TempoMap ()
739 Metrics::const_iterator d = _metrics.begin();
740 while (d != _metrics.end()) {
748 TempoMap::frame_at_minute (const double time) const
750 return (framepos_t) floor ((time * 60.0 * _frame_rate) + 0.5);
754 TempoMap::minute_at_frame (const framepos_t frame) const
756 return (frame / (double) _frame_rate) / 60.0;
760 TempoMap::remove_tempo (const TempoSection& tempo, bool complete_operation)
762 bool removed = false;
765 Glib::Threads::RWLock::WriterLock lm (lock);
766 if ((removed = remove_tempo_locked (tempo))) {
767 if (complete_operation) {
768 recompute_map (_metrics);
773 if (removed && complete_operation) {
774 PropertyChanged (PropertyChange ());
779 TempoMap::remove_tempo_locked (const TempoSection& tempo)
783 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
784 if (dynamic_cast<TempoSection*> (*i) != 0) {
785 if (tempo.frame() == (*i)->frame()) {
786 if ((*i)->movable()) {
799 TempoMap::remove_meter (const MeterSection& tempo, bool complete_operation)
801 bool removed = false;
804 Glib::Threads::RWLock::WriterLock lm (lock);
805 if ((removed = remove_meter_locked (tempo))) {
806 if (complete_operation) {
807 recompute_map (_metrics);
812 if (removed && complete_operation) {
813 PropertyChanged (PropertyChange ());
818 TempoMap::remove_meter_locked (const MeterSection& meter)
821 if (meter.position_lock_style() == AudioTime) {
822 /* remove meter-locked tempo */
823 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
825 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
826 if (t->locked_to_meter() && meter.frame() == (*i)->frame()) {
835 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
836 if (dynamic_cast<MeterSection*> (*i) != 0) {
837 if (meter.frame() == (*i)->frame()) {
838 if ((*i)->movable()) {
851 TempoMap::do_insert (MetricSection* section)
853 bool need_add = true;
854 /* we only allow new meters to be inserted on beat 1 of an existing
858 if ((m = dynamic_cast<MeterSection*>(section)) != 0) {
860 if ((m->bbt().beats != 1) || (m->bbt().ticks != 0)) {
862 pair<double, BBT_Time> corrected = make_pair (m->beat(), m->bbt());
863 corrected.second.beats = 1;
864 corrected.second.ticks = 0;
865 corrected.first = beat_at_bbt_locked (_metrics, corrected.second);
866 warning << string_compose (_("Meter changes can only be positioned on the first beat of a bar. Moving from %1 to %2"),
867 m->bbt(), corrected.second) << endmsg;
868 //m->set_pulse (corrected);
872 /* Look for any existing MetricSection that is of the same type and
873 in the same bar as the new one, and remove it before adding
874 the new one. Note that this means that if we find a matching,
875 existing section, we can break out of the loop since we're
876 guaranteed that there is only one such match.
879 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
881 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
882 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
883 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
884 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
886 if (tempo && insert_tempo) {
889 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
890 if ((ipm && tempo->pulse() == insert_tempo->pulse()) || (!ipm && tempo->frame() == insert_tempo->frame())) {
892 if (!tempo->movable()) {
894 /* can't (re)move this section, so overwrite
895 * its data content (but not its properties as
899 *(dynamic_cast<Tempo*>(*i)) = *(dynamic_cast<Tempo*>(insert_tempo));
900 (*i)->set_position_lock_style (AudioTime);
902 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
903 t->set_type (insert_tempo->type());
913 } else if (meter && insert_meter) {
917 bool const ipm = insert_meter->position_lock_style() == MusicTime;
919 if ((ipm && meter->beat() == insert_meter->beat()) || (!ipm && meter->frame() == insert_meter->frame())) {
921 if (!meter->movable()) {
923 /* can't (re)move this section, so overwrite
924 * its data content (but not its properties as
928 *(dynamic_cast<Meter*>(*i)) = *(dynamic_cast<Meter*>(insert_meter));
929 (*i)->set_position_lock_style (AudioTime);
939 /* non-matching types, so we don't care */
943 /* Add the given MetricSection, if we didn't just reset an existing
948 MeterSection* const insert_meter = dynamic_cast<MeterSection*> (section);
949 TempoSection* const insert_tempo = dynamic_cast<TempoSection*> (section);
952 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
953 MeterSection* const meter = dynamic_cast<MeterSection*> (*i);
956 bool const ipm = insert_meter->position_lock_style() == MusicTime;
957 if ((ipm && meter->beat() > insert_meter->beat()) || (!ipm && meter->frame() > insert_meter->frame())) {
962 } else if (insert_tempo) {
963 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
964 TempoSection* const tempo = dynamic_cast<TempoSection*> (*i);
967 bool const ipm = insert_tempo->position_lock_style() == MusicTime;
968 if ((ipm && tempo->pulse() > insert_tempo->pulse()) || (!ipm && tempo->frame() > insert_tempo->frame())) {
975 _metrics.insert (i, section);
976 //dump (_metrics, std::cout);
979 /* user supplies the exact pulse if pls == MusicTime */
981 TempoMap::add_tempo (const Tempo& tempo, const double& pulse, const framepos_t& frame, ARDOUR::TempoSection::Type type, PositionLockStyle pls)
983 TempoSection* ts = 0;
985 Glib::Threads::RWLock::WriterLock lm (lock);
986 ts = add_tempo_locked (tempo, pulse, minute_at_frame (frame), type, pls, true);
990 PropertyChanged (PropertyChange ());
996 TempoMap::replace_tempo (const TempoSection& ts, const Tempo& tempo, const double& pulse, const framepos_t& frame, TempoSection::Type type, PositionLockStyle pls)
998 const bool locked_to_meter = ts.locked_to_meter();
1001 Glib::Threads::RWLock::WriterLock lm (lock);
1002 TempoSection& first (first_tempo());
1003 if (ts.frame() != first.frame()) {
1004 remove_tempo_locked (ts);
1005 add_tempo_locked (tempo, pulse, minute_at_frame (frame), type, pls, true, locked_to_meter);
1007 first.set_type (type);
1008 first.set_pulse (0.0);
1009 first.set_minute (minute_at_frame (frame));
1010 first.set_position_lock_style (AudioTime);
1012 /* cannot move the first tempo section */
1013 *static_cast<Tempo*>(&first) = tempo;
1014 recompute_map (_metrics);
1019 PropertyChanged (PropertyChange ());
1023 TempoMap::add_tempo_locked (const Tempo& tempo, double pulse, double minute
1024 , TempoSection::Type type, PositionLockStyle pls, bool recompute, bool locked_to_meter)
1026 TempoSection* t = new TempoSection (pulse, minute, tempo.beats_per_minute(), tempo.note_type(), type, pls, _frame_rate);
1027 t->set_locked_to_meter (locked_to_meter);
1028 bool solved = false;
1033 if (pls == AudioTime) {
1034 solved = solve_map_minute (_metrics, t, t->minute());
1036 solved = solve_map_pulse (_metrics, t, t->pulse());
1038 recompute_meters (_metrics);
1041 if (!solved && recompute) {
1042 recompute_map (_metrics);
1049 TempoMap::add_meter (const Meter& meter, const double& beat, const Timecode::BBT_Time& where, PositionLockStyle pls)
1051 MeterSection* m = 0;
1053 Glib::Threads::RWLock::WriterLock lm (lock);
1054 m = add_meter_locked (meter, beat, where, pls, true);
1059 if (DEBUG_ENABLED(DEBUG::TempoMap)) {
1060 dump (_metrics, std::cerr);
1064 PropertyChanged (PropertyChange ());
1069 TempoMap::replace_meter (const MeterSection& ms, const Meter& meter, const BBT_Time& where, PositionLockStyle pls)
1072 Glib::Threads::RWLock::WriterLock lm (lock);
1073 const double beat = beat_at_bbt_locked (_metrics, where);
1076 remove_meter_locked (ms);
1077 add_meter_locked (meter, beat, where, pls, true);
1079 MeterSection& first (first_meter());
1080 TempoSection& first_t (first_tempo());
1081 /* cannot move the first meter section */
1082 *static_cast<Meter*>(&first) = meter;
1083 first.set_position_lock_style (AudioTime);
1084 first.set_pulse (0.0);
1085 //first.set_minute (minute_at_frame (frame));
1086 pair<double, BBT_Time> beat = make_pair (0.0, BBT_Time (1, 1, 0));
1087 first.set_beat (beat);
1088 first_t.set_minute (first.minute());
1089 first_t.set_pulse (0.0);
1090 first_t.set_position_lock_style (AudioTime);
1091 recompute_map (_metrics);
1095 PropertyChanged (PropertyChange ());
1099 TempoMap::add_meter_locked (const Meter& meter, double beat, const BBT_Time& where, PositionLockStyle pls, bool recompute)
1101 const MeterSection& prev_m = meter_section_at_minute_locked (_metrics, minute_at_beat_locked (_metrics, beat) - minute_at_frame (1));
1102 const double pulse = ((where.bars - prev_m.bbt().bars) * (prev_m.divisions_per_bar() / prev_m.note_divisor())) + prev_m.pulse();
1103 const double time_minutes = minute_at_pulse_locked (_metrics, pulse);
1104 TempoSection* mlt = 0;
1106 if (pls == AudioTime) {
1107 /* add meter-locked tempo */
1108 mlt = add_tempo_locked (tempo_at_minute_locked (_metrics, time_minutes), pulse, time_minutes, TempoSection::Ramp, AudioTime, true, true);
1116 MeterSection* new_meter = new MeterSection (pulse, time_minutes, beat, where, meter.divisions_per_bar(), meter.note_divisor(), pls, _frame_rate);
1117 bool solved = false;
1119 do_insert (new_meter);
1123 if (pls == AudioTime) {
1124 solved = solve_map_minute (_metrics, new_meter, time_minutes);
1126 solved = solve_map_bbt (_metrics, new_meter, where);
1127 /* required due to resetting the pulse of meter-locked tempi above.
1128 Arguably solve_map_bbt() should use solve_map_pulse (_metrics, TempoSection) instead,
1129 but afaict this cannot cause the map to be left unsolved (these tempi are all audio locked).
1131 recompute_map (_metrics);
1135 if (!solved && recompute) {
1136 /* if this has failed to solve, there is little we can do other than to ensure that
1137 the new map is recalculated.
1139 warning << "Adding meter may have left the tempo map unsolved." << endmsg;
1140 recompute_map (_metrics);
1147 TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
1149 Tempo newtempo (beats_per_minute, note_type);
1152 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1153 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
1158 Glib::Threads::RWLock::WriterLock lm (lock);
1159 *((Tempo*) t) = newtempo;
1160 recompute_map (_metrics);
1162 PropertyChanged (PropertyChange ());
1169 TempoMap::change_existing_tempo_at (framepos_t where, double beats_per_minute, double note_type)
1171 Tempo newtempo (beats_per_minute, note_type);
1174 TempoSection* first;
1175 Metrics::iterator i;
1177 /* find the TempoSection immediately preceding "where"
1180 for (first = 0, i = _metrics.begin(), prev = 0; i != _metrics.end(); ++i) {
1182 if ((*i)->frame() > where) {
1188 if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
1201 error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
1211 Glib::Threads::RWLock::WriterLock lm (lock);
1212 /* cannot move the first tempo section */
1213 *((Tempo*)prev) = newtempo;
1214 recompute_map (_metrics);
1217 PropertyChanged (PropertyChange ());
1221 TempoMap::first_meter () const
1223 const MeterSection *m = 0;
1225 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1226 if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
1231 fatal << _("programming error: no meter section in tempo map!") << endmsg;
1232 abort(); /*NOTREACHED*/
1237 TempoMap::first_meter ()
1239 MeterSection *m = 0;
1241 /* CALLER MUST HOLD LOCK */
1243 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1244 if ((m = dynamic_cast<MeterSection *> (*i)) != 0) {
1249 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1250 abort(); /*NOTREACHED*/
1255 TempoMap::first_tempo () const
1257 const TempoSection *t = 0;
1259 /* CALLER MUST HOLD LOCK */
1261 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1262 if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
1266 if (!t->movable()) {
1272 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1273 abort(); /*NOTREACHED*/
1278 TempoMap::first_tempo ()
1280 TempoSection *t = 0;
1282 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1283 if ((t = dynamic_cast<TempoSection *> (*i)) != 0) {
1287 if (!t->movable()) {
1293 fatal << _("programming error: no tempo section in tempo map!") << endmsg;
1294 abort(); /*NOTREACHED*/
1298 TempoMap::recompute_tempi (Metrics& metrics)
1300 TempoSection* prev_t = 0;
1302 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1305 if ((*i)->is_tempo()) {
1306 t = static_cast<TempoSection*> (*i);
1310 if (!t->movable()) {
1318 if (t->position_lock_style() == AudioTime) {
1319 prev_t->set_c_func (prev_t->compute_c_func_minute (t->beats_per_minute(), t->minute()));
1320 if (!t->locked_to_meter()) {
1321 t->set_pulse (prev_t->pulse_at_tempo (t->beats_per_minute(), t->minute()));
1325 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->beats_per_minute(), t->pulse()));
1326 t->set_minute (prev_t->minute_at_tempo (t->beats_per_minute(), t->pulse()));
1333 prev_t->set_c_func (0.0);
1336 /* tempos must be positioned correctly.
1337 the current approach is to use a meter's bbt time as its base position unit.
1338 an audio-locked meter requires a recomputation of pulse and beat (but not bbt),
1339 while a music-locked meter requires recomputations of frame pulse and beat (but not bbt)
1342 TempoMap::recompute_meters (Metrics& metrics)
1344 MeterSection* meter = 0;
1345 MeterSection* prev_m = 0;
1347 for (Metrics::const_iterator mi = metrics.begin(); mi != metrics.end(); ++mi) {
1348 if (!(*mi)->is_tempo()) {
1349 meter = static_cast<MeterSection*> (*mi);
1350 if (meter->position_lock_style() == AudioTime) {
1352 pair<double, BBT_Time> b_bbt;
1353 TempoSection* meter_locked_tempo = 0;
1354 for (Metrics::const_iterator ii = metrics.begin(); ii != metrics.end(); ++ii) {
1356 if ((*ii)->is_tempo()) {
1357 t = static_cast<TempoSection*> (*ii);
1358 if ((t->locked_to_meter() || !t->movable()) && t->frame() == meter->frame()) {
1359 meter_locked_tempo = t;
1366 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1367 if (beats + prev_m->beat() != meter->beat()) {
1368 /* reordering caused a bbt change */
1369 b_bbt = make_pair (beats + prev_m->beat()
1370 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1371 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1373 } else if (meter->movable()) {
1374 b_bbt = make_pair (meter->beat(), meter->bbt());
1375 pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
1378 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
1380 if (meter_locked_tempo) {
1381 meter_locked_tempo->set_pulse (pulse);
1383 meter->set_beat (b_bbt);
1384 meter->set_pulse (pulse);
1389 pair<double, BBT_Time> b_bbt;
1391 const double beats = (meter->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
1392 if (beats + prev_m->beat() != meter->beat()) {
1393 /* reordering caused a bbt change */
1394 b_bbt = make_pair (beats + prev_m->beat()
1395 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
1397 b_bbt = make_pair (beats + prev_m->beat(), meter->bbt());
1399 pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
1401 /* shouldn't happen - the first is audio-locked */
1402 pulse = pulse_at_beat_locked (metrics, meter->beat());
1403 b_bbt = make_pair (meter->beat(), meter->bbt());
1406 meter->set_beat (b_bbt);
1407 meter->set_pulse (pulse);
1408 meter->set_minute (minute_at_pulse_locked (metrics, pulse));
1417 TempoMap::recompute_map (Metrics& metrics, framepos_t end)
1419 /* CALLER MUST HOLD WRITE LOCK */
1421 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("recomputing tempo map, zero to %1\n", end));
1424 /* silly call from Session::process() during startup
1429 recompute_tempi (metrics);
1430 recompute_meters (metrics);
1434 TempoMap::metric_at (framepos_t frame, Metrics::const_iterator* last) const
1436 Glib::Threads::RWLock::ReaderLock lm (lock);
1437 TempoMetric m (first_meter(), first_tempo());
1439 /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
1440 at something, because we insert the default tempo and meter during
1441 TempoMap construction.
1443 now see if we can find better candidates.
1446 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
1448 if ((*i)->frame() > frame) {
1462 /* XX meters only */
1464 TempoMap::metric_at (BBT_Time bbt) 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)->is_tempo()) {
1479 mw = static_cast<MeterSection*> (*i);
1480 BBT_Time section_start (mw->bbt());
1482 if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
1493 /** Returns the BBT (meter-based) beat corresponding to the supplied frame, possibly returning a negative value.
1494 * @param frame The session frame position.
1495 * @return The beat duration according to the tempo map at the supplied frame.
1497 * If the supplied frame lies before the first meter, the returned beat duration will be negative.
1498 * The returned beat is obtained using the first meter and the continuation of the tempo curve (backwards).
1500 * This function uses both tempo and meter.
1503 TempoMap::beat_at_frame (const framecnt_t& frame) const
1505 Glib::Threads::RWLock::ReaderLock lm (lock);
1507 return beat_at_minute_locked (_metrics, minute_at_frame (frame));
1510 /* This function uses both tempo and meter.*/
1512 TempoMap::beat_at_minute_locked (const Metrics& metrics, const double& minute) const
1514 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
1515 MeterSection* prev_m = 0;
1516 MeterSection* next_m = 0;
1518 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1519 if (!(*i)->is_tempo()) {
1520 if (prev_m && (*i)->minute() > minute) {
1521 next_m = static_cast<MeterSection*> (*i);
1524 prev_m = static_cast<MeterSection*> (*i);
1528 const double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
1530 /* audio locked meters fake their beat */
1531 if (next_m && next_m->beat() < beat) {
1532 return next_m->beat();
1538 /** Returns the frame corresponding to the supplied BBT (meter-based) beat.
1539 * @param beat The BBT (meter-based) beat.
1540 * @return The frame duration according to the tempo map at the supplied BBT (meter-based) beat.
1542 * This function uses both tempo and meter.
1545 TempoMap::frame_at_beat (const double& beat) const
1547 Glib::Threads::RWLock::ReaderLock lm (lock);
1549 return frame_at_minute (minute_at_beat_locked (_metrics, beat));
1552 /* meter & tempo section based */
1554 TempoMap::minute_at_beat_locked (const Metrics& metrics, const double& beat) const
1556 MeterSection* prev_m = 0;
1557 TempoSection* prev_t = 0;
1561 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1562 if (!(*i)->is_tempo()) {
1563 m = static_cast<MeterSection*> (*i);
1564 if (prev_m && m->beat() > beat) {
1573 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1574 if ((*i)->is_tempo()) {
1575 t = static_cast<TempoSection*> (*i);
1576 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
1584 return prev_t->minute_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse());
1587 /** Returns a Tempo corresponding to the supplied frame.
1588 * @param frame The audio frame.
1589 * @return a Tempo according to the tempo map at the supplied frame.
1593 TempoMap::tempo_at_frame (const framepos_t& frame) const
1595 Glib::Threads::RWLock::ReaderLock lm (lock);
1597 return tempo_at_minute_locked (_metrics, minute_at_frame (frame));
1601 TempoMap::tempo_at_minute_locked (const Metrics& metrics, const double& minute) const
1603 TempoSection* prev_t = 0;
1607 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1608 if ((*i)->is_tempo()) {
1609 t = static_cast<TempoSection*> (*i);
1613 if ((prev_t) && t->minute() > minute) {
1614 /* t is the section past frame */
1615 const double ret_bpm = prev_t->tempo_at_minute (minute);
1616 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
1623 const double ret = prev_t->beats_per_minute();
1624 const Tempo ret_tempo (ret, prev_t->note_type ());
1629 /** returns the frame at which the supplied tempo occurs, or
1630 * the frame of the last tempo section (search exhausted)
1631 * only the position of the first occurence will be returned
1635 TempoMap::frame_at_tempo (const Tempo& tempo) const
1637 Glib::Threads::RWLock::ReaderLock lm (lock);
1639 return frame_at_minute (minute_at_tempo_locked (_metrics, tempo));
1643 TempoMap::minute_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1645 TempoSection* prev_t = 0;
1646 const double tempo_bpm = tempo.beats_per_minute();
1648 Metrics::const_iterator i;
1650 for (i = metrics.begin(); i != metrics.end(); ++i) {
1652 if ((*i)->is_tempo()) {
1653 t = static_cast<TempoSection*> (*i);
1659 const double t_bpm = t->beats_per_minute();
1661 if (t_bpm == tempo_bpm) {
1666 const double prev_t_bpm = prev_t->beats_per_minute();
1668 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1669 return prev_t->minute_at_tempo (tempo_bpm, prev_t->pulse());
1676 return prev_t->minute();
1680 TempoMap::tempo_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1682 TempoSection* prev_t = 0;
1686 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1687 if ((*i)->is_tempo()) {
1688 t = static_cast<TempoSection*> (*i);
1692 if ((prev_t) && t->pulse() > pulse) {
1693 /* t is the section past frame */
1694 const double ret_bpm = prev_t->tempo_at_pulse (pulse);
1695 const Tempo ret_tempo (ret_bpm, prev_t->note_type());
1702 const double ret = prev_t->beats_per_minute();
1703 const Tempo ret_tempo (ret, prev_t->note_type ());
1709 TempoMap::pulse_at_tempo_locked (const Metrics& metrics, const Tempo& tempo) const
1711 TempoSection* prev_t = 0;
1712 const double tempo_bpm = tempo.beats_per_minute();
1714 Metrics::const_iterator i;
1716 for (i = metrics.begin(); i != metrics.end(); ++i) {
1718 if ((*i)->is_tempo()) {
1719 t = static_cast<TempoSection*> (*i);
1725 const double t_bpm = t->beats_per_minute();
1727 if (t_bpm == tempo_bpm) {
1732 const double prev_t_bpm = prev_t->beats_per_minute();
1734 if ((t_bpm > tempo_bpm && prev_t_bpm < tempo_bpm) || (t_bpm < tempo_bpm && prev_t_bpm > tempo_bpm)) {
1735 return prev_t->pulse_at_tempo (tempo_bpm, prev_t->minute());
1742 return prev_t->minute();
1745 /** Returns a Tempo corresponding to the supplied BBT (meter-based) beat.
1746 * @param beat The BBT (meter-based) beat.
1749 TempoMap::tempo_at_beat (const double& beat) const
1751 Glib::Threads::RWLock::ReaderLock lm (lock);
1752 const MeterSection* prev_m = &meter_section_at_beat_locked (_metrics, beat);
1753 const TempoSection* prev_t = &tempo_section_at_beat_locked (_metrics, beat);
1755 return Tempo (prev_t->tempo_at_pulse (((beat - prev_m->beat()) / prev_m->note_divisor()) + prev_m->pulse()), prev_t->note_type());
1758 /** Returns a BBT (meter-based) beat corresponding to the supplied Tempo.
1759 * @param tempo The tempo.
1762 TempoMap::beat_at_tempo (const Tempo& tempo) const
1764 Glib::Threads::RWLock::ReaderLock lm (lock);
1765 const double pulse = pulse_at_tempo_locked (_metrics, tempo);
1767 return beat_at_pulse_locked (_metrics, pulse);
1769 /** Returns the whole-note pulse corresponding to the supplied BBT (meter-based) beat.
1770 * @param beat The BBT (meter-based) beat.
1772 * a pulse or whole note is the base musical position of a MetricSection.
1773 * it is equivalent to four quarter notes.
1777 TempoMap::pulse_at_beat (const double& beat) const
1779 Glib::Threads::RWLock::ReaderLock lm (lock);
1780 return pulse_at_beat_locked (_metrics, beat);
1784 TempoMap::pulse_at_beat_locked (const Metrics& metrics, const double& beat) const
1786 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
1788 return prev_m->pulse() + ((beat - prev_m->beat()) / prev_m->note_divisor());
1791 /** Returns the BBT (meter-based) beat corresponding to the supplied whole-note pulse .
1792 * @param pulse the whole-note pulse.
1794 * a pulse or whole note is the base musical position of a MetricSection.
1795 * it is equivalent to four quarter notes.
1798 TempoMap::beat_at_pulse (const double& pulse) const
1800 Glib::Threads::RWLock::ReaderLock lm (lock);
1801 return beat_at_pulse_locked (_metrics, pulse);
1805 TempoMap::beat_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1807 MeterSection* prev_m = 0;
1809 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1811 if (!(*i)->is_tempo()) {
1812 m = static_cast<MeterSection*> (*i);
1813 if (prev_m && m->pulse() > pulse) {
1820 double const ret = ((pulse - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat();
1825 TempoMap::pulse_at_frame (const framepos_t& frame) const
1827 Glib::Threads::RWLock::ReaderLock lm (lock);
1828 return pulse_at_minute_locked (_metrics, minute_at_frame (frame));
1831 /* tempo section based */
1833 TempoMap::pulse_at_minute_locked (const Metrics& metrics, const double& minute) const
1835 /* HOLD (at least) THE READER LOCK */
1836 TempoSection* prev_t = 0;
1838 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1840 if ((*i)->is_tempo()) {
1841 t = static_cast<TempoSection*> (*i);
1845 if (prev_t && t->minute() > minute) {
1846 /*the previous ts is the one containing the frame */
1847 const double ret = prev_t->pulse_at_minute (minute);
1848 /* audio locked section in new meter*/
1849 if (t->pulse() < ret) {
1858 /* treated as constant for this ts */
1859 const double pulses_in_section = ((minute - prev_t->minute()) * prev_t->beats_per_minute()) / prev_t->note_type();
1861 return pulses_in_section + prev_t->pulse();
1865 TempoMap::frame_at_pulse (const double& pulse) const
1867 Glib::Threads::RWLock::ReaderLock lm (lock);
1869 return frame_at_minute (minute_at_pulse_locked (_metrics, pulse));
1872 /* tempo section based */
1874 TempoMap::minute_at_pulse_locked (const Metrics& metrics, const double& pulse) const
1876 /* HOLD THE READER LOCK */
1878 const TempoSection* prev_t = 0;
1880 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1883 if ((*i)->is_tempo()) {
1884 t = static_cast<TempoSection*> (*i);
1888 if (prev_t && t->pulse() > pulse) {
1889 return prev_t->minute_at_pulse (pulse);
1895 /* must be treated as constant, irrespective of _type */
1896 double const dtime = ((pulse - prev_t->pulse()) * prev_t->note_type()) / prev_t->beats_per_minute();
1898 return dtime + prev_t->minute();
1901 /** Returns the BBT (meter-based) beat corresponding to the supplied BBT time.
1902 * @param bbt The BBT time (meter-based).
1906 TempoMap::beat_at_bbt (const Timecode::BBT_Time& bbt)
1908 Glib::Threads::RWLock::ReaderLock lm (lock);
1909 return beat_at_bbt_locked (_metrics, bbt);
1914 TempoMap::beat_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
1916 /* CALLER HOLDS READ LOCK */
1918 MeterSection* prev_m = 0;
1920 /* because audio-locked meters have 'fake' integral beats,
1921 there is no pulse offset here.
1925 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1926 if (!(*i)->is_tempo()) {
1927 m = static_cast<MeterSection*> (*i);
1929 const double bars_to_m = (m->beat() - prev_m->beat()) / prev_m->divisions_per_bar();
1930 if ((bars_to_m + (prev_m->bbt().bars - 1)) > (bbt.bars - 1)) {
1938 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
1939 const double remaining_bars_in_beats = remaining_bars * prev_m->divisions_per_bar();
1940 const double ret = remaining_bars_in_beats + prev_m->beat() + (bbt.beats - 1) + (bbt.ticks / BBT_Time::ticks_per_beat);
1945 /** Returns the BBT time corresponding to the supplied BBT (meter-based) beat.
1946 * @param beat The BBT (meter-based) beat.
1950 TempoMap::bbt_at_beat (const double& beat)
1952 Glib::Threads::RWLock::ReaderLock lm (lock);
1953 return bbt_at_beat_locked (_metrics, beat);
1957 TempoMap::bbt_at_beat_locked (const Metrics& metrics, const double& b) const
1959 /* CALLER HOLDS READ LOCK */
1960 MeterSection* prev_m = 0;
1961 const double beats = max (0.0, b);
1963 MeterSection* m = 0;
1965 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
1966 if (!(*i)->is_tempo()) {
1967 m = static_cast<MeterSection*> (*i);
1969 if (m->beat() > beats) {
1970 /* this is the meter after the one our beat is on*/
1979 const double beats_in_ms = beats - prev_m->beat();
1980 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
1981 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
1982 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
1983 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
1987 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
1988 ret.beats = (uint32_t) floor (remaining_beats);
1989 ret.bars = total_bars;
1991 /* 0 0 0 to 1 1 0 - based mapping*/
1995 if (ret.ticks >= BBT_Time::ticks_per_beat) {
1997 ret.ticks -= BBT_Time::ticks_per_beat;
2000 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2008 /** Returns the whole-note pulse corresponding to the supplied BBT time (meter-based).
2009 * @param bbt The BBT time (meter-based).
2011 * a pulse or whole note is the basic musical position of a MetricSection.
2012 * it is equivalent to four quarter notes.
2013 * while the input uses meter, the output does not.
2016 TempoMap::pulse_at_bbt (const Timecode::BBT_Time& bbt)
2018 Glib::Threads::RWLock::ReaderLock lm (lock);
2020 return pulse_at_bbt_locked (_metrics, bbt);
2024 TempoMap::pulse_at_bbt_rt (const Timecode::BBT_Time& bbt)
2026 Glib::Threads::RWLock::ReaderLock lm (lock);
2029 throw std::logic_error ("TempoMap::pulse_at_bbt_rt() could not lock tempo map");
2032 return pulse_at_bbt_locked (_metrics, bbt);
2036 TempoMap::pulse_at_bbt_locked (const Metrics& metrics, const Timecode::BBT_Time& bbt) const
2038 /* CALLER HOLDS READ LOCK */
2040 MeterSection* prev_m = 0;
2042 /* because audio-locked meters have 'fake' integral beats,
2043 there is no pulse offset here.
2047 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2048 if (!(*i)->is_tempo()) {
2049 m = static_cast<MeterSection*> (*i);
2051 if (m->bbt().bars > bbt.bars) {
2059 const double remaining_bars = bbt.bars - prev_m->bbt().bars;
2060 const double remaining_pulses = remaining_bars * prev_m->divisions_per_bar() / prev_m->note_divisor();
2061 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 (meter-based) corresponding to the supplied whole-note pulse.
2066 * @param pulse The whole-note pulse.
2068 * a pulse or whole note is the basic musical position of a MetricSection.
2069 * it is equivalent to four quarter notes.
2070 * while the input uses meter, the output does not.
2073 TempoMap::bbt_at_pulse (const double& pulse)
2075 Glib::Threads::RWLock::ReaderLock lm (lock);
2077 return bbt_at_pulse_locked (_metrics, pulse);
2081 TempoMap::bbt_at_pulse_locked (const Metrics& metrics, const double& pulse) const
2083 MeterSection* prev_m = 0;
2085 MeterSection* m = 0;
2087 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2089 if (!(*i)->is_tempo()) {
2090 m = static_cast<MeterSection*> (*i);
2093 double const pulses_to_m = m->pulse() - prev_m->pulse();
2094 if (prev_m->pulse() + pulses_to_m > pulse) {
2095 /* this is the meter after the one our beat is on*/
2104 const double beats_in_ms = (pulse - prev_m->pulse()) * prev_m->note_divisor();
2105 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2106 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2107 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2108 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2112 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2113 ret.beats = (uint32_t) floor (remaining_beats);
2114 ret.bars = total_bars;
2116 /* 0 0 0 to 1 1 0 mapping*/
2120 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2122 ret.ticks -= BBT_Time::ticks_per_beat;
2125 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2133 /** Returns the BBT time corresponding to the supplied frame position.
2134 * @param frame the position in audio samples.
2138 TempoMap::bbt_at_frame (framepos_t frame)
2145 warning << string_compose (_("tempo map asked for BBT time at frame %1\n"), frame) << endmsg;
2148 Glib::Threads::RWLock::ReaderLock lm (lock);
2150 return bbt_at_minute_locked (_metrics, minute_at_frame (frame));
2154 TempoMap::bbt_at_frame_rt (framepos_t frame)
2156 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2159 throw std::logic_error ("TempoMap::bbt_at_frame_rt() could not lock tempo map");
2162 return bbt_at_minute_locked (_metrics, minute_at_frame (frame));
2166 TempoMap::bbt_at_minute_locked (const Metrics& metrics, const double& minute) const
2176 const TempoSection& ts = tempo_section_at_minute_locked (metrics, minute);
2177 MeterSection* prev_m = 0;
2178 MeterSection* next_m = 0;
2182 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2183 if (!(*i)->is_tempo()) {
2184 m = static_cast<MeterSection*> (*i);
2185 if (prev_m && m->minute() > minute) {
2193 double beat = prev_m->beat() + (ts.pulse_at_minute (minute) - prev_m->pulse()) * prev_m->note_divisor();
2195 /* handle frame before first meter */
2196 if (minute < prev_m->minute()) {
2199 /* audio locked meters fake their beat */
2200 if (next_m && next_m->beat() < beat) {
2201 beat = next_m->beat();
2204 beat = max (0.0, beat);
2206 const double beats_in_ms = beat - prev_m->beat();
2207 const uint32_t bars_in_ms = (uint32_t) floor (beats_in_ms / prev_m->divisions_per_bar());
2208 const uint32_t total_bars = bars_in_ms + (prev_m->bbt().bars - 1);
2209 const double remaining_beats = beats_in_ms - (bars_in_ms * prev_m->divisions_per_bar());
2210 const double remaining_ticks = (remaining_beats - floor (remaining_beats)) * BBT_Time::ticks_per_beat;
2214 ret.ticks = (uint32_t) floor (remaining_ticks + 0.5);
2215 ret.beats = (uint32_t) floor (remaining_beats);
2216 ret.bars = total_bars;
2218 /* 0 0 0 to 1 1 0 - based mapping*/
2222 if (ret.ticks >= BBT_Time::ticks_per_beat) {
2224 ret.ticks -= BBT_Time::ticks_per_beat;
2227 if (ret.beats >= prev_m->divisions_per_bar() + 1) {
2235 /** Returns the frame corresponding to the supplied BBT time.
2236 * @param bbt the position in BBT time.
2240 TempoMap::frame_at_bbt (const BBT_Time& bbt)
2243 warning << string_compose (_("tempo map asked for frame time at bar < 1 (%1)\n"), bbt) << endmsg;
2247 if (bbt.beats < 1) {
2248 throw std::logic_error ("beats are counted from one");
2250 Glib::Threads::RWLock::ReaderLock lm (lock);
2252 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
2255 /* meter & tempo section based */
2257 TempoMap::minute_at_bbt_locked (const Metrics& metrics, const BBT_Time& bbt) const
2259 /* HOLD THE READER LOCK */
2261 const double ret = minute_at_beat_locked (metrics, beat_at_bbt_locked (metrics, bbt));
2266 * Returns the quarter-note beat position corresponding to the supplied frame.
2268 * @param frame The distance in frames relative to session 0 whose quarter note distance you would like.
2269 * @return The quarter-note position of the supplied frame. Ignores meter.
2271 * Plugin APIs don't count ticks in the same way PROGRAM_NAME does.
2272 * We use ticks per beat whereas the rest of the world uses ticks per quarter note.
2273 * This is more or less the VST's ppqPos (a scalar you use to obtain tick position
2274 * in whatever ppqn you're using).
2278 TempoMap::quarter_note_at_frame (const framepos_t frame)
2280 Glib::Threads::RWLock::ReaderLock lm (lock);
2282 const double ret = quarter_note_at_minute_locked (_metrics, minute_at_frame (frame));
2288 TempoMap::quarter_note_at_minute_locked (const Metrics& metrics, const double minute) const
2290 const double ret = pulse_at_minute_locked (metrics, minute) * 4.0;
2296 TempoMap::quarter_note_at_frame_rt (const framepos_t frame)
2298 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
2301 throw std::logic_error ("TempoMap::quarter_note_at_frame_rt() could not lock tempo map");
2304 const double ret = pulse_at_minute_locked (_metrics, minute_at_frame (frame)) * 4.0;
2310 * Returns the frame corresponding to the supplied quarter-note beat position.
2312 * @param quarter_note The quarter-note relative to session 0 whose frame position you would like.
2313 * @return The frame position of the supplied quarter-note. Ignores meter.
2318 TempoMap::frame_at_quarter_note (const double quarter_note)
2320 Glib::Threads::RWLock::ReaderLock lm (lock);
2322 const framepos_t ret = frame_at_minute (minute_at_quarter_note_locked (_metrics, quarter_note));
2328 TempoMap::minute_at_quarter_note_locked (const Metrics& metrics, const double quarter_note) const
2330 const double ret = minute_at_pulse_locked (metrics, quarter_note / 4.0);
2335 /** Returns the quarter-note beats corresponding to the supplied BBT (meter-based) beat.
2336 * @param beat The BBT (meter-based) beat.
2338 * a quarter-note is the musical unit of Evoral::Beats.
2342 TempoMap::quarter_note_at_beat (const double beat)
2344 Glib::Threads::RWLock::ReaderLock lm (lock);
2346 const double ret = quarter_note_at_beat_locked (_metrics, beat);
2352 TempoMap::quarter_note_at_beat_locked (const Metrics& metrics, const double beat) const
2354 const double ret = pulse_at_beat_locked (metrics, beat) * 4.0;
2359 /** Returns the BBT (meter-based) beat position corresponding to the supplied quarter-note beats.
2360 * @param quarter_note The position in quarter-note beats.
2362 * a quarter-note is the musical unit of Evoral::Beats.
2366 TempoMap::beat_at_quarter_note (const double quarter_note)
2368 Glib::Threads::RWLock::ReaderLock lm (lock);
2370 const double ret = beat_at_quarter_note_locked (_metrics, quarter_note);
2376 TempoMap::beat_at_quarter_note_locked (const Metrics& metrics, const double quarter_note) const
2379 return beat_at_pulse_locked (metrics, quarter_note / 4.0);
2382 /** Returns the distance in frames between two supplied quarter-note beats.
2383 * @param start the first position in quarter-note beats.
2384 * @param end the end position in quarter-note beats.
2386 * use this rather than e.g.
2387 * frame_at-quarter_note (end_beats) - frame_at_quarter_note (start_beats).
2388 * frames_between_quarter_notes() doesn't round to audio frames as an intermediate step,
2392 TempoMap::frames_between_quarter_notes (const double start, const double end)
2394 Glib::Threads::RWLock::ReaderLock lm (lock);
2396 return frame_at_minute (minutes_between_quarter_notes_locked (_metrics, start, end));
2400 TempoMap::minutes_between_quarter_notes_locked (const Metrics& metrics, const double start, const double end)
2403 return minute_at_pulse_locked (metrics, end / 4.0) - minute_at_pulse_locked (metrics, start / 4.0);
2407 TempoMap::check_solved (const Metrics& metrics) const
2409 TempoSection* prev_t = 0;
2410 MeterSection* prev_m = 0;
2412 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2415 if ((*i)->is_tempo()) {
2416 t = static_cast<TempoSection*> (*i);
2421 /* check ordering */
2422 if ((t->minute() <= prev_t->minute()) || (t->pulse() <= prev_t->pulse())) {
2426 /* precision check ensures tempo and frames align.*/
2427 if (t->frame() != frame_at_minute (prev_t->minute_at_tempo (t->beats_per_minute(), t->pulse()))) {
2428 if (!t->locked_to_meter()) {
2433 /* gradient limit - who knows what it should be?
2434 things are also ok (if a little chaotic) without this
2436 if (fabs (prev_t->c_func()) > 1000.0) {
2437 //std::cout << "c : " << prev_t->c_func() << std::endl;
2444 if (!(*i)->is_tempo()) {
2445 m = static_cast<MeterSection*> (*i);
2446 if (prev_m && m->position_lock_style() == AudioTime) {
2447 const TempoSection* t = &tempo_section_at_minute_locked (metrics, minute_at_frame (m->frame() - 1));
2448 const double nascent_m_minute = t->minute_at_pulse (m->pulse());
2449 /* Here we check that a preceding section of music doesn't overlap a subsequent one.
2451 if (t && (nascent_m_minute > m->minute() || nascent_m_minute < 0.0)) {
2465 TempoMap::set_active_tempos (const Metrics& metrics, const framepos_t& frame)
2467 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2469 if ((*i)->is_tempo()) {
2470 t = static_cast<TempoSection*> (*i);
2471 if (!t->movable()) {
2472 t->set_active (true);
2475 if (t->movable() && t->active () && t->position_lock_style() == AudioTime && t->frame() < frame) {
2476 t->set_active (false);
2478 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() > frame) {
2479 t->set_active (true);
2480 } else if (t->movable() && t->position_lock_style() == AudioTime && t->frame() == frame) {
2489 TempoMap::solve_map_minute (Metrics& imaginary, TempoSection* section, const double& minute)
2491 TempoSection* prev_t = 0;
2492 TempoSection* section_prev = 0;
2493 double first_m_minute = 0.0;
2495 /* can't move a tempo before the first meter */
2496 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2498 if (!(*i)->is_tempo()) {
2499 m = static_cast<MeterSection*> (*i);
2500 if (!m->movable()) {
2501 first_m_minute = m->minute();
2506 if (section->movable() && minute <= first_m_minute) {
2510 section->set_active (true);
2511 section->set_minute (minute);
2513 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2515 if ((*i)->is_tempo()) {
2516 t = static_cast<TempoSection*> (*i);
2523 section_prev = prev_t;
2524 if (t->locked_to_meter()) {
2529 if (t->position_lock_style() == MusicTime) {
2530 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->beats_per_minute(), t->pulse()));
2531 t->set_minute (prev_t->minute_at_tempo (t->beats_per_minute(), t->pulse()));
2533 prev_t->set_c_func (prev_t->compute_c_func_minute (t->beats_per_minute(), t->minute()));
2534 if (!t->locked_to_meter()) {
2535 t->set_pulse (prev_t->pulse_at_tempo (t->beats_per_minute(), t->minute()));
2544 section_prev->set_c_func (section_prev->compute_c_func_minute (section->beats_per_minute(), minute));
2545 if (!section->locked_to_meter()) {
2546 section->set_pulse (section_prev->pulse_at_tempo (section->beats_per_minute(), minute));
2551 recompute_tempi (imaginary);
2553 if (check_solved (imaginary)) {
2556 dunp (imaginary, std::cout);
2560 MetricSectionFrameSorter fcmp;
2561 imaginary.sort (fcmp);
2563 recompute_tempi (imaginary);
2565 if (check_solved (imaginary)) {
2573 TempoMap::solve_map_pulse (Metrics& imaginary, TempoSection* section, const double& pulse)
2575 TempoSection* prev_t = 0;
2576 TempoSection* section_prev = 0;
2578 section->set_pulse (pulse);
2580 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2582 if ((*i)->is_tempo()) {
2583 t = static_cast<TempoSection*> (*i);
2587 if (!t->movable()) {
2594 section_prev = prev_t;
2597 if (t->position_lock_style() == MusicTime) {
2598 prev_t->set_c_func (prev_t->compute_c_func_pulse (t->beats_per_minute(), t->pulse()));
2599 t->set_minute (prev_t->minute_at_tempo (t->beats_per_minute(), t->pulse()));
2601 prev_t->set_c_func (prev_t->compute_c_func_minute (t->beats_per_minute(), t->minute()));
2602 if (!t->locked_to_meter()) {
2603 t->set_pulse (prev_t->pulse_at_tempo (t->beats_per_minute(), t->minute()));
2612 section_prev->set_c_func (section_prev->compute_c_func_pulse (section->beats_per_minute(), pulse));
2613 section->set_minute (section_prev->minute_at_tempo (section->beats_per_minute(), pulse));
2617 recompute_tempi (imaginary);
2619 if (check_solved (imaginary)) {
2622 dunp (imaginary, std::cout);
2626 MetricSectionSorter cmp;
2627 imaginary.sort (cmp);
2629 recompute_tempi (imaginary);
2631 * XX need a restriction here, but only for this case,
2632 * as audio locked tempos don't interact in the same way.
2634 * With music-locked tempos, the solution to cross-dragging can fly off the screen
2636 * |50 bpm |250 bpm |60 bpm
2637 * drag 250 to the pulse after 60->
2638 * a clue: dragging the second 60 <- past the 250 would cause no such problem.
2640 if (check_solved (imaginary)) {
2648 TempoMap::solve_map_minute (Metrics& imaginary, MeterSection* section, const double& minute)
2650 /* disallow moving first meter past any subsequent one, and any movable meter before the first one */
2651 const MeterSection* other = &meter_section_at_minute_locked (imaginary, minute);
2652 if ((!section->movable() && other->movable()) || (!other->movable() && section->movable() && other->minute() >= minute)) {
2656 if (!section->movable()) {
2657 /* lock the first tempo to our first meter */
2658 if (!set_active_tempos (imaginary, section->frame_at_minute (minute))) {
2663 TempoSection* meter_locked_tempo = 0;
2665 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2667 if ((*ii)->is_tempo()) {
2668 t = static_cast<TempoSection*> (*ii);
2669 if ((t->locked_to_meter() || !t->movable()) && t->minute() == section->minute()) {
2670 meter_locked_tempo = t;
2676 if (!meter_locked_tempo) {
2680 MeterSection* prev_m = 0;
2682 TempoSection* tempo_copy = copy_metrics_and_point (imaginary, future_map, meter_locked_tempo);
2683 bool solved = false;
2685 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2687 if (!(*i)->is_tempo()) {
2688 m = static_cast<MeterSection*> (*i);
2690 if (prev_m && section->movable()) {
2691 const double beats = (pulse_at_minute_locked (imaginary, minute) - prev_m->pulse()) * prev_m->note_divisor();
2692 if (beats + prev_m->beat() < section->beat()) {
2693 /* set the section pulse according to its musical position,
2694 * as an earlier time than this has been requested.
2696 const double new_pulse = ((section->beat() - prev_m->beat())
2697 / prev_m->note_divisor()) + prev_m->pulse();
2699 tempo_copy->set_position_lock_style (MusicTime);
2700 if ((solved = solve_map_pulse (future_map, tempo_copy, new_pulse))) {
2701 meter_locked_tempo->set_position_lock_style (MusicTime);
2702 section->set_position_lock_style (MusicTime);
2703 section->set_pulse (new_pulse);
2704 solve_map_pulse (imaginary, meter_locked_tempo, new_pulse);
2705 meter_locked_tempo->set_position_lock_style (AudioTime);
2706 section->set_position_lock_style (AudioTime);
2707 section->set_minute (meter_locked_tempo->minute());
2713 Metrics::const_iterator d = future_map.begin();
2714 while (d != future_map.end()) {
2723 /* all is ok. set section's locked tempo if allowed.
2724 possibly disallowed if there is an adjacent audio-locked tempo.
2725 XX this check could possibly go. its never actually happened here.
2727 MeterSection* meter_copy = const_cast<MeterSection*>
2728 (&meter_section_at_minute_locked (future_map, section->minute()));
2730 meter_copy->set_minute (minute);
2732 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2733 section->set_minute (minute);
2734 meter_locked_tempo->set_pulse (((section->beat() - prev_m->beat())
2735 / prev_m->note_divisor()) + prev_m->pulse());
2736 solve_map_minute (imaginary, meter_locked_tempo, minute);
2741 Metrics::const_iterator d = future_map.begin();
2742 while (d != future_map.end()) {
2752 /* not movable (first meter atm) */
2754 tempo_copy->set_minute (minute);
2755 tempo_copy->set_pulse (0.0);
2757 if ((solved = solve_map_minute (future_map, tempo_copy, minute))) {
2758 section->set_minute (minute);
2759 meter_locked_tempo->set_minute (minute);
2760 meter_locked_tempo->set_pulse (0.0);
2761 solve_map_minute (imaginary, meter_locked_tempo, minute);
2766 Metrics::const_iterator d = future_map.begin();
2767 while (d != future_map.end()) {
2776 pair<double, BBT_Time> b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2777 section->set_beat (b_bbt);
2778 section->set_pulse (0.0);
2788 MetricSectionFrameSorter fcmp;
2789 imaginary.sort (fcmp);
2791 recompute_meters (imaginary);
2797 TempoMap::solve_map_bbt (Metrics& imaginary, MeterSection* section, const BBT_Time& when)
2799 /* disallow setting section to an existing meter's bbt */
2800 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2802 if (!(*i)->is_tempo()) {
2803 m = static_cast<MeterSection*> (*i);
2804 if (m != section && m->bbt().bars == when.bars) {
2810 MeterSection* prev_m = 0;
2811 MeterSection* section_prev = 0;
2813 for (Metrics::iterator i = imaginary.begin(); i != imaginary.end(); ++i) {
2815 if (!(*i)->is_tempo()) {
2816 m = static_cast<MeterSection*> (*i);
2817 pair<double, BBT_Time> b_bbt;
2818 double new_pulse = 0.0;
2820 if (prev_m && m->bbt().bars > when.bars && !section_prev){
2821 section_prev = prev_m;
2822 const double beats = (when.bars - section_prev->bbt().bars) * section_prev->divisions_per_bar();
2823 const double pulse = (beats / section_prev->note_divisor()) + section_prev->pulse();
2824 pair<double, BBT_Time> b_bbt = make_pair (beats + section_prev->beat(), when);
2826 section->set_beat (b_bbt);
2827 section->set_pulse (pulse);
2828 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
2833 if (m->position_lock_style() == AudioTime) {
2834 TempoSection* meter_locked_tempo = 0;
2836 for (Metrics::const_iterator ii = imaginary.begin(); ii != imaginary.end(); ++ii) {
2838 if ((*ii)->is_tempo()) {
2839 t = static_cast<TempoSection*> (*ii);
2840 if ((t->locked_to_meter() || !t->movable()) && t->frame() == m->frame()) {
2841 meter_locked_tempo = t;
2847 if (!meter_locked_tempo) {
2852 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2854 if (beats + prev_m->beat() != m->beat()) {
2855 /* tempo/ meter change caused a change in beat (bar). */
2856 b_bbt = make_pair (beats + prev_m->beat()
2857 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2858 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2859 } else if (m->movable()) {
2860 b_bbt = make_pair (m->beat(), m->bbt());
2861 new_pulse = prev_m->pulse() + (beats / prev_m->note_divisor());
2864 b_bbt = make_pair (0.0, BBT_Time (1, 1, 0));
2867 meter_locked_tempo->set_pulse (new_pulse);
2868 m->set_beat (b_bbt);
2869 m->set_pulse (new_pulse);
2873 const double beats = ((m->bbt().bars - prev_m->bbt().bars) * prev_m->divisions_per_bar());
2874 if (beats + prev_m->beat() != m->beat()) {
2875 /* tempo/ meter change caused a change in beat (bar). */
2876 b_bbt = make_pair (beats + prev_m->beat()
2877 , BBT_Time ((beats / prev_m->divisions_per_bar()) + prev_m->bbt().bars, 1, 0));
2879 b_bbt = make_pair (beats + prev_m->beat()
2882 new_pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2883 m->set_beat (b_bbt);
2884 m->set_pulse (new_pulse);
2885 m->set_minute (minute_at_pulse_locked (imaginary, new_pulse));
2892 if (!section_prev) {
2894 const double beats = (when.bars - prev_m->bbt().bars) * prev_m->divisions_per_bar();
2895 const double pulse = (beats / prev_m->note_divisor()) + prev_m->pulse();
2896 pair<double, BBT_Time> b_bbt = make_pair (beats + prev_m->beat(), when);
2898 section->set_beat (b_bbt);
2899 section->set_pulse (pulse);
2900 section->set_minute (minute_at_pulse_locked (imaginary, pulse));
2903 MetricSectionSorter cmp;
2904 imaginary.sort (cmp);
2906 recompute_meters (imaginary);
2911 /** places a copy of _metrics into copy and returns a pointer
2912 * to section's equivalent in copy.
2915 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, TempoSection* section)
2917 TempoSection* ret = 0;
2919 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2922 if ((*i)->is_tempo()) {
2923 t = static_cast<TempoSection*> (*i);
2925 ret = new TempoSection (*t);
2926 copy.push_back (ret);
2930 TempoSection* cp = new TempoSection (*t);
2931 copy.push_back (cp);
2933 if (!(*i)->is_tempo()) {
2934 m = static_cast<MeterSection *> (*i);
2935 MeterSection* cp = new MeterSection (*m);
2936 copy.push_back (cp);
2944 TempoMap::copy_metrics_and_point (const Metrics& metrics, Metrics& copy, MeterSection* section)
2946 MeterSection* ret = 0;
2948 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
2951 if ((*i)->is_tempo()) {
2952 t = static_cast<TempoSection*> (*i);
2953 TempoSection* cp = new TempoSection (*t);
2954 copy.push_back (cp);
2957 if (!(*i)->is_tempo()) {
2958 m = static_cast<MeterSection *> (*i);
2960 ret = new MeterSection (*m);
2961 copy.push_back (ret);
2964 MeterSection* cp = new MeterSection (*m);
2965 copy.push_back (cp);
2972 /** answers the question "is this a valid beat position for this tempo section?".
2973 * it returns true if the tempo section can be moved to the requested bbt position,
2974 * leaving the tempo map in a solved state.
2975 * @param ts the tempo section to be moved
2976 * @param bbt the requested new position for the tempo section
2977 * @return true if the tempo section can be moved to the position, otherwise false.
2980 TempoMap::can_solve_bbt (TempoSection* ts, const BBT_Time& bbt)
2983 TempoSection* tempo_copy = 0;
2986 Glib::Threads::RWLock::ReaderLock lm (lock);
2987 tempo_copy = copy_metrics_and_point (_metrics, copy, ts);
2993 const bool ret = solve_map_pulse (copy, tempo_copy, pulse_at_bbt_locked (copy, bbt));
2995 Metrics::const_iterator d = copy.begin();
2996 while (d != copy.end()) {
3005 * 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,
3006 * taking any possible reordering as a consequence of this into account.
3007 * @param section - the section to be altered
3008 * @param bbt - the BBT time where the altered tempo will fall
3009 * @return returns - the position in pulses and frames (as a pair) where the new tempo section will lie.
3011 pair<double, framepos_t>
3012 TempoMap::predict_tempo_position (TempoSection* section, const BBT_Time& bbt)
3015 pair<double, framepos_t> ret = make_pair (0.0, 0);
3017 Glib::Threads::RWLock::ReaderLock lm (lock);
3019 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, section);
3021 const double beat = beat_at_bbt_locked (future_map, bbt);
3023 if (solve_map_pulse (future_map, tempo_copy, pulse_at_beat_locked (future_map, beat))) {
3024 ret.first = tempo_copy->pulse();
3025 ret.second = tempo_copy->frame();
3027 ret.first = section->pulse();
3028 ret.second = section->frame();
3031 Metrics::const_iterator d = future_map.begin();
3032 while (d != future_map.end()) {
3039 /** moves a TempoSection to a specified position.
3040 * @param ts - the section to be moved
3041 * @param frame - the new position in frames for the tempo
3042 * @param sub_num - the snap division to use if using musical time.
3044 * if sub_num is non-zero, the frame position is used to calculate an exact
3047 * -1 | snap to bars (meter-based)
3048 * 0 | no snap - use audio frame for musical position
3049 * 1 | snap to meter-based (BBT) beat
3050 * >1 | snap to quarter-note subdivision (i.e. 4 will snap to sixteenth notes)
3052 * this follows the snap convention in the gui.
3053 * if sub_num is zero, the musical position will be taken from the supplied frame.
3056 TempoMap::gui_move_tempo (TempoSection* ts, const framepos_t& frame, const int& sub_num)
3060 if (ts->position_lock_style() == MusicTime) {
3062 /* if we're snapping to a musical grid, set the pulse exactly instead of via the supplied frame. */
3063 Glib::Threads::RWLock::WriterLock lm (lock);
3064 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3066 tempo_copy->set_position_lock_style (AudioTime);
3068 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3069 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
3070 const double pulse = pulse_at_beat_locked (future_map, beat);
3072 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3073 solve_map_pulse (_metrics, ts, pulse);
3074 recompute_meters (_metrics);
3082 Glib::Threads::RWLock::WriterLock lm (lock);
3083 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3085 if (solve_map_minute (future_map, tempo_copy, minute_at_frame (frame))) {
3087 /* We're moving the object that defines the grid while snapping to it...
3088 * Placing the ts at the beat corresponding to the requested frame may shift the
3089 * grid in such a way that the mouse is left hovering over a completerly different division,
3090 * causing jittering when the mouse next moves (esp. large tempo deltas).
3091 * To avoid this, place the ts at the requested frame in a dummy map
3092 * then find the closest beat subdivision to that frame in the dummy.
3093 * This alters the snap behaviour slightly in that we snap to beat divisions
3094 * in the future map rather than the existing one.
3096 const double beat = exact_beat_at_frame_locked (future_map, frame, sub_num);
3097 const double pulse = pulse_at_beat_locked (future_map, beat);
3099 if (solve_map_pulse (future_map, tempo_copy, pulse)) {
3100 /* snapping to a grid. force MusicTime temporarily. */
3101 ts->set_position_lock_style (MusicTime);
3102 solve_map_pulse (_metrics, ts, pulse);
3103 ts->set_position_lock_style (AudioTime);
3105 recompute_meters (_metrics);
3108 solve_map_minute (_metrics, ts, minute_at_frame (frame));
3109 recompute_meters (_metrics);
3115 Metrics::const_iterator d = future_map.begin();
3116 while (d != future_map.end()) {
3121 MetricPositionChanged (); // Emit Signal
3124 /** moves a MeterSection to a specified position.
3125 * @param ms - the section to be moved
3126 * @param frame - the new position in frames for the meter
3128 * as a meter cannot snap to anything but bars,
3129 * the supplied frame is rounded to the nearest bar, possibly
3130 * leaving the meter position unchanged.
3133 TempoMap::gui_move_meter (MeterSection* ms, const framepos_t& frame)
3137 if (ms->position_lock_style() == AudioTime) {
3140 Glib::Threads::RWLock::WriterLock lm (lock);
3141 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3143 if (solve_map_minute (future_map, copy, minute_at_frame (frame))) {
3144 solve_map_minute (_metrics, ms, minute_at_frame (frame));
3145 recompute_tempi (_metrics);
3150 Glib::Threads::RWLock::WriterLock lm (lock);
3151 MeterSection* copy = copy_metrics_and_point (_metrics, future_map, ms);
3153 const double beat = beat_at_minute_locked (_metrics, minute_at_frame (frame));
3154 const Timecode::BBT_Time bbt = bbt_at_beat_locked (_metrics, beat);
3156 if (solve_map_bbt (future_map, copy, bbt)) {
3157 solve_map_bbt (_metrics, ms, bbt);
3158 recompute_tempi (_metrics);
3163 Metrics::const_iterator d = future_map.begin();
3164 while (d != future_map.end()) {
3169 MetricPositionChanged (); // Emit Signal
3173 TempoMap::gui_change_tempo (TempoSection* ts, const Tempo& bpm)
3176 bool can_solve = false;
3178 Glib::Threads::RWLock::WriterLock lm (lock);
3179 TempoSection* tempo_copy = copy_metrics_and_point (_metrics, future_map, ts);
3180 tempo_copy->set_beats_per_minute (bpm.beats_per_minute());
3181 recompute_tempi (future_map);
3183 if (check_solved (future_map)) {
3184 ts->set_beats_per_minute (bpm.beats_per_minute());
3185 recompute_map (_metrics);
3190 Metrics::const_iterator d = future_map.begin();
3191 while (d != future_map.end()) {
3196 MetricPositionChanged (); // Emit Signal
3202 TempoMap::gui_dilate_tempo (TempoSection* ts, const framepos_t& frame, const framepos_t& end_frame, const double& pulse)
3205 Ts (future prev_t) Tnext
3208 |----------|----------
3215 Glib::Threads::RWLock::WriterLock lm (lock);
3221 TempoSection* prev_t = copy_metrics_and_point (_metrics, future_map, ts);
3222 TempoSection* prev_to_prev_t = 0;
3223 const frameoffset_t fr_off = end_frame - frame;
3225 if (prev_t && prev_t->pulse() > 0.0) {
3226 prev_to_prev_t = const_cast<TempoSection*>(&tempo_section_at_minute_locked (future_map, minute_at_frame (prev_t->frame() - 1)));
3229 TempoSection* next_t = 0;
3230 for (Metrics::iterator i = future_map.begin(); i != future_map.end(); ++i) {
3231 TempoSection* t = 0;
3232 if ((*i)->is_tempo()) {
3233 t = static_cast<TempoSection*> (*i);
3234 if (t->frame() > ts->frame()) {
3240 /* minimum allowed measurement distance in frames */
3241 const framepos_t min_dframe = 2;
3243 /* the change in frames is the result of changing the slope of at most 2 previous tempo sections.
3244 constant to constant is straightforward, as the tempo prev to prev_t has constant slope.
3246 double contribution = 0.0;
3248 if (next_t && prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3249 contribution = (prev_t->frame() - prev_to_prev_t->frame()) / (double) (next_t->frame() - prev_to_prev_t->frame());
3252 const frameoffset_t prev_t_frame_contribution = fr_off - (contribution * (double) fr_off);
3254 const double start_pulse = prev_t->pulse_at_frame (frame);
3255 const double end_pulse = prev_t->pulse_at_frame (end_frame);
3259 if (prev_t->type() == TempoSection::Constant || prev_t->c_func() == 0.0) {
3261 if (prev_t->position_lock_style() == MusicTime) {
3262 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3263 if (frame > prev_to_prev_t->frame() + min_dframe && (frame + prev_t_frame_contribution) > prev_to_prev_t->frame() + min_dframe) {
3265 new_bpm = prev_t->beats_per_minute() * ((frame - prev_to_prev_t->frame())
3266 / (double) ((frame + prev_t_frame_contribution) - prev_to_prev_t->frame()));
3268 new_bpm = prev_t->beats_per_minute();
3271 /* prev to prev is irrelevant */
3273 if (start_pulse > prev_t->pulse() && end_pulse > prev_t->pulse()) {
3274 new_bpm = prev_t->beats_per_minute() * ((start_pulse - prev_t->pulse()) / (end_pulse - prev_t->pulse()));
3276 new_bpm = prev_t->beats_per_minute();
3281 if (prev_to_prev_t && prev_to_prev_t->type() == TempoSection::Ramp) {
3282 if (frame > prev_to_prev_t->frame() + min_dframe && end_frame > prev_to_prev_t->frame() + min_dframe) {
3284 new_bpm = prev_t->beats_per_minute() * ((frame - prev_to_prev_t->frame())
3285 / (double) ((end_frame) - prev_to_prev_t->frame()));
3287 new_bpm = prev_t->beats_per_minute();
3290 /* prev_to_prev_t is irrelevant */
3292 if (frame > prev_t->frame() + min_dframe && end_frame > prev_t->frame() + min_dframe) {
3293 new_bpm = prev_t->beats_per_minute() * ((frame - prev_t->frame()) / (double) (end_frame - prev_t->frame()));
3295 new_bpm = prev_t->beats_per_minute();
3301 double frame_ratio = 1.0;
3302 double pulse_ratio = 1.0;
3303 const double pulse_pos = prev_t->frame_at_pulse (pulse);
3305 if (prev_to_prev_t) {
3306 if (pulse_pos > prev_to_prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_to_prev_t->frame() + min_dframe) {
3307 frame_ratio = (((pulse_pos - fr_off) - prev_to_prev_t->frame()) / (double) ((pulse_pos) - prev_to_prev_t->frame()));
3309 if (end_pulse > prev_to_prev_t->pulse() && start_pulse > prev_to_prev_t->pulse()) {
3310 pulse_ratio = ((start_pulse - prev_to_prev_t->pulse()) / (end_pulse - prev_to_prev_t->pulse()));
3313 if (pulse_pos > prev_t->frame() + min_dframe && (pulse_pos - fr_off) > prev_t->frame() + min_dframe) {
3314 frame_ratio = (((pulse_pos - fr_off) - prev_t->frame()) / (double) ((pulse_pos) - prev_t->frame()));
3316 pulse_ratio = (start_pulse / end_pulse);
3318 new_bpm = prev_t->beats_per_minute() * (pulse_ratio * frame_ratio);
3321 /* don't clamp and proceed here.
3322 testing has revealed that this can go negative,
3323 which is an entirely different thing to just being too low.
3325 if (new_bpm < 0.5) {
3328 new_bpm = min (new_bpm, (double) 1000.0);
3329 prev_t->set_beats_per_minute (new_bpm);
3330 recompute_tempi (future_map);
3331 recompute_meters (future_map);
3333 if (check_solved (future_map)) {
3334 ts->set_beats_per_minute (new_bpm);
3335 recompute_tempi (_metrics);
3336 recompute_meters (_metrics);
3340 Metrics::const_iterator d = future_map.begin();
3341 while (d != future_map.end()) {
3346 MetricPositionChanged (); // Emit Signal
3349 /** Returns the exact bbt-based beat corresponding to the bar, beat or quarter note subdivision nearest to
3350 * the supplied frame, possibly returning a negative value.
3351 * @param frame The session frame position.
3352 * @param sub_num The subdivision to use when rounding the beat.
3353 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3354 * Positive integers indicate quarter note (non BBT) divisions.
3355 * 0 indicates that the returned beat should not be rounded (equivalent to quarter_note_at_frame()).
3356 * @return The beat position of the supplied frame.
3358 * If the supplied frame lies before the first meter, the return will be negative,
3359 * in which case the returned beat uses the first meter (for BBT subdivisions) and
3360 * the continuation of the tempo curve (backwards).
3362 * This function uses both tempo and meter.
3365 TempoMap::exact_beat_at_frame (const framepos_t& frame, const int32_t sub_num)
3367 Glib::Threads::RWLock::ReaderLock lm (lock);
3369 return exact_beat_at_frame_locked (_metrics, frame, sub_num);
3373 TempoMap::exact_beat_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t divisions)
3375 return beat_at_pulse_locked (_metrics, exact_qn_at_frame_locked (metrics, frame, divisions) / 4.0);
3378 /** Returns the exact quarter note corresponding to the bar, beat or quarter note subdivision nearest to
3379 * the supplied frame, possibly returning a negative value.
3380 * Supplying a frame position with a non-zero sub_num is equivalent to supplying
3381 * a quarter-note musical position without frame rounding (see below)
3383 * @param frame The session frame position.
3384 * @param sub_num The subdivision to use when rounding the quarter note.
3385 * A value of -1 indicates rounding to BBT bar. 1 indicates rounding to BBT beats.
3386 * Positive integers indicate quarter note (non BBT) divisions.
3387 * 0 indicates that the returned quarter note should not be rounded (equivalent to quarter_note_at_frame()).
3388 * @return The quarter note position of the supplied frame.
3390 * If the supplied frame lies before the first meter, the return will be negative,
3391 * in which case the returned quarter note uses the first meter (for BBT subdivisions) and
3392 * the continuation of the tempo curve (backwards).
3394 * This function uses both tempo and meter.
3397 TempoMap::exact_qn_at_frame (const framepos_t& frame, const int32_t sub_num)
3399 Glib::Threads::RWLock::ReaderLock lm (lock);
3401 return exact_qn_at_frame_locked (_metrics, frame, sub_num);
3405 TempoMap::exact_qn_at_frame_locked (const Metrics& metrics, const framepos_t& frame, const int32_t sub_num)
3407 double qn = quarter_note_at_minute_locked (metrics, minute_at_frame (frame));
3410 qn = floor (qn) + (floor (((qn - floor (qn)) * (double) sub_num) + 0.5) / sub_num);
3411 } else if (sub_num == 1) {
3412 /* the gui requested exact musical (BBT) beat */
3413 qn = quarter_note_at_beat_locked (metrics, floor (beat_at_minute_locked (metrics, minute_at_frame (frame)) + 0.5));
3414 } else if (sub_num == -1) {
3416 Timecode::BBT_Time bbt = bbt_at_pulse_locked (metrics, qn / 4.0);
3420 const double prev_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3422 const double next_b = pulse_at_bbt_locked (metrics, bbt) * 4.0;
3424 if ((qn - prev_b) > (next_b - prev_b) / 2.0) {
3434 /** returns the frame duration of the supplied BBT time at a specified frame position in the tempo map.
3435 * @param pos the frame position in the tempo map.
3436 * @param bbt the distance in BBT time from pos to calculate.
3437 * @param dir the rounding direction..
3438 * @return the duration in frames between pos and bbt
3441 TempoMap::bbt_duration_at (framepos_t pos, const BBT_Time& bbt, int dir)
3443 Glib::Threads::RWLock::ReaderLock lm (lock);
3445 BBT_Time pos_bbt = bbt_at_minute_locked (_metrics, minute_at_frame (pos));
3446 const framecnt_t offset = frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3447 const double divisions = meter_section_at_minute_locked (_metrics, minute_at_frame (pos)).divisions_per_bar();
3450 pos_bbt.bars += bbt.bars;
3452 pos_bbt.ticks += bbt.ticks;
3453 if ((double) pos_bbt.ticks > BBT_Time::ticks_per_beat) {
3455 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
3458 pos_bbt.beats += bbt.beats;
3459 if ((double) pos_bbt.beats > divisions) {
3461 pos_bbt.beats -= divisions;
3464 return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt)) - offset;
3466 pos_bbt.bars -= bbt.bars;
3468 if (pos_bbt.ticks < bbt.ticks) {
3469 if (pos_bbt.beats == 1) {
3471 pos_bbt.beats = divisions;
3475 pos_bbt.ticks = BBT_Time::ticks_per_beat - (bbt.ticks - pos_bbt.ticks);
3477 pos_bbt.ticks -= bbt.ticks;
3480 if (pos_bbt.beats <= bbt.beats) {
3482 pos_bbt.beats = divisions - (bbt.beats - pos_bbt.beats);
3484 pos_bbt.beats -= bbt.beats;
3487 return offset - frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
3494 TempoMap::round_to_bar (framepos_t fr, RoundMode dir)
3496 return round_to_type (fr, dir, Bar);
3500 TempoMap::round_to_beat (framepos_t fr, RoundMode dir)
3502 return round_to_type (fr, dir, Beat);
3506 TempoMap::round_to_beat_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3508 Glib::Threads::RWLock::ReaderLock lm (lock);
3509 uint32_t ticks = (uint32_t) floor (max (0.0, beat_at_minute_locked (_metrics, minute_at_frame (fr))) * BBT_Time::ticks_per_beat);
3510 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3511 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3513 ticks -= beats * BBT_Time::ticks_per_beat;
3516 /* round to next (or same iff dir == RoundUpMaybe) */
3518 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3520 if (mod == 0 && dir == RoundUpMaybe) {
3521 /* right on the subdivision, which is fine, so do nothing */
3523 } else if (mod == 0) {
3524 /* right on the subdivision, so the difference is just the subdivision ticks */
3525 ticks += ticks_one_subdivisions_worth;
3528 /* not on subdivision, compute distance to next subdivision */
3530 ticks += ticks_one_subdivisions_worth - mod;
3533 if (ticks >= BBT_Time::ticks_per_beat) {
3534 ticks -= BBT_Time::ticks_per_beat;
3536 } else if (dir < 0) {
3538 /* round to previous (or same iff dir == RoundDownMaybe) */
3540 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3542 if (difference == 0 && dir == RoundDownAlways) {
3543 /* right on the subdivision, but force-rounding down,
3544 so the difference is just the subdivision ticks */
3545 difference = ticks_one_subdivisions_worth;
3548 if (ticks < difference) {
3549 ticks = BBT_Time::ticks_per_beat - ticks;
3551 ticks -= difference;
3555 /* round to nearest */
3558 /* compute the distance to the previous and next subdivision */
3560 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3562 /* closer to the next subdivision, so shift forward */
3564 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3566 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3568 if (ticks > BBT_Time::ticks_per_beat) {
3570 ticks -= BBT_Time::ticks_per_beat;
3571 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3574 } else if (rem > 0) {
3576 /* closer to previous subdivision, so shift backward */
3580 /* can't go backwards past zero, so ... */
3583 /* step back to previous beat */
3585 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3586 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3588 ticks = lrint (ticks - rem);
3589 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3592 /* on the subdivision, do nothing */
3596 const framepos_t ret_frame = frame_at_minute (minute_at_beat_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat)));
3602 TempoMap::round_to_quarter_note_subdivision (framepos_t fr, int sub_num, RoundMode dir)
3604 Glib::Threads::RWLock::ReaderLock lm (lock);
3605 uint32_t ticks = (uint32_t) floor (max (0.0, quarter_note_at_minute_locked (_metrics, minute_at_frame (fr))) * BBT_Time::ticks_per_beat);
3606 uint32_t beats = (uint32_t) floor (ticks / BBT_Time::ticks_per_beat);
3607 uint32_t ticks_one_subdivisions_worth = (uint32_t) BBT_Time::ticks_per_beat / sub_num;
3609 ticks -= beats * BBT_Time::ticks_per_beat;
3612 /* round to next (or same iff dir == RoundUpMaybe) */
3614 uint32_t mod = ticks % ticks_one_subdivisions_worth;
3616 if (mod == 0 && dir == RoundUpMaybe) {
3617 /* right on the subdivision, which is fine, so do nothing */
3619 } else if (mod == 0) {
3620 /* right on the subdivision, so the difference is just the subdivision ticks */
3621 ticks += ticks_one_subdivisions_worth;
3624 /* not on subdivision, compute distance to next subdivision */
3626 ticks += ticks_one_subdivisions_worth - mod;
3629 if (ticks >= BBT_Time::ticks_per_beat) {
3630 ticks -= BBT_Time::ticks_per_beat;
3632 } else if (dir < 0) {
3634 /* round to previous (or same iff dir == RoundDownMaybe) */
3636 uint32_t difference = ticks % ticks_one_subdivisions_worth;
3638 if (difference == 0 && dir == RoundDownAlways) {
3639 /* right on the subdivision, but force-rounding down,
3640 so the difference is just the subdivision ticks */
3641 difference = ticks_one_subdivisions_worth;
3644 if (ticks < difference) {
3645 ticks = BBT_Time::ticks_per_beat - ticks;
3647 ticks -= difference;
3651 /* round to nearest */
3654 /* compute the distance to the previous and next subdivision */
3656 if ((rem = fmod ((double) ticks, (double) ticks_one_subdivisions_worth)) > ticks_one_subdivisions_worth/2.0) {
3658 /* closer to the next subdivision, so shift forward */
3660 ticks = lrint (ticks + (ticks_one_subdivisions_worth - rem));
3662 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved forward to %1\n", ticks));
3664 if (ticks > BBT_Time::ticks_per_beat) {
3666 ticks -= BBT_Time::ticks_per_beat;
3667 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("fold beat to %1\n", beats));
3670 } else if (rem > 0) {
3672 /* closer to previous subdivision, so shift backward */
3676 /* can't go backwards past zero, so ... */
3679 /* step back to previous beat */
3681 ticks = lrint (BBT_Time::ticks_per_beat - rem);
3682 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("step back beat to %1\n", beats));
3684 ticks = lrint (ticks - rem);
3685 DEBUG_TRACE (DEBUG::SnapBBT, string_compose ("moved backward to %1\n", ticks));
3688 /* on the subdivision, do nothing */
3692 const framepos_t ret_frame = frame_at_minute (minute_at_quarter_note_locked (_metrics, beats + (ticks / BBT_Time::ticks_per_beat)));
3698 TempoMap::round_to_type (framepos_t frame, RoundMode dir, BBTPointType type)
3700 Glib::Threads::RWLock::ReaderLock lm (lock);
3702 const double beat_at_framepos = max (0.0, beat_at_minute_locked (_metrics, minute_at_frame (frame)));
3703 BBT_Time bbt (bbt_at_beat_locked (_metrics, beat_at_framepos));
3708 /* find bar previous to 'frame' */
3711 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3713 } else if (dir > 0) {
3714 /* find bar following 'frame' */
3718 return frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3720 /* true rounding: find nearest bar */
3721 framepos_t raw_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3724 framepos_t prev_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3726 framepos_t next_ft = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3728 if ((raw_ft - prev_ft) > (next_ft - prev_ft) / 2) {
3739 return frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos)));
3740 } else if (dir > 0) {
3741 return frame_at_minute (minute_at_beat_locked (_metrics, ceil (beat_at_framepos)));
3743 return frame_at_minute (minute_at_beat_locked (_metrics, floor (beat_at_framepos + 0.5)));
3752 TempoMap::get_grid (vector<TempoMap::BBTPoint>& points,
3753 framepos_t lower, framepos_t upper, uint32_t bar_mod)
3755 Glib::Threads::RWLock::ReaderLock lm (lock);
3756 int32_t cnt = ceil (beat_at_minute_locked (_metrics, minute_at_frame (lower)));
3758 /* although the map handles negative beats, bbt doesn't. */
3763 if (minute_at_beat_locked (_metrics, cnt) >= minute_at_frame (upper)) {
3767 while (pos >= 0 && pos < upper) {
3768 pos = frame_at_minute (minute_at_beat_locked (_metrics, cnt));
3769 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
3770 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
3771 const BBT_Time bbt = bbt_at_beat_locked (_metrics, cnt);
3772 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c_func()));
3776 BBT_Time bbt = bbt_at_minute_locked (_metrics, minute_at_frame (lower));
3781 bbt.bars -= bbt.bars % bar_mod;
3785 while (pos >= 0 && pos < upper) {
3786 pos = frame_at_minute (minute_at_bbt_locked (_metrics, bbt));
3787 const TempoSection tempo = tempo_section_at_minute_locked (_metrics, minute_at_frame (pos));
3788 const MeterSection meter = meter_section_at_minute_locked (_metrics, minute_at_frame (pos));
3789 points.push_back (BBTPoint (meter, tempo_at_minute_locked (_metrics, minute_at_frame (pos)), pos, bbt.bars, bbt.beats, tempo.c_func()));
3790 bbt.bars += bar_mod;
3796 TempoMap::tempo_section_at_frame (framepos_t frame) const
3798 Glib::Threads::RWLock::ReaderLock lm (lock);
3800 return tempo_section_at_minute_locked (_metrics, minute_at_frame (frame));
3804 TempoMap::tempo_section_at_minute_locked (const Metrics& metrics, double minute) const
3806 TempoSection* prev = 0;
3810 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3812 if ((*i)->is_tempo()) {
3813 t = static_cast<TempoSection*> (*i);
3817 if (prev && t->minute() > minute) {
3827 abort(); /*NOTREACHED*/
3834 TempoMap::tempo_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3836 TempoSection* prev_t = 0;
3837 const MeterSection* prev_m = &meter_section_at_beat_locked (metrics, beat);
3841 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3842 if ((*i)->is_tempo()) {
3843 t = static_cast<TempoSection*> (*i);
3844 if (prev_t && ((t->pulse() - prev_m->pulse()) * prev_m->note_divisor()) + prev_m->beat() > beat) {
3854 /* don't use this to calculate length (the tempo is only correct for this frame).
3855 do that stuff based on the beat_at_frame and frame_at_beat api
3858 TempoMap::frames_per_beat_at (const framepos_t& frame, const framecnt_t& sr) const
3860 Glib::Threads::RWLock::ReaderLock lm (lock);
3862 const TempoSection* ts_at = 0;
3863 const TempoSection* ts_after = 0;
3864 Metrics::const_iterator i;
3867 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
3869 if ((*i)->is_tempo()) {
3870 t = static_cast<TempoSection*> (*i);
3874 if (ts_at && (*i)->frame() > frame) {
3883 return (60.0 * _frame_rate) / ts_at->tempo_at_minute (minute_at_frame (frame));
3885 /* must be treated as constant tempo */
3886 return ts_at->frames_per_beat (_frame_rate);
3890 TempoMap::meter_section_at_frame (framepos_t frame) const
3892 Glib::Threads::RWLock::ReaderLock lm (lock);
3893 return meter_section_at_minute_locked (_metrics, minute_at_frame (frame));
3897 TempoMap::meter_section_at_minute_locked (const Metrics& metrics, double minute) const
3899 Metrics::const_iterator i;
3900 MeterSection* prev = 0;
3904 for (i = metrics.begin(); i != metrics.end(); ++i) {
3906 if (!(*i)->is_tempo()) {
3907 m = static_cast<MeterSection*> (*i);
3909 if (prev && (*i)->minute() > minute) {
3919 abort(); /*NOTREACHED*/
3926 TempoMap::meter_section_at_beat_locked (const Metrics& metrics, const double& beat) const
3928 MeterSection* prev_m = 0;
3930 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
3932 if (!(*i)->is_tempo()) {
3933 m = static_cast<MeterSection*> (*i);
3934 if (prev_m && m->beat() > beat) {
3945 TempoMap::meter_section_at_beat (double beat) const
3947 Glib::Threads::RWLock::ReaderLock lm (lock);
3948 return meter_section_at_beat_locked (_metrics, beat);
3952 TempoMap::meter_at_frame (framepos_t frame) const
3954 TempoMetric m (metric_at (frame));
3959 TempoMap::fix_legacy_session ()
3961 MeterSection* prev_m = 0;
3962 TempoSection* prev_t = 0;
3964 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
3968 if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
3969 if (!m->movable()) {
3970 pair<double, BBT_Time> bbt = make_pair (0.0, BBT_Time (1, 1, 0));
3973 m->set_minute (0.0);
3974 m->set_position_lock_style (AudioTime);
3979 pair<double, BBT_Time> start = make_pair (((m->bbt().bars - 1) * prev_m->note_divisor())
3980 + (m->bbt().beats - 1)
3981 + (m->bbt().ticks / BBT_Time::ticks_per_beat)
3983 m->set_beat (start);
3984 const double start_beat = ((m->bbt().bars - 1) * prev_m->note_divisor())
3985 + (m->bbt().beats - 1)
3986 + (m->bbt().ticks / BBT_Time::ticks_per_beat);
3987 m->set_pulse (start_beat / prev_m->note_divisor());
3990 } else if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
3996 if (!t->movable()) {
3998 t->set_minute (0.0);
3999 t->set_position_lock_style (AudioTime);
4005 const double beat = ((t->legacy_bbt().bars - 1) * ((prev_m) ? prev_m->note_divisor() : 4.0))
4006 + (t->legacy_bbt().beats - 1)
4007 + (t->legacy_bbt().ticks / BBT_Time::ticks_per_beat);
4009 t->set_pulse (beat / prev_m->note_divisor());
4011 /* really shouldn't happen but.. */
4012 t->set_pulse (beat / 4.0);
4021 TempoMap::get_state ()
4023 Metrics::const_iterator i;
4024 XMLNode *root = new XMLNode ("TempoMap");
4027 Glib::Threads::RWLock::ReaderLock lm (lock);
4028 for (i = _metrics.begin(); i != _metrics.end(); ++i) {
4029 root->add_child_nocopy ((*i)->get_state());
4037 TempoMap::set_state (const XMLNode& node, int /*version*/)
4040 Glib::Threads::RWLock::WriterLock lm (lock);
4043 XMLNodeConstIterator niter;
4044 Metrics old_metrics (_metrics);
4047 nlist = node.children();
4049 for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
4050 XMLNode* child = *niter;
4052 if (child->name() == TempoSection::xml_state_node_name) {
4055 TempoSection* ts = new TempoSection (*child, _frame_rate);
4056 _metrics.push_back (ts);
4059 catch (failed_constructor& err){
4060 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4061 _metrics = old_metrics;
4062 old_metrics.clear();
4066 } else if (child->name() == MeterSection::xml_state_node_name) {
4069 MeterSection* ms = new MeterSection (*child, _frame_rate);
4070 _metrics.push_back (ms);
4073 catch (failed_constructor& err) {
4074 error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
4075 _metrics = old_metrics;
4076 old_metrics.clear();
4082 if (niter == nlist.end()) {
4083 MetricSectionSorter cmp;
4084 _metrics.sort (cmp);
4087 /* check for legacy sessions where bbt was the base musical unit for tempo */
4088 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4090 if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
4091 if (t->legacy_bbt().bars != 0) {
4092 fix_legacy_session();
4099 /* check for multiple tempo/meters at the same location, which
4100 ardour2 somehow allowed.
4103 Metrics::iterator prev = _metrics.end();
4104 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4105 if (prev != _metrics.end()) {
4107 MeterSection* prev_m;
4109 TempoSection* prev_t;
4110 if ((prev_m = dynamic_cast<MeterSection*>(*prev)) != 0 && (ms = dynamic_cast<MeterSection*>(*i)) != 0) {
4111 if (prev_m->pulse() == ms->pulse()) {
4112 cerr << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4113 error << string_compose (_("Multiple meter definitions found at %1"), prev_m->pulse()) << endmsg;
4116 } else if ((prev_t = dynamic_cast<TempoSection*>(*prev)) != 0 && (ts = dynamic_cast<TempoSection*>(*i)) != 0) {
4117 if (prev_t->pulse() == ts->pulse()) {
4118 cerr << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4119 error << string_compose (_("Multiple tempo definitions found at %1"), prev_t->pulse()) << endmsg;
4127 recompute_map (_metrics);
4129 Metrics::const_iterator d = old_metrics.begin();
4130 while (d != old_metrics.end()) {
4134 old_metrics.clear ();
4137 PropertyChanged (PropertyChange ());
4143 TempoMap::dump (const Metrics& metrics, std::ostream& o) const
4145 Glib::Threads::RWLock::ReaderLock lm (lock, Glib::Threads::TRY_LOCK);
4146 const MeterSection* m;
4147 const TempoSection* t;
4148 const TempoSection* prev_t = 0;
4150 for (Metrics::const_iterator i = metrics.begin(); i != metrics.end(); ++i) {
4152 if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
4153 o << "Tempo @ " << *i << t->beats_per_minute() << " BPM (pulse = 1/" << t->note_type()
4154 << " type= " << enum_2_string (t->type()) << ") " << " at pulse= " << t->pulse()
4155 << " minute= " << t->minute() << " frame= " << t->frame() << " (movable? " << t->movable() << ')'
4156 << " pos lock: " << enum_2_string (t->position_lock_style()) << std::endl;
4158 o << std::setprecision (17) << " current : " << t->beats_per_minute()
4159 << " | " << t->pulse() << " | " << t->frame() << " | " << t->minute() << std::endl;
4160 o << " previous : " << prev_t->beats_per_minute()
4161 << " | " << prev_t->pulse() << " | " << prev_t->frame() << " | " << prev_t->minute() << std::endl;
4162 o << " calculated : " << prev_t->tempo_at_pulse (t->pulse())
4163 << " | " << prev_t->pulse_at_tempo (t->beats_per_minute(), t->minute())
4164 << " | " << frame_at_minute (prev_t->minute_at_tempo (t->beats_per_minute(), t->pulse()))
4165 << " | " << prev_t->minute_at_tempo (t->beats_per_minute(), t->pulse()) << std::endl;
4168 } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
4169 o << "Meter @ " << *i << ' ' << m->divisions_per_bar() << '/' << m->note_divisor() << " at " << m->bbt()
4170 << " frame= " << m->frame() << " pulse: " << m->pulse() << " beat : " << m->beat()
4171 << " pos lock: " << enum_2_string (m->position_lock_style()) << " (movable? " << m->movable() << ')' << endl;
4174 o << "------" << std::endl;
4178 TempoMap::n_tempos() const
4180 Glib::Threads::RWLock::ReaderLock lm (lock);
4183 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4184 if ((*i)->is_tempo()) {
4193 TempoMap::n_meters() const
4195 Glib::Threads::RWLock::ReaderLock lm (lock);
4198 for (Metrics::const_iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4199 if (!(*i)->is_tempo()) {
4208 TempoMap::insert_time (framepos_t where, framecnt_t amount)
4210 for (Metrics::reverse_iterator i = _metrics.rbegin(); i != _metrics.rend(); ++i) {
4211 if ((*i)->frame() >= where && (*i)->movable ()) {
4215 if ((ms = dynamic_cast <MeterSection*>(*i)) != 0) {
4216 gui_move_meter (ms, (*i)->frame() + amount);
4219 if ((ts = dynamic_cast <TempoSection*>(*i)) != 0) {
4220 gui_move_tempo (ts, (*i)->frame() + amount, 0);
4225 PropertyChanged (PropertyChange ());
4229 TempoMap::remove_time (framepos_t where, framecnt_t amount)
4233 std::list<MetricSection*> metric_kill_list;
4235 TempoSection* last_tempo = NULL;
4236 MeterSection* last_meter = NULL;
4237 bool tempo_after = false; // is there a tempo marker at the first sample after the removed range?
4238 bool meter_after = false; // is there a meter marker likewise?
4240 Glib::Threads::RWLock::WriterLock lm (lock);
4241 for (Metrics::iterator i = _metrics.begin(); i != _metrics.end(); ++i) {
4242 if ((*i)->frame() >= where && (*i)->frame() < where+amount) {
4243 metric_kill_list.push_back(*i);
4244 TempoSection *lt = dynamic_cast<TempoSection*> (*i);
4247 MeterSection *lm = dynamic_cast<MeterSection*> (*i);
4251 else if ((*i)->frame() >= where) {
4252 // TODO: make sure that moved tempo/meter markers are rounded to beat/bar boundaries
4253 (*i)->set_minute ((*i)->minute() - minute_at_frame (amount));
4254 if ((*i)->frame() == where) {
4255 // marker was immediately after end of range
4256 tempo_after = dynamic_cast<TempoSection*> (*i);
4257 meter_after = dynamic_cast<MeterSection*> (*i);
4263 //find the last TEMPO and METER metric (if any) and move it to the cut point so future stuff is correct
4264 if (last_tempo && !tempo_after) {
4265 metric_kill_list.remove(last_tempo);
4266 last_tempo->set_minute (minute_at_frame (where));
4269 if (last_meter && !meter_after) {
4270 metric_kill_list.remove(last_meter);
4271 last_meter->set_minute (minute_at_frame (where));
4275 //remove all the remaining metrics
4276 for (std::list<MetricSection*>::iterator i = metric_kill_list.begin(); i != metric_kill_list.end(); ++i) {
4277 _metrics.remove(*i);
4282 recompute_map (_metrics);
4285 PropertyChanged (PropertyChange ());
4289 /** Add some (fractional) Beats to a session frame position, and return the result in frames.
4290 * pos can be -ve, if required.
4293 TempoMap::framepos_plus_qn (framepos_t frame, Evoral::Beats quarter_note) const
4295 Glib::Threads::RWLock::ReaderLock lm (lock);
4297 return frame_at_minute (minute_at_quarter_note_locked (_metrics, quarter_note_at_minute_locked (_metrics, minute_at_frame (frame)) + quarter_note.to_double()));
4301 TempoMap::framepos_plus_bbt (framepos_t pos, BBT_Time op) const
4303 Glib::Threads::RWLock::ReaderLock lm (lock);
4305 BBT_Time pos_bbt = bbt_at_beat_locked (_metrics, beat_at_minute_locked (_metrics, minute_at_frame (pos)));
4306 pos_bbt.ticks += op.ticks;
4307 if (pos_bbt.ticks >= BBT_Time::ticks_per_beat) {
4309 pos_bbt.ticks -= BBT_Time::ticks_per_beat;
4311 pos_bbt.beats += op.beats;
4312 /* the meter in effect will start on the bar */
4313 double divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4314 while (pos_bbt.beats >= divisions_per_bar + 1) {
4316 divisions_per_bar = meter_section_at_beat (beat_at_bbt_locked (_metrics, BBT_Time (pos_bbt.bars + op.bars, 1, 0))).divisions_per_bar();
4317 pos_bbt.beats -= divisions_per_bar;
4319 pos_bbt.bars += op.bars;
4321 return frame_at_minute (minute_at_bbt_locked (_metrics, pos_bbt));
4324 /** Count the number of beats that are equivalent to distance when going forward,
4328 TempoMap::framewalk_to_qn (framepos_t pos, framecnt_t distance) const
4330 Glib::Threads::RWLock::ReaderLock lm (lock);
4332 return Evoral::Beats (quarter_note_at_minute_locked (_metrics, minute_at_frame (pos + distance)) - quarter_note_at_minute_locked (_metrics, minute_at_frame (pos)));
4336 bool operator() (const BBT_Time& a, const BBT_Time& b) {
4342 operator<< (std::ostream& o, const Meter& m) {
4343 return o << m.divisions_per_bar() << '/' << m.note_divisor();
4347 operator<< (std::ostream& o, const Tempo& t) {
4348 return o << t.beats_per_minute() << " 1/" << t.note_type() << "'s per minute";
4352 operator<< (std::ostream& o, const MetricSection& section) {
4354 o << "MetricSection @ " << section.frame() << ' ';
4356 const TempoSection* ts;
4357 const MeterSection* ms;
4359 if ((ts = dynamic_cast<const TempoSection*> (§ion)) != 0) {
4360 o << *((const Tempo*) ts);
4361 } else if ((ms = dynamic_cast<const MeterSection*> (§ion)) != 0) {
4362 o << *((const Meter*) ms);